diff --git a/ER-Model/.gitignore b/ER-Model/.gitignore new file mode 100644 index 000000000..555f2e051 --- /dev/null +++ b/ER-Model/.gitignore @@ -0,0 +1,4 @@ +/output/ +/.idea/ +/agent/ +/.DS_Store/.DS_Store diff --git a/ER-Model/README.md b/ER-Model/README.md new file mode 100644 index 000000000..86c531e1f --- /dev/null +++ b/ER-Model/README.md @@ -0,0 +1,194 @@ +# ER-Model 插件 + +ER-Model 提供一个基于 Web 的 ER 模型设计器,支持 DBML 双向转换、实例元数据导入、可视化编辑与 SQL 执行。 + +## 功能特性 + +- **DBML 编辑 / 可视化联动**:左侧编辑器实时解析 DBML,右侧图形视图同步渲染表与关系。 +- **实例连接与 Schema 管理**:支持平台内已托管实例的选择、Schema 查询与创建、连接测试。 +- **导入导出**:可从 SQL 文件或数据库中加载结构;支持导出 SQL、PlantUML、PNG / PDF 等格式。 +- **自动布局与可视化优化**:内置多种布局方式、列 / 关系高亮、提示信息等交互体验。 +- **SQL 执行**:可将当前 DBML 转换的 DDL 直接执行到选定实例 / Schema。 + +--- + +## 目录结构 + +``` +plugins/er-model +├── src/main/java/org/opengauss/admin/plugin # 插件后端:控制器、服务、SQL 生成、翻译等 +│ ├── controller # REST 接口:执行、导入导出、SPA 前置等 +│ ├── dto # 数据传输对象(表/列/索引/实例请求等) +│ ├── service # 实例管理、SQL 执行、导出等业务服务 +│ ├── sql # 方言适配的 SQL 生成器 +│ └── translate # DBML → DTO / SQL 转换逻辑 +├── src/main/resources # 插件资源文件 +│ └── resources # 构建产物:前端静态资源、国际化等 +└── web-ui # 前端工程(Quasar + Vue3) + ├── src/components # 组件:图形视图、表格节点、工具提示等 + ├── src/pages # 页面:编辑器、只读展示、错误页等 + ├── src/store # Pinia 数据仓库:编辑器状态、图形状态等 + ├── src/utils # 工具:导出、布局、下载、认证等 + └── src/css # 样式:ER 图主题、全局样式 +``` + +--- + +# er-model 安装与运行指南 + +## 一、环境配置 + +### 1. 安装 openGauss 数据库 + +请先安装并启动 openGauss 数据库。 +> **参考文档:** [openGauss 参数配置说明](https://gitcode.com/opengauss/openGauss-workbench#补充opengauss参数配置) +> (特别注意数据库远程用户访问权限的设置) + +### 2. 安装依赖环境 + +确保已安装以下组件: + +- Java 17+ +- Maven 3.9.0+ +- Node.js v18+(含 npm) + +并配置好: + +- Maven 镜像源 +- Node 镜像源 + +### 3. 安装 SpringBrick 组件 + +在本地编译并安装: +[springboot-plugin-framework-parent](https://gitcode.com/wang4721/springboot-plugin-framework-parent.git) + +--- + +## 二、开发环境运行 + +### 构建步骤 + +1. 修改 `openGauss-datakit/visualtool-api` 目录下的 `application-dev.yml`,配置数据库连接、用户名与密码(需为远程用户)。 +2. 在项目根目录创建文件夹: + + ```bash + mkdir visualtool-plugin + ``` + + 用于存放构建后的插件 jar 包。 + +3. 执行构建脚本: + + ```bash + sh idea-debug-plugins-api-build.sh + ``` + +4. 将 `base-ops` 与 `er-model` 模块中 `target` 目录下的 jar 包拷贝至 `visualtool-plugin` 文件夹。 +5. 启动主应用类: + + ``` + openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/AdminApplication.java + ``` + +### 启动与访问 + +- 开发环境一般不启用 SSL,可直接使用 HTTP 访问: + + ``` + http://ip:9494/ + ``` + +- 默认账号密码: + + ``` + 用户名:admin + 密码:admin123 + ``` + + 首次登录需修改密码。 + +--- + +## 三、服务器环境运行 + +1. **构建安装包** + ```bash + sh build.sh + ``` + 成功后会生成: + ``` + openGauss-Datakit-All-7.0.0-RC3.tar.gz + ``` + 将该文件上传至服务器。 + +2. **解压安装包** + ```bash + tar -xzf openGauss-Datakit-All-7.0.0-RC3.tar.gz + cd openGauss-datakit + ``` + +3. **创建目录** + ```bash + mkdir config files ssl logs + ``` + +4. **修改配置文件 - 工作目录** + 编辑 `application-temp.yml`: + ```yaml + system.defaultStoragePath: /ops/files + server.ssl.key-store: /ops/ssl/keystore.p12 + logging.file.path: /ops/logs + ``` + 将 `/ops` 替换为实际安装目录(如 `/path/datakit_server`), + 然后将文件移动至 `config` 目录: + ```bash + mv application-temp.yml config/ + ``` + +5. **修改配置文件 - 数据库连接** + 如使用 openGauss 数据库: + ```yaml + driver-class-name: org.opengauss.Driver + url: jdbc:opengauss://ip:port/database?currentSchema=public&batchMode=off + username: dbuser + password: ****** + ``` + +6. **生成 SSL 密钥** + ```bash + keytool -genkey -noprompt -dname "CN=opengauss, OU=opengauss, O=opengauss, L=Beijing, S=Beijing, C=CN" -alias opengauss -storetype PKCS12 -keyalg RSA -keysize 4096 -keystore /path/datakit_server/ssl/keystore.p12 -validity 365 -storepass ****** -ext "SAN=IP:x.x.x.x" + ``` + - `-storepass` 值需与配置文件中 `server.ssl.key-store-password` 保持一致 + - `x.x.x.x` 替换为服务器实际 IP + +7. **启动与运维** + ```bash + sh ./run.sh start --aes-key xxxxxx # 启动 + sh ./run.sh stop # 停止 + sh ./run.sh restart --aes-key xxxxxx # 重启 + sh ./run.sh status # 查看状态 + ``` + +8. **访问服务** + 启动成功后,通过浏览器访问: + ``` + https://ip:9494/ + ``` + 默认账号密码: + ``` + 用户名:admin + 密码:admin123 + ``` + +--- + +## 四、补充说明 + +- 若使用 openGauss 作为后台数据库,请确保已正确配置远程连接参数。 +- 详细参数说明可参考:[openGauss 参数配置文档](https://gitcode.com/opengauss/openGauss-workbench#补充opengauss参数配置)。 + +--- + +## License + +本插件遵循 [Mulan PSL v2](http://license.coscl.org.cn/MulanPSL2) 协议。 diff --git a/ER-Model/build.sh b/ER-Model/build.sh new file mode 100644 index 000000000..387b8f855 --- /dev/null +++ b/ER-Model/build.sh @@ -0,0 +1,236 @@ +#!/bin/bash +############################################################################# +# Copyright (c) 2023 Huawei Technologies Co.,Ltd. +# +# openGauss is licensed under Mulan PSL v2. +# You can use this software according to the terms +# and conditions of the Mulan PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# +# http://license.coscl.org.cn/MulanPSL2 +# +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +# See the Mulan PSL v2 for more details. +# ---------------------------------------------------------------------------- +# Description : shell script for build datakit and plugins. +############################################################################# +declare mvn_target="clean package" +declare mvn_input_args='' +declare mvn_prod='prod' +############################################################################# +function print_help() +{ + echo "Usage: $0 [OPTION] + -h|--help show help information + -t|--target set the mvn build target, default is clean package + -a|--args set the mvn build args additions, default is empty + -p|--profile the profile, default is prod +example: + sh build.sh // build in default + sh build.sh -a \"-Dbuild.frontend.skip=true -Dweb.build.skip=true\" // build skip frontend + sh build.sh -t \"clean\" -a \"-Dbuild.frontend.skip=true -Dweb.build.skip=true\" // build only clean and skip frontend + " +} + +while [ $# -gt 0 ]; do + case "$1" in + -h|--help) + print_help + exit 1 + ;; + -t|--target) + if [ "$2"X = X ]; then + echo "no given correct mvn target, such as: clean package" + exit 1 + fi + mvn_target=$2 + shift 2 + ;; + -a|--args) + if [ "$2"X = X ]; then + echo "no given correct mvn args, such as: -Dbuild.frontend.skip=true -Dweb.build.skip=true" + exit 1 + fi + mvn_input_args=$2 + shift 2 + ;; + -p|--profile) + if [ "$2"X = X ]; then + echo "no given mvn profile, such as release or dev" + exit 1 + fi + mvn_prod=$2 + shift 2 + ;; + *) + echo "Internal Error: option processing error: $1" 1>&2 + echo "please input right paramtenter, the following command may help you" + echo "./build.sh --help or ./build.sh -h" + exit 1 + esac +done +root_path=`pwd` +output_path=$root_path/output +mvn_init_args="-Dmaven.test.skip=true" +mvn_args="$mvn_init_args -U -P $mvn_prod $mvn_input_args" +build_main_pkg=openGauss-datakit +plugin_output=visualtool-plugin +plugin_doc_output=doc + +echo "we got build cmd: mvn $mvn_target $mvn_args" + +export JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF8" +pom_version=`awk '/[^<]+<\/admin.version>/{gsub(/|<\/admin.version>/,"",$1);print $1;exit;}' ${root_path}/pom.xml` +mkdir -p output +rm -rf output/* +mkdir -p output/$plugin_output +mkdir -p output/$plugin_doc_output + +function prepare_java_env() +{ + echo "We no longer provide java, please makesure java(11+) already in PATH!" + JAVA_VERSION=`java -version 2>&1 | awk -F '"' '/version/ {print $2}'` + if [ -z "$JAVA_VERSION" ]; then + echo "Failed to obtain java version!" + exit 1 + fi + + echo java version is $JAVA_VERSION + MAJOR_VERSION=`echo $JAVA_VERSION | awk -F '[.]' '{print $1}'` + if [ $MAJOR_VERSION -lt 11 ]; then + echo "java version is not meeting requirements!" + exit 1 + fi +} + +function prepare_maven_env() +{ + echo "We no longer provide mvn, please makesure mvn(3.6.0+) already in PATH!" + MAVEN_VERSION=`mvn -v 2>&1 | awk '/Apache Maven / {print $3}'` + if [ -z "$MAVEN_VERSION" ]; then + echo "Failed to obtain maven version!" + exit 1 + fi + + echo maven version is $MAVEN_VERSION + read MAJOR_VERSION MINOR_VERSION <<< $(echo $MAVEN_VERSION | awk -F '[.]' '{print $1, $2}') + if [ $MAJOR_VERSION -lt 3 ] || ([ $MAJOR_VERSION -eq 3 ] && [ $MINOR_VERSION -lt 6 ]); then + echo "maven version is not meeting requirements!" + exit 1 + fi +} + +function prepare_npm_env() +{ + echo "We no longer provide node/npm, please makesure npm(8.11.0+) already in PATH!" + NPM_VERSION=`npm -v 2>&1 | awk -F '"' '// {print $1}'` + if [ -z "$NPM_VERSION" ]; then + echo "Failed to obtain npm version!" + exit 1 + fi + + echo npm version is $NPM_VERSION + read MAJOR_VERSION MINOR_VERSION <<< $(echo $NPM_VERSION | awk -F '[.]' '{print $1, $2}') + if [ $MAJOR_VERSION -lt 8 ] || ([ $MAJOR_VERSION -eq 8 ] && [ $MINOR_VERSION -lt 11 ]); then + echo "npm version is not meeting requirements!" + exit 1 + fi +} + +function prepare_env() +{ + prepare_java_env + prepare_maven_env + prepare_npm_env +} + +function get_git_log(){ + cd $output_path + package_time=`date '+%Y-%m-%d %H:%M:%S'` + echo "--------------------------------get_git_log---------------------------------" + echo "build time: "$package_time + echo "build time: "$package_time >> build_commit_id.log + echo "git branch: "$(git rev-parse --abbrev-ref HEAD) + echo "git branch: "$(git rev-parse --abbrev-ref HEAD) >> build_commit_id.log + echo "last commit:" + echo "last commit:" >> build_commit_id.log + echo "$(git log -1)" + echo "$(git log -1)" >> build_commit_id.log + echo "--------------------------------get_git_log finished---------------------------------" +} + +function fetch_and_build_install_spring_brick() { + cd $root_path + git clone https://gitcode.com/wang4721/springboot-plugin-framework-parent.git + cd springboot-plugin-framework-parent + echo "build dir:${root_path} ,to run cmd: mvn clean install -Dmaven.test.skip=true" + mvn clean install -Dmaven.test.skip=true + if [ $? -ne 0 ]; then + echo "Build spring brick failed..." + exit 1 + fi + cd .. + rm -rf springboot-plugin-framework-parent +} + +function build_pkg() { + cd $root_path + echo "build dir:${root_path} ,to run cmd: mvn ${mvn_target} ${mvn_args}" + mvn --threads=4C ${mvn_target} ${mvn_args} + if [ $? -ne 0 ]; then + echo "Build datakit failed..." + exit 1 + fi + cp ./${build_main_pkg}/visualtool-api/target/openGauss-datakit-*.jar ${output_path}/ + cp ./run.sh ${output_path}/ + cp ./${build_main_pkg}/README.md ${output_path}/${plugin_doc_output}/datakit-README.md + cp ./${build_main_pkg}/config/application-temp.yml ${output_path}/ +} + +function copy_plugin_pkg() { + local plugin_path=$root_path/plugins + cd $root_path + sub_plugin_paths=`ls -D ./plugins` + for plugin_name in ${sub_plugin_paths[*]} + do + { + local build_path=$plugin_path/${plugin_name} + cd ${build_path} + if [ "$plugin_name" == "openGauss-tools-monitor" ] ; then + cp ./**/target/*repackage.jar ${output_path}/$plugin_output/ 2>/dev/null + echo "copy ${plugin_name} success!!!" + elif [ "$plugin_name" == "container-management-plugin" ]; then + continue + else + cp ./**target/*repackage.jar ${output_path}/$plugin_output/ 2>/dev/null + echo "copy ${plugin_name} success!!!" + fi + if [ -f ./readme.md ] ; then + cp ./readme.md ${output_path}/${plugin_doc_output}/${plugin_name}-README.md + fi + if [ -f ./README.md ] ; then + cp ./README.md ${output_path}/${plugin_doc_output}/${plugin_name}-README.md + fi + }& + done + wait +} + +prepare_env +#fetch_and_build_install_spring_brick +#get_git_log +#build_pkg +copy_plugin_pkg +cd $output_path +tar -zcf openGauss-Datakit-All-${pom_version}.tar.gz ./* +tar -zcf openGauss-Datakit-Mini-${pom_version}.tar.gz \ + ./application-temp.yml \ + ./build_commit_id.log \ + ./doc \ + ./openGauss-datakit* \ + ./run.sh \ + ./visualtool-plugin/webds-plugin* \ + ./visualtool-plugin/base-ops* \ + ./agent/* diff --git a/ER-Model/er-model-test-report.pdf b/ER-Model/er-model-test-report.pdf new file mode 100644 index 000000000..d44023445 Binary files /dev/null and b/ER-Model/er-model-test-report.pdf differ diff --git a/ER-Model/idea-debug-plugins-api-build.sh b/ER-Model/idea-debug-plugins-api-build.sh new file mode 100644 index 000000000..f1ab9f555 --- /dev/null +++ b/ER-Model/idea-debug-plugins-api-build.sh @@ -0,0 +1,45 @@ +#!/bin/bash +############################################################################# +# Copyright (c) 2023 Huawei Technologies Co.,Ltd. +# +# openGauss is licensed under Mulan PSL v2. +# You can use this software according to the terms +# and conditions of the Mulan PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# +# http://license.coscl.org.cn/MulanPSL2 +# +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +# See the Mulan PSL v2 for more details. +# ---------------------------------------------------------------------------- +# Description : shell script for build datakit and plugins. +############################################################################# +mvn --threads=4C clean +# rm -rf /Users/wangziqi/.m2/repository/* +#mvn --threads=4C clean install -Dbuild.frontend.skip=true -Dweb.build.skip=true -Dmaven.test.skip=true +#mvn --threads=1C -T 1 clean install -Dmaven.test.skip=true +mvn clean install -Dmaven.test.skip=true + +version_num=7.0.0-RC3 + +rm -rf visualtool-plugin/* +rm -rf openGauss-datakit-${version_num}.jar + +cp plugins/base-ops/target/base-ops-${version_num}-repackage.jar visualtool-plugin/ +cp plugins/er-model/target/er-model-${version_num}-repackage.jar visualtool-plugin/ + +#function fetch_build_install_spring_brick() { +# cd $root_path +# git clone https://gitcode.com/wang4721/springboot-plugin-framework-parent.git +# cd springboot-plugin-framework-parent +# echo "build dir:${root_path} ,to run cmd: mvn clean install -Dmaven.test.skip=true" +# mvn clean install -Dmaven.test.skip=true +# if [ $? -ne 0 ]; then +# echo "Build spring brick failed..." +# exit 1 +# fi +# cd .. +# rm -rf springboot-plugin-framework-parent +#} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit-agent/.dockerignore b/ER-Model/openGauss-datakit-agent/.dockerignore new file mode 100644 index 000000000..ab14769c3 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/.dockerignore @@ -0,0 +1,5 @@ +* +!target/*-runner +!target/*-runner.jar +!target/lib/* +!target/quarkus-app/ diff --git a/ER-Model/openGauss-datakit-agent/.gitignore b/ER-Model/openGauss-datakit-agent/.gitignore new file mode 100644 index 000000000..087a18358 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/.gitignore @@ -0,0 +1,35 @@ +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties \ No newline at end of file diff --git a/ER-Model/openGauss-datakit-agent/README.md b/ER-Model/openGauss-datakit-agent/README.md new file mode 100644 index 000000000..ae0dfdea1 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/README.md @@ -0,0 +1,79 @@ +# Getting started with Quarkus + +This is a minimal CRUD service exposing a couple of endpoints over REST. + +Under the hood, this demo uses: + +- RESTEasy to expose the REST endpoints +- REST-assured and JUnit 5 for endpoint testing + +## Requirements + +To compile and run this demo you will need: + +- JDK 17+ +- GraalVM + +### Configuring GraalVM and JDK 17+ + +Make sure that both the `GRAALVM_HOME` and `JAVA_HOME` environment variables have +been set, and that a JDK 17+ `java` command is on the path. + +See the [Building a Native Executable guide](https://quarkus.io/guides/building-native-image-guide) +for help setting up your environment. + +## Building the application + +Launch the Maven build on the checked out sources of this demo: + +> ./mvnw package + +### Live coding with Quarkus + +The Maven Quarkus plugin provides a development mode that supports +live coding. To try this out: + +> ./mvnw quarkus:dev + +This command will leave Quarkus running in the foreground listening on port 8080. + +1. Visit the default endpoint: [http://127.0.0.1:8080](http://127.0.0.1:8080). + - Make a simple change to [src/main/resources/META-INF/resources/index.html](src/main/resources/META-INF/resources/index.html) file. + - Refresh the browser to see the updated page. +2. Visit the `/hello` endpoint: [http://127.0.0.1:8080/hello](http://127.0.0.1:8080/hello) + - Update the response in [src/main/java/org/acme/quickstart/GreetingResource.java](src/main/java/org/acme/quickstart/GreetingResource.java). Replace `hello` with `hello there` in the `hello()` method. + - Refresh the browser. You should now see `hello there`. + - Undo the change, so the method returns `hello` again. + - Refresh the browser. You should now see `hello`. + +### Run Quarkus in JVM mode + +When you're done iterating in developer mode, you can run the application as a +conventional jar file. + +First compile it: + +> ./mvnw package + +Then run it: + +> java -jar ./target/quarkus-app/quarkus-run.jar + +Have a look at how fast it boots, or measure the total native memory consumption. + +### Run Quarkus as a native executable + +You can also create a native executable from this application without making any +source code changes. A native executable removes the dependency on the JVM: +everything needed to run the application on the target platform is included in +the executable, allowing the application to run with minimal resource overhead. + +Compiling a native executable takes a bit longer, as GraalVM performs additional +steps to remove unnecessary codepaths. Use the `native` profile to compile a +native executable: + +> ./mvnw package -Dnative + +After getting a cup of coffee, you'll be able to run this executable directly: + +> ./target/getting-started-1.0.0-SNAPSHOT-runner diff --git a/ER-Model/openGauss-datakit-agent/pom.xml b/ER-Model/openGauss-datakit-agent/pom.xml new file mode 100644 index 000000000..d795a8814 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/pom.xml @@ -0,0 +1,337 @@ + + + + 4.0.0 + + org.opengauss + openGauss-datakit-agent + 7.0.0-RC3 + + + UTF-8 + UTF-8 + 17 + uber-jar + quarkus-bom + io.quarkus.platform + 3.26.4 + 3.0.0 + 6.4.6 + 3.18.0 + 1.5.0 + 1.18.34 + 5.8.38 + 7.0.0 + 6.0.0-og + 9.4.0 + 5.13.0 + ${java.version} + ${java.version} + 3.2.0 + 3.11.0 + 3.0.0-M7 + true + true + + + + jitpack.io + https://jitpack.io + + + + + + io.quarkus.platform + quarkus-bom + ${quarkus.platform.version} + pom + import + + + + + + + io.quarkus + quarkus-vertx + + + io.quarkus + quarkus-messaging + + + io.quarkus + quarkus-resteasy + + + io.quarkus.resteasy.reactive + resteasy-reactive-common + + + io.quarkus + quarkus-resteasy-mutiny + + + io.quarkus + quarkus-resteasy-client + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-resteasy-client-jackson + + + + io.quarkus + quarkus-config-yaml + + + io.quarkus + quarkus-scheduler + + + io.opentelemetry + opentelemetry-exporter-logging + + + + io.opentelemetry + opentelemetry-api + + + io.opentelemetry + opentelemetry-sdk + + + io.quarkus + quarkus-smallrye-context-propagation + + + + io.quarkus + quarkus-arc + + + + com.github.oshi + oshi-core + ${oshi.version} + + + com.squareup.retrofit2 + retrofit + ${retrofit.version} + + + com.squareup.retrofit2 + converter-jackson + ${retrofit.version} + + + net.java.dev.jna + jna-platform + ${jna.version} + + + net.java.dev.jna + jna + ${jna.version} + + + cn.hutool + hutool-all + ${hutool.version} + + + org.apache.commons + commons-lang3 + ${common.lang3.version} + + + org.projectlombok + lombok + ${lombok.version} + + + com.zaxxer + HikariCP + ${hikaricp.version} + compile + + + com.mysql + mysql-connector-j + ${mysql-jdbc.version} + + + org.opengauss + opengauss-jdbc + ${opengauss-jdbc.version} + + + org.apache.commons + commons-exec + ${commons.exec.version} + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.mockito + mockito-core + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + ${java.home}/lib/rt.jar + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + org.jboss.jandex + jandex-maven-plugin + 1.2.3 + + + make-index + jandex + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + + + + build + + + + + datakit-agent-${version} + ../agent + + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.0 + + + copy-configs + package + + copy-resources + + + ../agent + + + src/main/resources + + * + + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + package + + + + + + + run + + + + + + + + + + + native + + + native + + + + true + false + + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/datakit-agent-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + + diff --git a/ER-Model/openGauss-datakit-agent/src/main/docker/Dockerfile.jvm b/ER-Model/openGauss-datakit-agent/src/main/docker/Dockerfile.jvm new file mode 100644 index 000000000..4cfaaaa69 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/docker/Dockerfile.jvm @@ -0,0 +1,97 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/getting-started-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/getting-started-jvm +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/getting-started-jvm +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi8/openjdk-17:1.20 + +ENV LANGUAGE='en_US:en' + + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 target/quarkus-app/*.jar /deployments/ +COPY --chown=185 target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] + diff --git a/ER-Model/openGauss-datakit-agent/src/main/docker/Dockerfile.legacy-jar b/ER-Model/openGauss-datakit-agent/src/main/docker/Dockerfile.legacy-jar new file mode 100644 index 000000000..56931608b --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/docker/Dockerfile.legacy-jar @@ -0,0 +1,93 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package -Dquarkus.package.jar.type=legacy-jar +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/getting-started-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/getting-started-legacy-jar +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/getting-started-legacy-jar +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi8/openjdk-17:1.20 + +ENV LANGUAGE='en_US:en' + + +COPY target/lib/* /deployments/lib/ +COPY target/*-runner.jar /deployments/quarkus-run.jar + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] diff --git a/ER-Model/openGauss-datakit-agent/src/main/docker/Dockerfile.native b/ER-Model/openGauss-datakit-agent/src/main/docker/Dockerfile.native new file mode 100644 index 000000000..795084ad9 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/docker/Dockerfile.native @@ -0,0 +1,27 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/getting-started . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/getting-started +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.10 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/ER-Model/openGauss-datakit-agent/src/main/docker/Dockerfile.native-micro b/ER-Model/openGauss-datakit-agent/src/main/docker/Dockerfile.native-micro new file mode 100644 index 000000000..7de7d8887 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/docker/Dockerfile.native-micro @@ -0,0 +1,30 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# It uses a micro base image, tuned for Quarkus native executables. +# It reduces the size of the resulting container image. +# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/getting-started . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/getting-started +# +### +FROM quay.io/quarkus/quarkus-micro-image:2.0 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/AgentServerClient.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/AgentServerClient.java new file mode 100644 index 000000000..549775e37 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/AgentServerClient.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.client; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; + +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +/** + * AgentServerClient + * + * @author: wangchao + * @Date: 2025/2/27 12:25 + * @Description: AgentServerClient + * @since 7.0.0-RC2 + **/ +@Path("/agent") +@RegisterRestClient +public interface AgentServerClient { + /** + * task callback start + * + * @param agentId agent id + */ + @POST + @Path("/task/callback/start") + void taskCallbackStart(@QueryParam("agentId") Long agentId); +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/DataStreamService.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/DataStreamService.java new file mode 100644 index 000000000..6a19011a6 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/DataStreamService.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.client; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; +import org.opengauss.agent.entity.ProcessedData; + +import java.util.List; + +/** + * DataStreamService + * + * @author: wangchao + * @Date: 2025/2/28 09:26 + * @Description: DataStreamService + * @since 7.0.0-RC2 + **/ +@Path("/agent") +@RegisterRestClient +public interface DataStreamService { + /** + * push data to downstream service + * + * @param data data + */ + @POST + @Path("/data") + @Produces(MediaType.APPLICATION_JSON) + void pushData(ProcessedData data); + + /** + * batch push data to downstream service + * + * @param dataList data list + */ + @POST + @Path("/batch/data") + @Produces(MediaType.APPLICATION_JSON) + void batchPushData(List dataList); +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/DynamicHttpClientBuilder.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/DynamicHttpClientBuilder.java new file mode 100644 index 000000000..51a4ffa9f --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/DynamicHttpClientBuilder.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.client; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; +import retrofit2.Retrofit; +import retrofit2.converter.jackson.JacksonConverterFactory; + +import org.opengauss.agent.config.AgentSslContext; +import org.opengauss.agent.constant.AgentConstants; +import org.opengauss.agent.exception.AgentException; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.text.SimpleDateFormat; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; + +/** + * DynamicHttpClientBuilder + * + * @author: wangchao + * @Date: 2025/5/22 10:33 + * @since 7.0.0-RC2 + **/ +public class DynamicHttpClientBuilder { + private static final ObjectMapper MAPPER = new ObjectMapper().registerModule(new JavaTimeModule()) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .setTimeZone(TimeZone.getTimeZone("UTC")) // 全局时区 + .setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")) + .registerModule(new SimpleModule().addSerializer(Integer.class, new ToStringSerializer()) // 将Integer序列化为字符串 + .addSerializer(Integer.TYPE, new ToStringSerializer()) // 处理基本类型 Integer + .addSerializer(Long.class, new ToStringSerializer()) // 将Long序列化为字符串 + .addSerializer(Long.TYPE, new ToStringSerializer())); // 处理基本类型long + + /** + * createHttpClient + * + * @param baseUrl baseUrl + * @return MetricHttpClient + */ + public static MetricHttpClient createHttpClient(String baseUrl) { + Retrofit retrofit = new Retrofit.Builder().baseUrl(formatBaseUrl(baseUrl)) + .client(defaultClient()) + .addConverterFactory(JacksonConverterFactory.create(MAPPER)) + .build(); + return retrofit.create(MetricHttpClient.class); + } + + private static String formatBaseUrl(String rawUrl) { + return rawUrl.endsWith("/") ? rawUrl : rawUrl + "/"; + } + + private static OkHttpClient defaultClient() { + OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES)); + String protocol = ServerUrlBuilder.getAgentServerProtocol(); + if (AgentConstants.ConfigProperties.HTTPS.equalsIgnoreCase(protocol)) { + configureTrustAll(clientBuilder); + } + return clientBuilder.build(); + } + + private static void configureTrustAll(OkHttpClient.Builder clientBuilder) { + try { + SSLContext sslContext = AgentSslContext.configureSslContext(); + SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + clientBuilder.sslSocketFactory(sslSocketFactory, AgentSslContext.getX509TrustManager()); + clientBuilder.hostnameVerifier((hostname, session) -> true); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new AgentException("Failed to configure unsafe SSL", e); + } + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/HeartbeatServerClient.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/HeartbeatServerClient.java new file mode 100644 index 000000000..d8faf2128 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/HeartbeatServerClient.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.client; + +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; +import org.opengauss.agent.entity.HeartbeatReport; + +/** + * HeartbeatServerClient + * + * @author: wangchao + * @Date: 2025/2/27 12:25 + * @Description: HeartbeatServerClient + * @since 7.0.0-RC2 + **/ +@Path("/agent") +@RegisterRestClient +public interface HeartbeatServerClient { + /** + * heartbeat + * + * @param customHeader custom header + * @param heart heartbeat report + */ + @POST + @Path("/heartbeat") + @Produces(MediaType.APPLICATION_JSON) + void heartbeat(@HeaderParam("X-Custom-Header") String customHeader, HeartbeatReport heart); + + /** + * server deregister + * + * @param customHeader custom header + * @param requestDown heartbeat report + */ + @POST + @Path("/deregister") + void deregister(@HeaderParam("X-Custom-Header") String customHeader, HeartbeatReport requestDown); +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/HostFixedMetricsClient.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/HostFixedMetricsClient.java new file mode 100644 index 000000000..85a7d3372 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/HostFixedMetricsClient.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.client; + +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; +import org.opengauss.agent.entity.HostBaseInfo; + +/** + * HostFixedMetricsClient + * + * @author: wangchao + * @Date: 2025/2/27 12:25 + * @Description: HostFixedMetricsClient + * @since 7.0.0-RC2 + **/ +@Path("/receive") +@RegisterRestClient +public interface HostFixedMetricsClient { + /** + * sendHostBaseInfo + * + * @param customHeader X-Custom-Header + * @param hostBaseInfo hostBaseInfo + */ + @POST + @Path("/fixed/host/info") + @Produces(MediaType.APPLICATION_JSON) + void sendHostBaseInfo(@HeaderParam("X-Custom-Header") String customHeader, HostBaseInfo hostBaseInfo); +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/MetricHttpClient.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/MetricHttpClient.java new file mode 100644 index 000000000..247083f2d --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/MetricHttpClient.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.client; + +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.POST; +import retrofit2.http.Path; +import retrofit2.http.Query; + +import org.opengauss.agent.entity.Metric; + +import java.util.List; +import java.util.Map; + +/** + * MetricHttpClient + * + * @author: wangchao + * @Date: 2025/5/16 11:41 + * @Description: MetricHttpClient + * @since 7.0.0-RC2 + **/ +public interface MetricHttpClient { + /** + * send metrics + * + * @param path url path template, e.g. /metrics/collect + * @param taskIds task id list + * @param agentId agent id + * @param metricsPayload metrics payload + * @return void + */ + @POST("{path}") + Call sendMetrics(@Path(value = "path", encoded = true) String path, @Query("taskIds") List taskIds, + @Query("agentId") Long agentId, @Body List metricsPayload); + + /** + * send data metrics + * + * @param path url path template, e.g. /metrics/collect + * @param taskId task id + * @param agentId agent id + * @param dataList metrics payload + * @return void + */ + @POST("{path}") + Call sendDataMetrics(@Path(value = "path", encoded = true) String path, @Query("taskId") Long taskId, + @Query("agentId") Long agentId, @Body List> dataList); +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/ServerClientFactory.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/ServerClientFactory.java new file mode 100644 index 000000000..81a52e874 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/ServerClientFactory.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.client; + +import jakarta.enterprise.context.ApplicationScoped; + +import org.eclipse.microprofile.rest.client.RestClientBuilder; +import org.opengauss.agent.config.AgentSslContext; +import org.opengauss.agent.constant.AgentConstants; +import org.opengauss.agent.exception.AgentException; + +import java.net.URI; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +/** + * ServerClientFactory + * + * @author: wangchao + * @Date: 2025/10/21 11:05 + * @since 7.0.0-RC3 + **/ +@ApplicationScoped +public class ServerClientFactory { + /** + * create heartbeat client + * + * @return HeartbeatServerClient + */ + public HeartbeatServerClient createHeartbeatClient() { + return createClient(HeartbeatServerClient.class); + } + + /** + * create agent server client + * + * @return AgentServerClient + */ + public AgentServerClient createAgentServerClient() { + return createClient(AgentServerClient.class); + } + + /** + * create HostFixedMetricsClient client + * + * @return HostFixedMetricsClient + */ + public HostFixedMetricsClient createHostFixedMetricsClient() { + return createClient(HostFixedMetricsClient.class); + } + + /** + * create DataStreamService client + * + * @return DataStreamService + */ + public DataStreamService createDataStreamService() { + return createClient(DataStreamService.class); + } + + private T createClient(Class clientClass) { + try { + URI baseUri = ServerUrlBuilder.buildBaseUri(); + RestClientBuilder builder = RestClientBuilder.newBuilder().baseUri(baseUri); + String protocol = ServerUrlBuilder.getAgentServerProtocol(); + if (AgentConstants.ConfigProperties.HTTPS.equalsIgnoreCase(protocol)) { + builder.sslContext(AgentSslContext.configureSslContext()).hostnameVerifier((hostname, session) -> true); + } + return builder.build(clientClass); + } catch (NoSuchAlgorithmException | KeyManagementException ex) { + throw new AgentException("build agent server client error", ex); + } + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/ServerUrlBuilder.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/ServerUrlBuilder.java new file mode 100644 index 000000000..233be4a61 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/client/ServerUrlBuilder.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.client; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.opengauss.agent.constant.AgentConstants; + +import java.net.URI; +import java.util.Locale; + +/** + * ServerUrlBuilder + * + * @author: wangchao + * @Date: 2025/10/21 11:02 + * @since 7.0.0-RC2 + **/ +public class ServerUrlBuilder { + /** + * parse agent server protocol + * + * @return protocol + */ + public static String getAgentServerProtocol() { + String server = getAgentServerUrl(); + return server.split("://")[0]; + } + + private static String getAgentServerUrl() { + return ConfigProvider.getConfig().getValue(AgentConstants.ConfigProperties.AGENT_SERVER, String.class); + } + + /** + * get agent server base uri + * + * @return uri + */ + public static URI buildBaseUri() { + String server = getAgentServerUrl(); + return URI.create(server); + } + + /** + * get agent endpoint uri + * + * @param path interface path + * @return path uri + */ + public static URI buildEndpointUri(String path) { + String server = getAgentServerUrl(); + return URI.create(String.format(Locale.getDefault(), "%s/%s", server, path)); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/common/MutinyExecutor.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/common/MutinyExecutor.java new file mode 100644 index 000000000..d37c62a4e --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/common/MutinyExecutor.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.common; + +import io.quarkus.arc.Unremovable; +import io.quarkus.runtime.ShutdownEvent; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Singleton; +import lombok.Data; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * MutinyExecutor + * + * @author: wangchao + * @Date: 2025/5/21 11:14 + * @since 7.0.0-RC2 + **/ +@Singleton +@Data +@Unremovable +public class MutinyExecutor { + @ConfigProperty(name = "agent.thread-pool.max-threads") + int poolSize; + private ExecutorService workerPool; + private ScheduledExecutorService scheduledExecutorService; + + /** + * init + */ + @PostConstruct + void init() { + workerPool = Executors.newFixedThreadPool(poolSize); + scheduledExecutorService = Executors.newScheduledThreadPool(poolSize); + } + + /** + * execute + * + * @param command command + */ + public void execute(Runnable command) { + workerPool.execute(command); + } + + /** + * schedule + * + * @param command command + * @param delay delay + * @param unit unit + * @return ScheduledFuture + */ + public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { + return scheduledExecutorService.scheduleWithFixedDelay(command, delay, delay, unit); + } + + void onStop(@Observes ShutdownEvent event) { + if (workerPool != null) { + workerPool.shutdown(); + } + if (scheduledExecutorService != null) { + scheduledExecutorService.shutdown(); + } + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/config/AgentSslContext.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/config/AgentSslContext.java new file mode 100644 index 000000000..fa1ba213c --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/config/AgentSslContext.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.config; + +import org.opengauss.agent.exception.AgentException; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +/** + * AgentSSLContext + * + * @author: wangchao + * @Date: 2025/10/21 16:09 + * @since 7.0.0-RC3 + **/ +public class AgentSslContext { + private static final TrustManager[] TRUST_MANAGERS = {new InsecureTrustManager()}; + + /** + * config and init ssl context + * + * @return ssl context + * @throws NoSuchAlgorithmException NoSuchAlgorithmException + * @throws KeyManagementException KeyManagementException + */ + public static SSLContext configureSslContext() throws NoSuchAlgorithmException, KeyManagementException { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, TRUST_MANAGERS, new java.security.SecureRandom()); + return sslContext; + } + + /** + * get x509 trust manager + * + * @return X509TrustManager + */ + public static X509TrustManager getX509TrustManager() { + if (TRUST_MANAGERS != null && TRUST_MANAGERS[0] instanceof X509TrustManager x509TrustManager) { + return x509TrustManager; + } + throw new AgentException("init trust manager failed"); + } + + private static class InsecureTrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/config/AppConfig.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/config/AppConfig.java new file mode 100644 index 000000000..7c71f01fb --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/config/AppConfig.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.config; + +import jakarta.inject.Singleton; +import lombok.Data; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +/** + * AppConfig + * + * @author: wangchao + * @Date: 2025/4/26 15:46 + * @Description: AppConfig + * @since 7.0.0-RC2 + **/ +@Singleton +@Data +public class AppConfig { + @ConfigProperty(name = "agent.id") + Long agentId; + @ConfigProperty(name = "agent.name") + String appName; + @ConfigProperty(name = "agent.version") + String appVersion; + @ConfigProperty(name = "agent.server") + String appServerUrl; + @ConfigProperty(name = "agent.heartbeat.interval") + int heartbeatInterval; + @ConfigProperty(name = "agent.heartbeat.break-wait-max-times") + int heartbeatBreakWaitMaxTimes; + @ConfigProperty(name = "agent.os.command-write-list", defaultValue = "") + String osCommandWriteList; + String osCommandDefaultWriteList + = "date,dig,ping,ls,echo,dir,wc,head,java,git,cat,mkdir,tail,grep,touch,less,sort,id,javac"; +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/config/AppConfigDistributor.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/config/AppConfigDistributor.java new file mode 100644 index 000000000..4ef6249b0 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/config/AppConfigDistributor.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.config; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import io.quarkus.runtime.StartupEvent; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.agent.utils.OsCommandUtils; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * AppConfigDistributor + * + * @author: wangchao + * @Date: 2025/7/7 15:28 + * @since 7.0.0-RC2 + **/ +@Slf4j +@ApplicationScoped +public class AppConfigDistributor { + @Inject + AppConfig appConfig; + + /** + * onStart initialize AppConfigDistributor instance when application start + * + * @param event StartupEvent + */ + void onStart(@Observes StartupEvent event) { + initializeOsCommandWriteList(); + } + + private void initializeOsCommandWriteList() { + String osCommandWriteList = appConfig.getOsCommandWriteList(); + String osCommandDefaultWriteList = appConfig.getOsCommandDefaultWriteList(); + List writeList = new LinkedList<>(); + if (StrUtil.isNotEmpty(osCommandDefaultWriteList)) { + writeList.addAll(Arrays.asList(osCommandDefaultWriteList.split(","))); + log.info("initialized osCommand default write list : {}", osCommandDefaultWriteList); + } + if (StrUtil.isNotEmpty(osCommandWriteList)) { + writeList.addAll(Arrays.asList(osCommandWriteList.split(","))); + log.info("initialized osCommand custom write list : {}", osCommandWriteList); + } + if (CollUtil.isNotEmpty(writeList)) { + OsCommandUtils.forceRefreshAllowedCommand(writeList); + log.info("initialized osCommand write list: {}", OsCommandUtils.getAllowedCommands()); + } else { + log.warn("initialized osCommand write list empty"); + } + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/constant/AgentConstants.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/constant/AgentConstants.java new file mode 100644 index 000000000..6a7fece59 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/constant/AgentConstants.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.constant; + +/** + * AgentConstants + * + * @author: wangchao + * @date: 2025/5/8 11:40 + * @since 7.0.0-RC2 + **/ +public interface AgentConstants { + /** + * NULL string + */ + String NULL = "null"; + + /** + * heartbeat status up + */ + String HEARTBEAT_STATUS_UP = "UP"; + + /** + * heartbeat status down + */ + String HEARTBEAT_STATUS_DOWN = "DOWN"; + + /** + * deviation threshold 50% + */ + double DEVIATION_THRESHOLD = 0.5; + + /** + * Duration Timing Constant + */ + interface Duration { + /** + * duration time split T + */ + String SPLIT_DATE_AND_TIME = "T"; + + /** + * duration time unit Y/M + */ + String YEAR = "Y"; + + /** + * duration time unit Y/M + */ + String MONTH = "M"; + + /** + * duration time prefix P + */ + String PREFIX_DATE = "P"; + + /** + * duration time prefix PT + */ + String PREFIX_ONLE_TIME = "PT"; + } + + /** + * MetricTranslate + */ + interface MetricTranslate { + /** + * metric name unknown + */ + String DEFAULT_NAME = "unknown_metric"; + + /** + * metric description, no description + */ + String DEFAULT_DESC = "no_description"; + + /** + * metric unit, no unit + */ + String DEFAULT_UNIT = "unitless"; + + /** + * metric type, no defined + */ + String DEFAULT_TYPE = "UNDEFINED"; + } + + /** + * agent config properties + */ + interface ConfigProperties { + /** + * https protocol + */ + String HTTPS = "https"; + + /** + * agnet server properties name + */ + String AGENT_SERVER = "agent.server"; + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/DataPoint.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/DataPoint.java new file mode 100644 index 000000000..a7275b3c3 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/DataPoint.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +import lombok.Data; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * DataPoint + * + * @author: wangchao + * @Date: 2025/3/15 11:53 + * @Description: DataPoint + * @since 7.0.0-RC2 + **/ +@Data +public class DataPoint { + private String value; + private Instant epochNanos; + private Instant startEpochNanos; + private Map attributes = new HashMap<>(); + + @Override + public String toString() { + return "value=" + value; + } + + /** + * used display data details + * + * @return string + */ + public String detail() { + return "{value='" + value + '\'' + ", startEpochNanos='" + startEpochNanos + '\'' + ", epochNanos='" + + epochNanos + '\'' + ", attributes=" + formatAttributes() + "}"; + } + + private String formatAttributes() { + return attributes.entrySet() + .stream() + .map(entry -> entry.getKey() + "='" + entry.getValue() + "'") + .collect(Collectors.joining(", ", "{", "}")); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/HeartbeatHeader.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/HeartbeatHeader.java new file mode 100644 index 000000000..6dcc9d789 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/HeartbeatHeader.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * HeartbeatReport + * + * @author: wangchao + * @Date: 2025/2/27 09:07 + * @Description: HeartbeatReport + * @since 7.0.0-Rc2 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class HeartbeatHeader { + private String agentName; + private String instanceId; + private String agentAddress; + private String target; + + /** + * Convert to heartbeat header string + * + * @return heartbeat header string + */ + public String toHeartbeatHeader() { + return agentName + ":" + agentAddress + " : " + instanceId + "->" + target; + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/HeartbeatReport.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/HeartbeatReport.java new file mode 100644 index 000000000..84e703016 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/HeartbeatReport.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +import static org.opengauss.agent.constant.AgentConstants.HEARTBEAT_STATUS_UP; + +import com.cronutils.utils.StringUtils; + +import lombok.Data; + +import java.time.Instant; + +/** + * HeartbeatReport + * + * @author: wangchao + * @Date: 2025/2/27 09:07 + * @Description: HeartbeatReport + * @since 7.0.0-RC2 + **/ +@Data +public class HeartbeatReport { + private Instant timestamps; + private String status; + private Long agentId; + private String additionalInfo; + + /** + * Constructor + * + * @param agentId agent id + * @param timestamps timestamps + */ + public HeartbeatReport(Long agentId, Instant timestamps) { + this.agentId = agentId; + this.timestamps = timestamps; + this.status = HEARTBEAT_STATUS_UP; + } + + /** + * Convert to heartbeat report + * + * @return heartbeat report + */ + public String toHeartbeat() { + if (StringUtils.isEmpty(additionalInfo)) { + return "send heartbeat report " + status + ", at " + timestamps; + } + return "send heartbeat report " + status + ", at " + timestamps + ", additionalInfo: " + additionalInfo; + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/HostBaseInfo.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/HostBaseInfo.java new file mode 100644 index 000000000..c703f444b --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/HostBaseInfo.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * HostBaseInfo + * + * @author: wangchao + * @Date: 2025/4/8 17:18 + * @Description: HostBaseInfo + * @since 7.0.0-RC2 + **/ +@Data +@Builder +@Accessors(chain = true) +public class HostBaseInfo { + private String hostName; + private String cpuModel; + private String cpuArchitecture; + private long cpuFreq; + private int physicalCores; + private int logicalCores; + private String osName; + private String osVersion; + private String osBuild; +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/HostTaskDefinition.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/HostTaskDefinition.java new file mode 100644 index 000000000..1dad592dd --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/HostTaskDefinition.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * HostTaskDefinition + * + * @author: wangchao + * @Date: 2025/4/8 15:44 + * @Description: HostTaskDefinition + * @since 7.0.0-RC2 + **/ +@Data +@EqualsAndHashCode(callSuper = true) +public class HostTaskDefinition extends TaskDefinition { + private boolean isLocal; + + /** + * HostTaskDefinition + * + * @param isLocal isLocal + */ + public HostTaskDefinition(boolean isLocal) { + this.isLocal = isLocal; + } + + /** + * HostTaskDefinition + */ + public HostTaskDefinition() { + this(true); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/Metric.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/Metric.java new file mode 100644 index 000000000..ff4fa3555 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/Metric.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Metric + * + * @author: wangchao + * @Date: 2025/3/15 11:51 + * @Description: Metric + * @since 7.0.0-RC2 + **/ +@Getter +@NoArgsConstructor +public class Metric { + private String name; + private String description; + private String unit; + private String type; + @Setter + private List points; + + /** + * constructor + * + * @param name metric name + * @param description metric description + * @param unit metric unit + * @param type metric type + */ + public Metric(String name, String description, String unit, String type) { + this.name = name; + this.description = description; + this.unit = unit; + this.type = type; + } + + @Override + public String toString() { + return "\n|---------- Metric ----------\n| Name: " + name + "\n| Description: " + description + "\n| Unit: " + + unit + "\n| Type: " + type + "\n|------ Data Points ------" + (points == null || points.isEmpty() + ? "\n|(No data points)" + : points.stream().map(dp -> "\n| " + dp.toString().replace("\n", "\n| ")).collect(Collectors.joining())) + + "\n|--------------------"; + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/MetricRequest.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/MetricRequest.java new file mode 100644 index 000000000..c3766fadf --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/MetricRequest.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +import lombok.Data; + +import java.util.List; + +/** + * MetricRequest + * + * @author: wangchao + * @Date: 2025/3/21 09:14 + * @Description: MetricRequest + * @since 7.0.0-RC2 + **/ +@Data +public class MetricRequest { + private String agentId; + private String instanceId; + private String timestamp; + private String hostIp; + private List metrics; +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/OsCmdResult.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/OsCmdResult.java new file mode 100644 index 000000000..1ef31a123 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/OsCmdResult.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +/** + * OsCmdResult + * + * @author: wangchao + * @Date: 2025/7/5 11:04 + * @since 7.0.0-RC2 + **/ +public record OsCmdResult(int exitCode, String output) { + /** + * is success + * + * @return true if exitCode is 0, false otherwise + */ + public boolean isSuccess() { + return exitCode == 0; + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/ProcessedData.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/ProcessedData.java new file mode 100644 index 000000000..d64865e30 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/ProcessedData.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +import cn.hutool.json.JSONUtil; +import lombok.Data; + +/** + * ProcessedData + * + * @author: wangchao + * @Date: 2025/2/28 11:50 + * @Description: ProcessedData + * @since 7.0.0-RC2 + **/ +@Data +public class ProcessedData { + String message; + + /** + * Constructor + * + * @param raw RawData + */ + public ProcessedData(RawData raw) { + this.message = raw.getMessage(); + } + + /** + * toJson + * + * @return String + */ + public String toJson() { + return JSONUtil.toJsonStr(this); + } + + /** + * fromJson + * + * @param json String + * @return ProcessedData + */ + public static ProcessedData fromJson(String json) { + return JSONUtil.toBean(json, ProcessedData.class); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/RawData.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/RawData.java new file mode 100644 index 000000000..36fa3d2c9 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/RawData.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * RawData + * + * @author: wangchao + * @Date: 2025/2/28 11:50 + * @Description: RawData + * @since 7.0.0-RC2 + **/ +@Data +@AllArgsConstructor +public class RawData { + String message; +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/RemoteHostTaskDefinition.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/RemoteHostTaskDefinition.java new file mode 100644 index 000000000..4d6f474cc --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/RemoteHostTaskDefinition.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * RemoteHostTaskDefinition + * + * @author: wangchao + * @Date: 2025/4/8 15:44 + * @Description: RemoteHostTaskDefinition + * @since 7.0.0-RC2 + **/ +@Data +@EqualsAndHashCode(callSuper = true) +public class RemoteHostTaskDefinition extends HostTaskDefinition { + private String hostName; + private String hostIp; + private String hostPort; + private String hostUser; + private String hostPassword; + + /** + * RemoteHostTaskDefinition + */ + public RemoteHostTaskDefinition() { + super(false); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/TaskDefinition.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/TaskDefinition.java new file mode 100644 index 000000000..04c6abd3b --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/TaskDefinition.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +import lombok.Data; + +import org.opengauss.agent.entity.task.AgentClusterVo; +import org.opengauss.agent.entity.task.TaskMetricsDefinitionVo; +import org.opengauss.agent.enums.ObjectType; +import org.opengauss.agent.enums.StoragePolicy; +import org.opengauss.agent.enums.TaskType; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * TaskDefinition + * + * @author: wangchao + * @Date: 2025/4/8 15:44 + * @Description: TaskDefinition + * @since 7.0.0-RC2 + **/ +@Data +public class TaskDefinition { + private Long taskId; + private String taskName; + private String agentId; + private TaskType taskType; + private String groupTag; + private String pluginsTag; + private ObjectType operateObjType; + private String operateObj; + private String receiveApi; + private long period; + private long estimatedExecutionTime; + private TimeUnit unit; + private StoragePolicy storagePolicy; + private String dataSendTarget; + private boolean isDbTemplate; + private AgentClusterVo clusterConfig; + private List collectorList; + private List metricsDefinitionList; +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/TaskDefinitionBuilder.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/TaskDefinitionBuilder.java new file mode 100644 index 000000000..856779301 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/TaskDefinitionBuilder.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +import org.opengauss.agent.entity.task.AgentTaskVo; +import org.opengauss.agent.entity.task.TaskTemplateDefinitionVo; +import org.opengauss.agent.enums.ObjectType; +import org.opengauss.agent.utils.DurationUtils; + +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * TaskDefinitionBuilder + * + * @author: wangchao + * @Date: 2025/4/28 18:34 + * @Description: TaskDefinitionBuilder + * @since 7.0.0-RC2 + **/ +public class TaskDefinitionBuilder { + /** + * builder + * + * @param taskConfig taskConfig + * @return TaskDefinition + */ + public static TaskDefinition builder(AgentTaskVo taskConfig) { + TaskDefinition taskDefinition = new TaskDefinition(); + taskDefinition.setTaskId(taskConfig.getTaskId()); + taskDefinition.setTaskName(taskConfig.getTaskName()); + taskDefinition.setAgentId(taskConfig.getAgentId()); + TaskTemplateDefinitionVo definition = taskConfig.getTemplateDefinition(); + taskDefinition.setTaskType(definition.getType()); + taskDefinition.setStoragePolicy(definition.getStoragePolicy()); + taskDefinition.setGroupTag(definition.getGroupTag()); + taskDefinition.setPluginsTag(definition.getPluginsTag()); + taskDefinition.setOperateObjType(definition.getOperateObjType()); + taskDefinition.setOperateObj(definition.getOperateObj()); + taskDefinition.setReceiveApi(definition.getReceiveApi()); + taskDefinition.setPeriod(DurationUtils.parseToMillis(definition.getPeriod())); + taskDefinition.setEstimatedExecutionTime(Math.max(definition.getEstimatedExecutionTime(), 5)); + taskDefinition.setUnit(TimeUnit.MILLISECONDS); + taskDefinition.setDataSendTarget(definition.getReceiveApi()); + taskDefinition.setDbTemplate(isDbTemplate(definition)); + taskDefinition.setClusterConfig(taskConfig.getClusterConfig()); + taskDefinition.setMetricsDefinitionList(taskConfig.getMetricsDefinitionList()); + taskDefinition.setCollectorList(taskConfig.getCollectorMetricDetails()); + return taskDefinition; + } + + /** + * isDbTemplate + * + * @param definition definition + * @return boolean + */ + public static boolean isDbTemplate(TaskTemplateDefinitionVo definition) { + return Objects.equals(definition.getOperateObjType(), ObjectType.DB); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/TaskExecution.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/TaskExecution.java new file mode 100644 index 000000000..321af5e45 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/TaskExecution.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity; + +import cn.hutool.core.util.IdUtil; +import lombok.Data; + +import org.opengauss.agent.enums.TaskType; +import org.opengauss.agent.exception.AgentException; +import org.opengauss.agent.service.task.TaskExecutor; + +import java.time.Instant; +import java.util.Optional; + +/** + * TaskExecution + * + * @author: wangchao + * @Date: 2025/4/9 09:28 + * @Description: TaskExecution + * @since 7.0.0-RC2 + **/ +@Data +public class TaskExecution { + private String executionId; + private Long taskId; + private TaskDefinition taskDefinition; + private TaskExecutor executor; + private Instant startTime; + private Instant endTime; + private String status; + private String errorMessage; + + /** + * Constructor for TaskExecution + * + * @param taskDefinition taskDefinition + */ + public TaskExecution(TaskDefinition taskDefinition) { + this.executionId = IdUtil.getSnowflakeNextIdStr(); + this.taskId = taskDefinition.getTaskId(); + this.taskDefinition = taskDefinition; + this.startTime = Instant.now(); + this.status = "running"; + } + + /** + * getTaskType + * + * @return TaskType + */ + public TaskType getTaskType() { + return Optional.ofNullable(taskDefinition) + .orElseThrow(() -> new AgentException("TaskExecution [" + executionId + "] taskDefinition is null")) + .getTaskType(); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/task/AgentClusterVo.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/task/AgentClusterVo.java new file mode 100644 index 000000000..5cd439689 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/task/AgentClusterVo.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity.task; + +import lombok.Data; +import lombok.ToString; + +/** + * AgentClusterVo + * + * @author: wangchao + * @date: 2025/4/18 11:40 + * @since 7.0.0-RC2 + **/ +@Data +public class AgentClusterVo { + private String clusterId; + private String clusterNodeId; + private String dataBaseType; + private String hostIp; + private String port; + private String username; + @ToString.Exclude + private String dbPassword; + private String url; +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/task/AgentTaskVo.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/task/AgentTaskVo.java new file mode 100644 index 000000000..37405a24f --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/task/AgentTaskVo.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity.task; + +import lombok.Data; + +import java.util.List; + +/** + * AgentTaskVo + * + * @author: wangchao + * @Date: 2025/4/21 16:34 + * @Description: AgentTaskVo + * @since 7.0.0-RC2 + **/ +@Data +public class AgentTaskVo { + private Long taskId; + private String taskName; + private String agentId; + private TaskTemplateDefinitionVo templateDefinition; + private List collectorMetricDetails; + private List metricsDefinitionList; + private AgentClusterVo clusterConfig; +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/task/TaskMetricsDefinitionVo.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/task/TaskMetricsDefinitionVo.java new file mode 100644 index 000000000..8243899bb --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/task/TaskMetricsDefinitionVo.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity.task; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * AgentTaskMetricsDefinition + * + * @author: wangchao + * @Date: 2025/4/16 09:34 + * @Description: AgentTaskMetricsDefinition + * @since 7.0.0-RC2 + **/ +@Data +@EqualsAndHashCode +public class TaskMetricsDefinitionVo { + private String name; + private String description; + private String fieldName; + private String unit; + private String dataType; + private String prop; + private String collectCmd; +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/task/TaskTemplateDefinitionVo.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/task/TaskTemplateDefinitionVo.java new file mode 100644 index 000000000..0e6fda91a --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/entity/task/TaskTemplateDefinitionVo.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.entity.task; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import org.opengauss.agent.enums.ObjectType; +import org.opengauss.agent.enums.StoragePolicy; +import org.opengauss.agent.enums.TaskType; + +/** + * TaskTemplateDefinitionVo + * + * @author: wangchao + * @Date: 2025/4/16 09:34 + * @Description: TaskTemplateDefinitionVo + * @since 7.0.0-RC2 + **/ +@Data +@EqualsAndHashCode +public class TaskTemplateDefinitionVo { + private String name; + private TaskType type; + private String groupTag; + private String pluginsTag; + private ObjectType operateObjType; + private String operateObj; + + /** + *
+     * Obtains a Duration from a text string such as PnDTnHnMn.nS.
+     * time period and duration ,1year 2month 3week 4day 5hour 6minutes 7.5second eg. "P1Y2M3W4DT5H6M7.5S"
+     *
+     * The formats accepted are based on the ISO-8601 duration format PnDTnHnMn.nS with days considered to be exactly
+     * 24 hours.
+     * For example, P1Y2M3W4DT5H6M7S is a valid duration.
+     * A date-based amount of time in the ISO-8601 calendar system, such as '2 years, 3 months and 4 days'.
+     * 
+ */ + private String period; + private String collectMetric; + private StoragePolicy storagePolicy; + + /** + *
+     * Obtains a Duration from a text string such as PnDTnHnMn.nS.
+     * time period and duration,1year 2month 3week 4day 5hour 6minutes 7.5second eg.
+     * time period and duration,1year 2month 3week 4day 5hour 6minutes 7.5second eg. "P1Y2M3W4DT5H6M7.5S"
+     *
+     * The formats accepted are based on the ISO-8601 duration format PnDTnHnMn.nS with days considered to be exactly
+     * 24 hours.
+     * For example,      * For example, P1Y2M3W4DT5H6M7S is a valid duration.
+     * A date-based amount of time in the ISO-8601 calendar system, such as '2 years, 3 months and 4 days'.
+     * 
+ */ + private String keepPeriod; + + /** + * task estimated execution time , unit is milliseconds + */ + private long estimatedExecutionTime; + private String receiveApi; +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/ConnectionContainer.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/ConnectionContainer.java new file mode 100644 index 000000000..cf6bd2d17 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/ConnectionContainer.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.enums; + +import com.zaxxer.hikari.HikariConfig; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * ConnectionContainer + * + * @author: wangchao + * @Date: 2025/5/17 09:22 + * @Description: ConnectionContainer + * @since 7.0.0-RC2 + **/ +public interface ConnectionContainer { + /** + * configureDataSource + * + * @param hikariConfig hikariConfig + * @param url url + * @param user user + * @param password password + */ + void configureDataSource(HikariConfig hikariConfig, String url, String user, String password); + + /** + * getConnection + * + * @param url url + * @param user user + * @param password password + * @return Connection + * @throws SQLException SQLException + */ + Connection getConnection(String url, String user, String password) throws SQLException; +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/DatabaseType.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/DatabaseType.java new file mode 100644 index 000000000..1df6fd3d4 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/DatabaseType.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.enums; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + +import lombok.Getter; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Locale; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.sql.DataSource; + +/** + * DatabaseType + * + * @author: wangchao + * @Date: 2025/5/6 17:31 + * @Description: DatabaseType + * @since 7.0.0-RC2 + **/ +@Getter +public enum DatabaseType implements ConnectionContainer { + MYSQL("mysql", "com.mysql.cj.jdbc.Driver") { + @Override + public void configureDataSource(HikariConfig config, String url, String user, String password) { + config.setJdbcUrl(url); + config.setUsername(user); + config.setPassword(password); + config.addDataSourceProperty("cachePrepStmts", "true"); + config.addDataSourceProperty("prepStmtCacheSize", "250"); + config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); + } + }, + OPENGAUSS("opengauss", "org.opengauss.Driver") { + @Override + public void configureDataSource(HikariConfig config, String url, String user, String password) { + config.setJdbcUrl(url); + config.setUsername(user); + config.setPassword(password); + config.addDataSourceProperty("ssl", "false"); + } + }, + POSTGRESQL("postgresql", "org.postgresql.Driver") { + @Override + public void configureDataSource(HikariConfig config, String url, String user, String password) { + config.setJdbcUrl(url); + config.setUsername(user); + config.setPassword(password); + } + }, + ORACLE("oracle", "oracle.jdbc.driver.OracleDriver") { + @Override + public void configureDataSource(HikariConfig config, String url, String user, String password) { + config.setJdbcUrl(url); + config.setUsername(user); + config.setPassword(password); + config.addDataSourceProperty("implicitCachingEnabled", "true"); + } + }, + SQL_SERVER("sqlserver", "com.microsoft.sqlserver.jdbc.SQLServerDriver") { + @Override + public void configureDataSource(HikariConfig config, String url, String user, String password) { + config.setJdbcUrl(url); + config.setUsername(user); + config.setPassword(password); + config.addDataSourceProperty("encrypt", "false"); + } + }, + UNKNOWN("", null) { + @Override + public void configureDataSource(HikariConfig config, String url, String user, String password) { + throw new UnsupportedOperationException("Unknown database type"); + } + + @Override + public Connection getConnection(String url, String user, String password) throws SQLException { + throw new UnsupportedOperationException(); + } + }; + + private final String value; + private final String driverClass; + private final ConcurrentMap dataSources = new ConcurrentHashMap<>(); + + DatabaseType(String value, String driverClass) { + this.value = value.trim().toLowerCase(Locale.ENGLISH); + this.driverClass = driverClass; + } + + @Override + public Connection getConnection(String url, String user, String password) throws SQLException { + if (this == UNKNOWN) { + throw new SQLException("Unsupported database type"); + } + String poolKey = generatePoolKey(url, user); + DataSource dataSource = dataSources.computeIfAbsent(poolKey, k -> { + HikariConfig hikariConfig = createHikariConfig(url, user, password); + return new HikariDataSource(hikariConfig); + }); + return dataSource.getConnection(); + } + + private HikariConfig createHikariConfig(String url, String user, String password) { + HikariConfig config = new HikariConfig(); + config.setDriverClassName(driverClass); + config.setPoolName("hikariPool-" + this.value + "_" + url.hashCode()); + config.setMaximumPoolSize(10); + config.setMinimumIdle(2); + config.setConnectionTimeout(30000); + config.setIdleTimeout(600000); + config.setMaxLifetime(1800000); + configureDataSource(config, url, user, password); + return config; + } + + private String generatePoolKey(String url, String user) { + return url + "|" + user; + } + + /** + * getByValue + * + * @param value String + * @return Matching DatabaseType instance + */ + public static DatabaseType getByValue(String value) { + String normalizedValue = value == null ? "" : value.trim().toLowerCase(Locale.ENGLISH); + for (DatabaseType type : values()) { + if (type.value.equals(normalizedValue)) { + return type; + } + } + return UNKNOWN; + } + + /** + * current database type is support + * + * @param value String + * @return Boolean + */ + public static boolean isSupport(String value) { + return getByValue(value) != UNKNOWN; + } + + /** + * shutdownAll + * Shutdown all data sources in all database types. + */ + public static void shutdownAll() { + for (DatabaseType type : values()) { + type.dataSources.values().forEach(ds -> { + if (ds instanceof HikariDataSource) { + ((HikariDataSource) ds).close(); + } + }); + type.dataSources.clear(); + } + } +} + diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/MetricDataType.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/MetricDataType.java new file mode 100644 index 000000000..e363fcaf3 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/MetricDataType.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * MetricDataType + * + * @author: wangchao + * @Date: 2025/5/17 11:39 + * @since 7.0.0-RC2 + **/ +@Getter +@AllArgsConstructor +public enum MetricDataType { + STRING("String"), + LONG("Long"), + DOUBLE("Double"), + BOOLEAN("Boolean"); + private final String value; +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/ObjectType.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/ObjectType.java new file mode 100644 index 000000000..54c5e0c24 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/ObjectType.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * ObjectType + * + * @author: wangchao + * @Date: 2025/5/21 11:14 + * @since 7.0.0-RC2 + **/ +@Getter +@AllArgsConstructor +public enum ObjectType { + OSHI("oshi"), + OS("os"), + DB("db"), + HTTP("http"); + private final String value; +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/StoragePolicy.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/StoragePolicy.java new file mode 100644 index 000000000..e9cd1bc51 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/StoragePolicy.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * StoragePolicy + * + * @author: wangchao + * @Date: 2025/4/7 10:47 + * @Description: StoragePolicy + * @since 7.0.0-RC2 + **/ +@Getter +@AllArgsConstructor +public enum StoragePolicy { + CUSTOM("custom"), + REAL_TIME("real_time"), + HISTORY("history"), + FINGERPRINT("fingerprint"), + TREE("tree"); + + private final String value; +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/TaskType.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/TaskType.java new file mode 100644 index 000000000..f45f207aa --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/enums/TaskType.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +/** + * TaskType + * + * @author: wangchao + * @Date: 2025/4/8 16:40 + * @Description: TaskType + * @since 7.0.0-RC2 + **/ +@Getter +@AllArgsConstructor +public enum TaskType { + OSHI_FIXED_METRIC("OSHI_FIXED_METRIC"), + OSHI_DYNAMIC_METRIC("OSHI_DYNAMIC_METRIC"), + OS_METRIC("OS_METRIC"), + DB_METRIC("DB_METRIC"), + OS_PIPE("OS_PIPE"), + DB_PIPE("DB_PIPE"); + + private String value; + + private static final List METRIC_TASK_TYPES = List.of(OSHI_FIXED_METRIC, OSHI_DYNAMIC_METRIC, OS_METRIC, + DB_METRIC); + + /** + * isOtelMetricTask + * + * @param taskType taskType + * @return boolean + */ + public static boolean isOtelMetricTask(TaskType taskType) { + return METRIC_TASK_TYPES.contains(taskType); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/exception/AgentException.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/exception/AgentException.java new file mode 100644 index 000000000..764bcee05 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/exception/AgentException.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.exception; + +/** + * AgentException + * + * @author: wangchao + * @date: 2025/5/8 11:40 + * @since 7.0.0-RC2 + **/ +public class AgentException extends RuntimeException { + /** + * agent exception constructor + * + * @param message message + */ + public AgentException(String message) { + super(message); + } + + /** + * agent exception constructor + * + * @param message message + * @param cause cause + */ + public AgentException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/exception/TaskExecutionException.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/exception/TaskExecutionException.java new file mode 100644 index 000000000..215fbefa9 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/exception/TaskExecutionException.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.exception; + +/** + * TaskExecutionException + * + * @author: wangchao + * @Date: 2025/4/9 09:32 + * @Description: TaskExecutionException + * @since 7.0.0-RC2 + **/ +public class TaskExecutionException extends AgentException { + /** + * agent task execution exception constructor + * + * @param message message + */ + public TaskExecutionException(String message) { + super(message); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/resource/TaskResource.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/resource/TaskResource.java new file mode 100644 index 000000000..9e7829233 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/resource/TaskResource.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.resource; + +import io.quarkus.arc.Unremovable; +import io.quarkus.runtime.ShutdownEvent; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import lombok.extern.slf4j.Slf4j; +import retrofit2.http.Body; + +import org.opengauss.agent.client.AgentServerClient; +import org.opengauss.agent.client.ServerClientFactory; +import org.opengauss.agent.common.MutinyExecutor; +import org.opengauss.agent.config.AppConfig; +import org.opengauss.agent.entity.TaskExecution; +import org.opengauss.agent.entity.task.AgentTaskVo; +import org.opengauss.agent.exception.AgentException; +import org.opengauss.agent.service.task.TaskManager; +import org.opengauss.agent.service.task.core.TaskExecutionRecordService; +import org.opengauss.agent.utils.RsaUtils; + +import java.time.Instant; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * TaskResource + * + * @author: wangchao + * @Date: 2025/2/28 20:04 + * @Description: TaskResource + * @since 7.0.0-RC2 + **/ +@Unremovable +@Slf4j +@Path("/agent") +public class TaskResource { + @Inject + MutinyExecutor mutinyExecutor; + @Inject + TaskManager taskManager; + @Inject + AppConfig appConfig; + @Inject + TaskExecutionRecordService taskExecutionRecordService; + AgentServerClient agentServerClient; + @Inject + ServerClientFactory clientFactory; + private final AtomicBoolean isCalledTask = new AtomicBoolean(false); + private final Object taskLock = new Object(); + private ScheduledFuture scheduledTask; + + /** + * init + */ + void init() { + agentServerClient = clientFactory.createAgentServerClient(); + scheduledTask = mutinyExecutor.schedule(() -> { + if (taskManager.isEmpty() || !isCalledTask.get()) { + log.info("no task to inspect"); + try { + agentServerClient.taskCallbackStart(appConfig.getAgentId()); + log.info("task callback start success"); + isCalledTask.set(true); + } catch (AgentException ex) { + log.error("task callback start failed", ex); + } + } else { + cancelSchedule(); + } + }, 5, TimeUnit.SECONDS); + log.info("TaskResource init start task callback scheduler success"); + } + + private void cancelSchedule() { + synchronized (taskLock) { + if (scheduledTask != null && !scheduledTask.isDone()) { + scheduledTask.cancel(true); + } + } + } + + /** + * stop + * + * @param event event + */ + void onStop(@Observes ShutdownEvent event) { + cancelSchedule(); + } + + /** + * health check: other processes can call this endpoint to check if the process is still alive + * /agent/health + * + * @return string + */ + @GET + @Path("/health") + @Produces(MediaType.APPLICATION_JSON) + public String health() { + log.debug("agent health {}", Instant.now()); + return "success"; + } + + /** + * get secret rsa pub key + * + * @return pub key + */ + @GET + @Path("/pubKey") + @Produces(MediaType.APPLICATION_JSON) + public String pubKey() { + return RsaUtils.publicKey(); + } + + /** + * start task + * + * @param taskConfig task config + */ + @POST + @Path("/task/start") + @Produces(MediaType.APPLICATION_JSON) + public void start(@Body AgentTaskVo taskConfig) { + log.info("start task {}", taskConfig); + isCalledTask.set(true); + mutinyExecutor.getWorkerPool().execute(() -> { + TaskExecution execution = taskManager.startTask(taskConfig); + if (Objects.nonNull(execution)) { + taskExecutionRecordService.save(execution); + } + }); + } + + /** + * stop task + * + * @param taskId task id + */ + @POST + @Path("/task/stop") + @Produces(MediaType.APPLICATION_JSON) + public void stop(Long taskId) { + Optional execution = taskManager.stop(taskId); + execution.ifPresent(taskExecution -> { + taskExecutionRecordService.save(taskExecution); + }); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/heartbeat/HeartbeatScheduler.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/heartbeat/HeartbeatScheduler.java new file mode 100644 index 000000000..de0e8c231 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/heartbeat/HeartbeatScheduler.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.heartbeat; + +import cn.hutool.core.lang.UUID; +import cn.hutool.core.net.NetUtil; +import io.quarkus.runtime.Quarkus; +import io.quarkus.runtime.ShutdownEvent; +import io.quarkus.runtime.StartupEvent; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.agent.client.HeartbeatServerClient; +import org.opengauss.agent.client.ServerClientFactory; +import org.opengauss.agent.config.AppConfig; +import org.opengauss.agent.constant.AgentConstants; +import org.opengauss.agent.entity.HeartbeatHeader; +import org.opengauss.agent.entity.HeartbeatReport; + +import java.time.Instant; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * HeartbeatScheduler + * + * @author: wangchao + * @Date: 2025/2/26 18:45 + * @Description: HeartbeatScheduler + * @since 7.0.0-RC2 + **/ +@Slf4j +@ApplicationScoped +public class HeartbeatScheduler { + private static final String INSTANCE_ID = UUID.randomUUID().toString(); + private static final AtomicInteger HEARTBEAT_BREAK_TIMES = new AtomicInteger(0); + + HeartbeatHeader heartbeatHeader; + ScheduledExecutorService scheduler; + @Inject + AppConfig appConfig; + @Inject + ServerClientFactory clientFactory; + HeartbeatServerClient heartbeatServerClient = null; + + /** + * onStart + * + * @param event startup event + */ + void onStart(@Observes StartupEvent event) { + log.info("agent config : {}", appConfig); + initHeartbeatHeader(); + scheduler = Executors.newSingleThreadScheduledExecutor(); + heartbeatServerClient = clientFactory.createHeartbeatClient(); + scheduler.scheduleAtFixedRate(this::sendHeartbeat, 1, appConfig.getHeartbeatInterval(), TimeUnit.SECONDS); + } + + /** + * stop ,release resources + * + * @param event shutdown event + */ + void onStop(@Observes ShutdownEvent event) { + if (scheduler != null) { + scheduler.shutdownNow(); + } + HeartbeatReport report = new HeartbeatReport(appConfig.getAgentId(), Instant.now()); + try { + report.setStatus(AgentConstants.HEARTBEAT_STATUS_DOWN); + heartbeatServerClient.deregister(heartbeatHeader.toHeartbeatHeader(), report); + log.info("server deregister"); + } catch (Exception ex) { + log.warn("Failed to deregister server:" + report); + } + } + + private void initHeartbeatHeader() { + heartbeatHeader = new HeartbeatHeader(appConfig.getAppName(), INSTANCE_ID, NetUtil.getLocalhostStr(), + appConfig.getAppServerUrl()); + } + + private void sendHeartbeat() { + HeartbeatReport report = new HeartbeatReport(appConfig.getAgentId(), Instant.now()); + try { + heartbeatServerClient.heartbeat(heartbeatHeader.toHeartbeatHeader(), report); + HEARTBEAT_BREAK_TIMES.set(0); + log.info("agent heartbeat name=[{}] local=[{}] server=[{}]", heartbeatHeader.getAgentName(), + heartbeatHeader.getAgentAddress(), heartbeatHeader.getTarget()); + } catch (Exception e) { + if (HEARTBEAT_BREAK_TIMES.get() <= appConfig.getHeartbeatBreakWaitMaxTimes()) { + HEARTBEAT_BREAK_TIMES.incrementAndGet(); + log.error("Failed to send heartbeat name=[{}] local=[{}] server=[{}] {}", + heartbeatHeader.getAgentName(), heartbeatHeader.getAgentAddress(), heartbeatHeader.getTarget(), + e.getMessage()); + } else { + log.error("Heartbeat detection failure count exceeds limit, process will exit soon"); + Quarkus.asyncExit(1); // 优雅关闭 + } + } + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/otel/HttpMetricExporter.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/otel/HttpMetricExporter.java new file mode 100644 index 000000000..2fb876d88 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/otel/HttpMetricExporter.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.otel; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import lombok.extern.slf4j.Slf4j; +import retrofit2.Response; + +import org.opengauss.agent.client.DynamicHttpClientBuilder; +import org.opengauss.agent.client.MetricHttpClient; +import org.opengauss.agent.entity.Metric; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * HttpMetricExporter + * + * @author: wangchao + * @Date: 2025/3/14 17:13 + * @Description: HttpMetricExporter + * @since 7.0.0-RC2 + **/ +@Slf4j +public class HttpMetricExporter implements MetricExporter { + private final MetricHttpClient metricHttpClient; + private final String baseUrl; + private final AtomicBoolean isShutdown; + private final AggregationTemporality aggregationTemporality; + private final MetricDataTranslate metricDataTranslate = new MetricDataTranslate(); + private final Map commonParams = new ConcurrentHashMap<>(); + + /** + * constructor + * + * @param baseUrl URL Template + * @param aggregationTemporality otel aggregation temporality + * @param commonParams common params + */ + public HttpMetricExporter(String baseUrl, AggregationTemporality aggregationTemporality, + Map commonParams) { + this.baseUrl = baseUrl; + this.aggregationTemporality = aggregationTemporality; + this.commonParams.putAll(commonParams); + this.isShutdown = new AtomicBoolean(false); + this.metricHttpClient = DynamicHttpClientBuilder.createHttpClient(baseUrl); + } + + /** + * create http metric exporter with params + * + * @param baseUrl URL Template + * @param commonParams common params + * @return exporter + */ + public static HttpMetricExporter createWithParams(String baseUrl, Map commonParams) { + return new HttpMetricExporter(baseUrl, AggregationTemporality.CUMULATIVE, commonParams); + } + + @Override + public CompletableResultCode export(Collection metrics) { + try { + String path = resolvePath(baseUrl, commonParams); + List payload = metricDataTranslate.doTranslate(metrics); + Object objAgentId = commonParams.get("agentId"); + Long agentId = objAgentId instanceof Long ? (Long) objAgentId : Long.valueOf(objAgentId.toString()); + retrofit2.Call call = metricHttpClient.sendMetrics(path, (List) commonParams.get("taskIds"), + agentId, payload); + log.debug("Export http info | {}: {}", baseUrl, commonParams); + Response response = call.execute(); // 同步执行(或使用enqueue异步) + if (!response.isSuccessful()) { + log.error("Export failed | Code: {}", response.code()); + return CompletableResultCode.ofFailure(); + } + return CompletableResultCode.ofSuccess(); + } catch (Exception e) { + log.error("Export failed", e); + return CompletableResultCode.ofFailure(); + } + } + + private String resolvePath(String urlTemplate, Map params) { + String path = urlTemplate; + for (Map.Entry entry : params.entrySet()) { + String placeholder = "{" + entry.getKey() + "}"; + if (path.contains(placeholder)) { + path = path.replace(placeholder, entry.getValue().toString()); + } + } + return path; + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + if (isShutdown.getAndSet(true)) { + return CompletableResultCode.ofSuccess(); + } + return CompletableResultCode.ofSuccess(); + } + + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return aggregationTemporality; + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/otel/MetricDataTranslate.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/otel/MetricDataTranslate.java new file mode 100644 index 000000000..25d44c768 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/otel/MetricDataTranslate.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.otel; + +import static java.util.stream.Collectors.toList; +import static org.opengauss.agent.constant.AgentConstants.MetricTranslate.DEFAULT_DESC; +import static org.opengauss.agent.constant.AgentConstants.MetricTranslate.DEFAULT_NAME; +import static org.opengauss.agent.constant.AgentConstants.MetricTranslate.DEFAULT_TYPE; +import static org.opengauss.agent.constant.AgentConstants.MetricTranslate.DEFAULT_UNIT; + +import io.opentelemetry.sdk.metrics.data.Data; +import io.opentelemetry.sdk.metrics.data.DoublePointData; +import io.opentelemetry.sdk.metrics.data.HistogramPointData; +import io.opentelemetry.sdk.metrics.data.LongPointData; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.data.PointData; +import io.opentelemetry.sdk.metrics.data.SummaryPointData; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.agent.constant.AgentConstants; +import org.opengauss.agent.entity.DataPoint; +import org.opengauss.agent.entity.Metric; +import org.opengauss.agent.exception.AgentException; + +import java.time.Instant; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * MetricDataTranslate + * + * @author: wangchao + * @Date: 2025/3/15 14:14 + * @Description: MetricDataTranslate + * @since 7.0.0-RC2 + **/ +@Slf4j +public class MetricDataTranslate { + /** + * doTranslate + * + * @param metrics metrics + * @return List + */ + public List doTranslate(Collection metrics) { + if (metrics == null || metrics.isEmpty()) { + log.warn("Input metrics collection is empty"); + return Collections.emptyList(); + } + return metrics.stream() + .filter(Objects::nonNull) + .map(this::convertMetric) + .collect(toList()); + } + + private Metric convertMetric(MetricData metric) { + Metric converted = new Metric(getName(metric), getDescription(metric), getUnit(metric), getType(metric)); + converted.setPoints(getMetricDataPoint(metric)); + log.debug("Converted metric: {} {}", converted, metric.getData()); + return converted; + } + + private List getMetricDataPoint(MetricData metric) { + return Optional.ofNullable(metric.getData()) + .map(Data::getPoints) + .orElse(Collections.emptyList()) + .stream() + .map(this::convertDataPoint) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private DataPoint convertDataPoint(PointData point) { + DataPoint dp; + try { + dp = new DataPoint(); + dp.setValue(getPointValue(point)); // 保留原始数值类型 + dp.setEpochNanos(translateEpochNanos(point.getEpochNanos())); + dp.setStartEpochNanos(translateEpochNanos(point.getStartEpochNanos())); + convertAttributes(point, dp); + } catch (AgentException ex) { + log.error("DataPoint conversion error: {}", ex.getMessage()); + dp = null; + } + return dp; + } + + private void convertAttributes(PointData point, DataPoint dp) { + point.getAttributes() + .forEach((key, value) -> dp.getAttributes().put(attributeConvert(key), attributeConvert(value))); + } + + private String attributeConvert(Object obj) { + if (obj == null) { + return AgentConstants.NULL; + } + try { + if (obj instanceof Collection collection) { + return collection.stream().map(this::attributeConvert).collect(Collectors.joining(",")); + } else if (obj.getClass().isArray()) { + return Arrays.stream((Object[]) obj).map(this::attributeConvert).collect(Collectors.joining(",")); + } else { + return obj.toString(); + } + } catch (Exception e) { + log.error("Attribute conversion error: {}", e.getMessage()); + throw new AgentException("Attribute conversion error " + e.getMessage()); + } + } + + private static Instant translateEpochNanos(Long epochNanos) { + long seconds = epochNanos / 1_000_000_000L; // 提取秒部分 + int nanoAdjustment = (int) (epochNanos % 1_000_000_000L); // 剩余纳秒 + return Instant.ofEpochSecond(seconds, nanoAdjustment); + } + + private String getName(MetricData metric) { + return Optional.ofNullable(metric.getName()).orElse(DEFAULT_NAME); + } + + private String getDescription(MetricData metric) { + return Optional.ofNullable(metric.getDescription()).orElse(DEFAULT_DESC); + } + + private String getUnit(MetricData metric) { + return Optional.ofNullable(metric.getUnit()).orElse(DEFAULT_UNIT); + } + + private String getType(MetricData metric) { + return Optional.ofNullable(metric.getType()).map(Enum::name).orElse(DEFAULT_TYPE); + } + + private String getPointValue(PointData point) { + if (point instanceof LongPointData longPointData) { + return String.valueOf(longPointData.getValue()); + } else if (point instanceof DoublePointData doublePointData) { + return String.valueOf(doublePointData.getValue()); + } else if (point instanceof HistogramPointData histogramPointData) { + return String.valueOf(histogramPointData.getSum()); + } else if (point instanceof SummaryPointData summaryPointData) { + return String.valueOf(summaryPointData.getSum()); + } else { + throw new AgentException("Unsupported point data type"); + } + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/TaskExecutor.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/TaskExecutor.java new file mode 100644 index 000000000..1bffd7ee9 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/TaskExecutor.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task; + +import org.opengauss.agent.entity.TaskDefinition; +import org.opengauss.agent.exception.TaskExecutionException; + +/** + * TaskExecutor + * + * @author: wangchao + * @Date: 2025/4/9 09:32 + * @Description: TaskExecutor + * @since 7.0.0-RC2 + **/ +public interface TaskExecutor { + /** + * initialize + * + * @param taskDefinition taskDefinition + * @throws TaskExecutionException TaskExecutionException + */ + void initialize(TaskDefinition taskDefinition) throws TaskExecutionException; + + /** + * execute + * + * @throws TaskExecutionException TaskExecutionException + */ + void execute() throws TaskExecutionException; + + /** + * cancel + * + * @param taskId taskId + */ + void cancel(Long taskId); + + /** + * hasTaskRunning + * + * @return boolean + */ + boolean hasTaskRunning(); +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/TaskExecutorFactory.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/TaskExecutorFactory.java new file mode 100644 index 000000000..abef7baa2 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/TaskExecutorFactory.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task; + +import io.quarkus.arc.Unremovable; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import org.opengauss.agent.enums.TaskType; +import org.opengauss.agent.exception.TaskExecutionException; +import org.opengauss.agent.service.task.core.DbMetricExecutor; +import org.opengauss.agent.service.task.core.DbPipeExecutor; +import org.opengauss.agent.service.task.core.HostDynamicExecutor; +import org.opengauss.agent.service.task.core.HostStaticExecutor; +import org.opengauss.agent.service.task.core.OsMetricExecutor; +import org.opengauss.agent.service.task.core.OsPipeExecutor; + +import java.util.EnumMap; +import java.util.Map; +import java.util.Objects; + +/** + * TaskExecutorFactory + * + * @author: wangchao + * @Date: 2025/4/9 09:34 + * @Description: TaskExecutorFactory + * @since 7.0.0-RC2 + **/ +@Unremovable +@ApplicationScoped +public class TaskExecutorFactory { + @Inject + HostStaticExecutor hostStaticExecutor; + @Inject + HostDynamicExecutor hostDynamicExecutor; + @Inject + OsMetricExecutor osMetricExecutor; + @Inject + DbMetricExecutor dbMetricExecutor; + @Inject + OsPipeExecutor osPipeExecutor; + @Inject + DbPipeExecutor dbPipeExecutor; + + private final Map executors = new EnumMap<>(TaskType.class); + + /** + * Initialize the task executor factory. + */ + @PostConstruct + public void init() { + executors.put(TaskType.OSHI_FIXED_METRIC, hostStaticExecutor); + executors.put(TaskType.OSHI_DYNAMIC_METRIC, hostDynamicExecutor); + executors.put(TaskType.OS_METRIC, osMetricExecutor); + executors.put(TaskType.DB_METRIC, dbMetricExecutor); + executors.put(TaskType.OS_PIPE, osPipeExecutor); + executors.put(TaskType.DB_PIPE, dbPipeExecutor); + } + + /** + * Get the task executor by task type. + * + * @param taskType task type + * @return task executor + */ + public TaskExecutor getExecutor(TaskType taskType) { + TaskExecutor executor = executors.get(taskType); + if (Objects.isNull(executor)) { + throw new TaskExecutionException("task executor" + taskType + " not found"); + } + return executor; + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/TaskManager.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/TaskManager.java new file mode 100644 index 000000000..0c12fa929 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/TaskManager.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task; + +import io.quarkus.arc.Unremovable; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.agent.entity.TaskDefinition; +import org.opengauss.agent.entity.TaskDefinitionBuilder; +import org.opengauss.agent.entity.TaskExecution; +import org.opengauss.agent.entity.task.AgentTaskVo; +import org.opengauss.agent.enums.TaskType; +import org.opengauss.agent.exception.AgentException; + +import java.time.Instant; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * TaskManager + * + * @author: wangchao + * @Date: 2025/5/21 11:18 + * @since 7.0.0-RC2 + **/ +@Slf4j +@Unremovable +@ApplicationScoped +public class TaskManager { + @Inject + TaskExecutorFactory executorFactory; + + private final ConcurrentMap taskExecMap = new ConcurrentHashMap<>(); + + /** + * startTask + * + * @param taskConfig taskConfig + * @return TaskExecution + */ + public TaskExecution startTask(AgentTaskVo taskConfig) { + TaskDefinition taskDef = TaskDefinitionBuilder.builder(taskConfig); + TaskExecution execution = new TaskExecution(taskDef); + if (taskExecMap.containsKey(taskDef.getTaskId())) { + log.info("task {} is already running", taskDef.getTaskId()); + execution.setStatus("already_running"); + execution.setErrorMessage("task " + taskDef.getTaskId() + " is already running"); + return execution; + } + log.info("task {} is running ", taskDef.getTaskName()); + try { + TaskExecutor executor = executorFactory.getExecutor(taskDef.getTaskType()); + executor.initialize(taskDef); + executor.execute(); + execution.setStatus("running"); + execution.setStartTime(Instant.now()); + taskExecMap.put(taskDef.getTaskId(), execution); + } catch (AgentException e) { + log.error("task {} execute failed", taskDef.getTaskName(), e); + execution.setStatus("no_start"); + execution.setErrorMessage(e.getMessage()); + } + return execution; + } + + /** + * stop task + * + * @param taskId taskId + * @return TaskExecution + */ + public Optional stop(Long taskId) { + if (!taskExecMap.containsKey(taskId)) { + log.warn("task {} is not running", taskId); + return Optional.empty(); + } + TaskExecution execution = taskExecMap.get(taskId); + TaskType taskType = execution.getTaskType(); + if (taskType == null) { + log.warn("task {} type is null", taskId); + return Optional.empty(); + } + TaskExecutor executor = executorFactory.getExecutor(taskType); + if (executor == null) { + log.warn("task {} does not have executor for task type {}", taskId, taskType); + return Optional.empty(); + } + executor.cancel(taskId); + log.info("stop task {}", taskId); + if (!executor.hasTaskRunning()) { + taskExecMap.remove(taskId); + log.info("task {} does not have executor {} running, remove it", taskId, taskType); + } + execution.setStatus("stop"); + execution.setEndTime(Instant.now()); + return Optional.of(execution); + } + + /** + * check task is empty + * + * @return boolean + */ + public boolean isEmpty() { + return taskExecMap.isEmpty(); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/CommonExecutor.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/CommonExecutor.java new file mode 100644 index 000000000..79d49af9e --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/CommonExecutor.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.core; + +import lombok.Getter; + +import org.opengauss.agent.config.AppConfig; +import org.opengauss.agent.entity.TaskDefinition; +import org.opengauss.agent.exception.TaskExecutionException; +import org.opengauss.agent.service.task.group.GroupKey; +import org.opengauss.agent.service.task.group.TaskGroup; +import org.slf4j.Logger; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * CommonExecutor + * + * @author: wangchao + * @Date: 2025/4/9 10:35 + * @Description: CommonExecutor + * @since 7.0.0-RC2 + **/ +public class CommonExecutor { + private final Logger log; + private final AppConfig appConfig; + private final Map taskIdToGroupKey = new ConcurrentHashMap<>(); + @Getter + private final ConcurrentMap targetGroups = new ConcurrentHashMap<>(); + + /** + * CommonExecutor + * + * @param appConfig appConfig + * @param log log + */ + public CommonExecutor(AppConfig appConfig, Logger log) { + this.log = log; + this.appConfig = appConfig; + } + + /** + * Initialize task + * + * @param task task + * @throws TaskExecutionException TaskExecutionException + */ + public void initialize(TaskDefinition task) throws TaskExecutionException { + GroupKey key = new GroupKey(task); + taskIdToGroupKey.put(task.getTaskId(), key); + TaskGroup otelTaskGroup = targetGroups.compute(key, (k, group) -> { + if (group == null) { + TaskGroup newGroup = new TaskGroup(key); + newGroup.addTask(task); + newGroup.createContext(appConfig); + return newGroup; + } else { + synchronized (this) { + if (!group.validateTaskConsistency(task)) { + throw new TaskExecutionException("Task conflicts with existing group configuration"); + } + group.addTask(task); + group.refreshContext(); + } + return group; + } + }); + log.info("Initialized task execute environment {} :tasks:{}", key, otelTaskGroup.getTaskIds()); + } + + /** + * Check if there is a task running + * + * @return boolean + */ + public boolean hasTaskRunning() { + return targetGroups.values().stream().anyMatch(TaskGroup::hasTask); + } + + /** + * Remove task + * + * @param taskId Task ID + */ + public void cancal(Long taskId) { + GroupKey groupKey = taskIdToGroupKey.remove(taskId); + if (groupKey == null) { + log.warn("Task {} not found in the executor", taskId); + return; + } + TaskGroup group = targetGroups.get(groupKey); + if (group == null) { + log.warn("Group for task {} not found in the executor", taskId); + return; + } + synchronized (group) { + group.removeTask(taskId); + if (group.isEmpty()) { + targetGroups.remove(groupKey); + log.info("Removed group {} due to no tasks", groupKey); + } + } + } + + /** + * Stop all tasks + */ + public void stop() { + targetGroups.forEach((target, group) -> { + group.stopPeriodicCollection(); + }); + targetGroups.clear(); + taskIdToGroupKey.clear(); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/DbMetricExecutor.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/DbMetricExecutor.java new file mode 100644 index 000000000..c6ea8c38d --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/DbMetricExecutor.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.core; + +import io.quarkus.runtime.ShutdownEvent; +import io.quarkus.runtime.StartupEvent; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.agent.config.AppConfig; +import org.opengauss.agent.entity.TaskDefinition; +import org.opengauss.agent.entity.task.AgentClusterVo; +import org.opengauss.agent.entity.task.TaskMetricsDefinitionVo; +import org.opengauss.agent.enums.DatabaseType; +import org.opengauss.agent.exception.TaskExecutionException; +import org.opengauss.agent.service.task.TaskExecutor; +import org.opengauss.agent.service.task.group.GroupKey; +import org.opengauss.agent.service.task.group.OtelTaskGroup; +import org.opengauss.agent.utils.RsaUtils; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * DbMetricExecutor + * + * @author: wangchao + * @Date: 2025/4/9 10:35 + * @Description: DbMetricExecutor + * @since 7.0.0-RC2 + **/ +@Slf4j +@ApplicationScoped +public class DbMetricExecutor implements TaskExecutor { + OtelCommonExecutor otelCommonExecutor; + @Inject + AppConfig appConfig; + + /** + * initialize DbMetricExecutor instance when application start + * + * @param event startup event + */ + void onStart(@Observes StartupEvent event) { + otelCommonExecutor = new OtelCommonExecutor(appConfig, log); + } + + @Override + public void initialize(TaskDefinition task) throws TaskExecutionException { + log.info("initialize task: {}", task); + otelCommonExecutor.initialize(task); + } + + @Override + public void execute() throws TaskExecutionException { + ConcurrentMap targetGroups = otelCommonExecutor.getTargetGroups(); + targetGroups.forEach((target, group) -> { + if (!group.hasTask()) { + log.warn("No tasks configured for group {}", target); + return; + } + // start schedule + startGroupTask(target, group); + }); + } + + private void startGroupTask(GroupKey key, OtelTaskGroup group) { + log.info("Starting group task: {}:{}", key, group.getTaskIds()); + group.startGroupTask(new DbMetricsCollectorTask(group, key)); + } + + static class DbMetricsCollectorTask implements Runnable { + private final OtelTaskGroup group; + private final GroupKey key; + + /** + * DbMetricsCollectorTask + * + * @param group OtelTaskGroup + * @param key GroupKey + */ + DbMetricsCollectorTask(OtelTaskGroup group, GroupKey key) { + this.group = group; + this.key = key; + } + + @Override + public void run() { + MeterProviderContext meterProviderContext = group.getMeterProviderContext(); + if (meterProviderContext == null) { + log.warn("MeterProviderContext is null for group {}", key); + return; + } + Map dynamicMetrics = new ConcurrentHashMap<>(); + group.getTasks().forEach((taskId, taskDefinition) -> { + collectSingleTaskMetrics(key, taskId, taskDefinition, dynamicMetrics); + }); + meterProviderContext.refreshDynamicMetrics(dynamicMetrics); + log.info("Successfully collected {} db metrics for group [{}, tasks={}] collectors", dynamicMetrics.size(), + key, group.getTaskIds()); + meterProviderContext.exportMetrics(); + } + + /** + * collect metrics for single task + * + * @param key group key + * @param taskId task id + * @param taskDefinition task definition + * @param dynamicMetrics dynamic metrics + */ + private void collectSingleTaskMetrics(GroupKey key, Long taskId, TaskDefinition taskDefinition, + Map dynamicMetrics) { + AgentClusterVo clusterConfig = taskDefinition.getClusterConfig(); + try (Connection connection = createConnection(clusterConfig);) { + processMetricsDefinition(taskDefinition, connection, dynamicMetrics); + } catch (SQLException e) { + handleDatabaseError(key, taskId, clusterConfig, e); + } catch (Exception ex) { + log.error("Metric collection failed for group {}", key.getDataSendTarget(), ex); + } + } + + /** + * handle metrics definition + * + * @param taskDefinition task definition + * @param connection database connection + * @param dynamicMetrics dynamic metrics + */ + private void processMetricsDefinition(TaskDefinition taskDefinition, Connection connection, + Map dynamicMetrics) { + List list = taskDefinition.getMetricsDefinitionList(); + list.forEach(metric -> { + processMetric(connection, metric, dynamicMetrics); + }); + } + + /** + * execute metric collect sql and collect metrics + * + * @param connection database connection + * @param metricDef metric definition + * @param dynamicMetrics dynamic metrics + */ + private void processMetric(Connection connection, TaskMetricsDefinitionVo metricDef, + Map dynamicMetrics) { + String name = metricDef.getName(); + String cmd = metricDef.getCollectCmd(); + try (PreparedStatement ps = connection.prepareStatement(cmd); ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + dynamicMetrics.put(name, rs.getDouble(metricDef.getFieldName())); + } else { + log.warn("No data queried for metrics: {}", metricDef); + } + } catch (SQLException e) { + handleMetricError(name, e); + } + } + + private void handleMetricError(String name, SQLException e) { + log.error("Metric collection error: Metric: {} Error Code: {} SQL State: {} Message: {}", name, + e.getErrorCode(), e.getSQLState(), e.getMessage()); + } + + private void handleDatabaseError(GroupKey key, Long taskId, AgentClusterVo clusterConfig, SQLException e) { + log.error("Database error occurred: Group: {} Task ID: {} Database: {} URL: {} " + + "Error Code: {} SQL State: {} Message: {}", key, taskId, clusterConfig.getDataBaseType(), + clusterConfig.getUrl(), e.getErrorCode(), e.getSQLState(), e.getMessage()); + } + + /** + * Create a connection to the database. + * + * @param clusterConfig the cluster configuration + * @return the connection + * @throws SQLException if a database access error occurs + */ + private Connection createConnection(AgentClusterVo clusterConfig) throws SQLException { + return DatabaseType.valueOf(clusterConfig.getDataBaseType()) + .getConnection(clusterConfig.getUrl(), clusterConfig.getUsername(), + RsaUtils.decrypt(clusterConfig.getDbPassword())); + } + } + + /** + * Stop the executor. + * + * @param event the shutdown event + */ + void onStop(@Observes ShutdownEvent event) { + otelCommonExecutor.stop(); + DatabaseType.shutdownAll(); + } + + @Override + public boolean hasTaskRunning() { + return otelCommonExecutor.hasTaskRunning(); + } + + /** + * Remove task + * + * @param taskId Task ID + */ + @Override + public void cancel(Long taskId) { + otelCommonExecutor.cancal(taskId); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/DbPipeExecutor.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/DbPipeExecutor.java new file mode 100644 index 000000000..0ec69eb1c --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/DbPipeExecutor.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.core; + +import cn.hutool.core.collection.CollUtil; +import io.quarkus.runtime.StartupEvent; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.agent.config.AppConfig; +import org.opengauss.agent.entity.TaskDefinition; +import org.opengauss.agent.entity.task.AgentClusterVo; +import org.opengauss.agent.entity.task.TaskMetricsDefinitionVo; +import org.opengauss.agent.enums.DatabaseType; +import org.opengauss.agent.exception.TaskExecutionException; +import org.opengauss.agent.service.task.TaskExecutor; +import org.opengauss.agent.service.task.group.GroupKey; +import org.opengauss.agent.service.task.group.TaskGroup; +import org.opengauss.agent.utils.RsaUtils; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentMap; + +/** + * DbPipeExecutor + * + * @author: wangchao + * @Date: 2025/4/9 10:43 + * @Description: SqlPipelineExecutor + * @since 7.0.0-RC2 + **/ +@Slf4j +@ApplicationScoped +public class DbPipeExecutor implements TaskExecutor { + @Inject + AppConfig appConfig; + private CommonExecutor commonExecutor; + + /** + * initialize DbPipeExecutor instance when application start + * + * @param event startup event + */ + void onStart(@Observes StartupEvent event) { + commonExecutor = new CommonExecutor(appConfig, log); + } + + @Override + public void initialize(TaskDefinition task) throws TaskExecutionException { + commonExecutor.initialize(task); + } + + @Override + public void execute() throws TaskExecutionException { + ConcurrentMap targetGroups = commonExecutor.getTargetGroups(); + targetGroups.forEach((groupKey, group) -> { + if (!group.hasTask()) { + log.warn("No tasks configured for group {}", groupKey); + return; + } + // start schedule + group.startGroupTask(new DatabasePipeTask(groupKey, group)); + }); + } + + @Override + public void cancel(Long taskId) { + commonExecutor.cancal(taskId); + } + + @Override + public boolean hasTaskRunning() { + return false; + } + + static class DatabasePipeTask implements Runnable { + private static final String NAN = "NaN"; + + private final GroupKey groupKey; + private final TaskGroup group; + + public DatabasePipeTask(GroupKey groupKey, TaskGroup group) { + this.groupKey = groupKey; + this.group = group; + } + + @Override + public void run() { + group.getTasks().forEach((taskId, taskDefinition) -> { + List> taskPipeData = collectSingleTaskMetrics(taskId, taskDefinition); + pushDataToAgentPipe(taskId, taskPipeData); + log.info("Successfully collected {} db metrics for group [{}, task={}] collectors", taskPipeData.size(), + groupKey, taskId); + }); + } + + private void pushDataToAgentPipe(Long taskId, List> taskPipeData) { + if (CollUtil.isNotEmpty(taskPipeData)) { + group.sendData(taskId, taskPipeData); + } + } + + /** + * collect metrics for single task + * + * @param taskId task id + * @param taskDefinition task definition + * @return metrics data list + */ + private List> collectSingleTaskMetrics(Long taskId, TaskDefinition taskDefinition) { + AgentClusterVo clusterConfig = taskDefinition.getClusterConfig(); + List> dataList = new LinkedList<>(); + try (Connection connection = createConnection(clusterConfig);) { + processMetricsDefinition(taskDefinition, connection, dataList); + } catch (SQLException e) { + handleDatabaseError(taskId, clusterConfig, e); + } catch (Exception ex) { + log.error("Pipe Metric collection failed for group {}", groupKey.getDataSendTarget(), ex); + } + return dataList; + } + + private void handleDatabaseError(Long taskId, AgentClusterVo clusterConfig, SQLException e) { + log.error("Database error occurred: Group: {} Task ID: {} Database: {} URL: {} " + + "Error Code: {} SQL State: {} Message: {}", groupKey, taskId, clusterConfig.getDataBaseType(), + clusterConfig.getUrl(), e.getErrorCode(), e.getSQLState(), e.getMessage()); + } + + /** + * handle metrics definition + * + * @param taskDefinition task definition + * @param connection database connection + * @param dataList dataList + */ + private void processMetricsDefinition(TaskDefinition taskDefinition, Connection connection, + List> dataList) { + List list = taskDefinition.getMetricsDefinitionList(); + String execQuerySql = taskDefinition.getOperateObj(); + try (PreparedStatement ps = connection.prepareStatement(execQuerySql); ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + Map rowData = createRowData(list, rs); + dataList.add(rowData); + } + } catch (SQLException e) { + handleMetricError(taskDefinition.getTaskName(), e); + } + } + + private Map createRowData(List metricsDefinitions, ResultSet rs) + throws SQLException { + Map rowData = new HashMap<>(); + for (TaskMetricsDefinitionVo metricDef : metricsDefinitions) { + Object value = getValueFromResultSet(metricDef, rs); + rowData.put(metricDef.getName(), Optional.ofNullable(value).orElse(NAN)); + } + return rowData; + } + + private Object getValueFromResultSet(TaskMetricsDefinitionVo metricDef, ResultSet rs) { + String fieldName = metricDef.getFieldName(); + String dataType = Optional.ofNullable(metricDef.getDataType()).orElse("STRING").toUpperCase(Locale.ROOT); + Object result = null; + try { + result = switch (dataType) { + case "INT" -> rs.getInt(fieldName); + case "LONG" -> rs.getLong(fieldName); + case "DOUBLE" -> rs.getDouble(fieldName); + case "STRING" -> rs.getString(fieldName); + default -> rs.getObject(fieldName); + }; + } catch (SQLException e) { + log.error("Error retrieving value for task.field -> {}.{} :{}", metricDef.getName(), fieldName, + e.getMessage()); + } + return result; + } + + private void handleMetricError(String name, SQLException e) { + log.error("Pipe Metric collection error: task: {} Error Code: {} SQL State: {} Message: {}", name, + e.getErrorCode(), e.getSQLState(), e.getMessage()); + } + + /** + * Create a connection to the database. + * + * @param clusterConfig the cluster configuration + * @return the connection + * @throws SQLException if a database access error occurs + */ + private Connection createConnection(AgentClusterVo clusterConfig) throws SQLException { + return DatabaseType.valueOf(clusterConfig.getDataBaseType()) + .getConnection(clusterConfig.getUrl(), clusterConfig.getUsername(), + RsaUtils.decrypt(clusterConfig.getDbPassword())); + } + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/HostDynamicExecutor.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/HostDynamicExecutor.java new file mode 100644 index 000000000..c917a8a05 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/HostDynamicExecutor.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.core; + +import io.quarkus.runtime.ShutdownEvent; +import io.quarkus.runtime.StartupEvent; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.agent.config.AppConfig; +import org.opengauss.agent.entity.TaskDefinition; +import org.opengauss.agent.exception.AgentException; +import org.opengauss.agent.exception.TaskExecutionException; +import org.opengauss.agent.service.task.TaskExecutor; +import org.opengauss.agent.service.task.group.GroupKey; +import org.opengauss.agent.service.task.group.OtelTaskGroup; +import org.opengauss.agent.vo.MultiValueMetric; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +/** + * HostDynamicExecutor + * + * @author: wangchao + * @Date: 2025/4/9 10:35 + * @Description: HostStaticCollector + * @since 7.0.0-RC2 + **/ +@Slf4j +@ApplicationScoped +public class HostDynamicExecutor implements TaskExecutor { + @Inject + AppConfig appConfig; + + private OtelCommonExecutor otelCommonExecutor; + private final OshiServerCollector oshiServerCollector = new OshiServerCollector(); + + /** + * start + * + * @param event startup event + */ + void onStart(@Observes StartupEvent event) { + otelCommonExecutor = new OtelCommonExecutor(appConfig, log); + } + + @Override + public void initialize(TaskDefinition task) throws TaskExecutionException { + otelCommonExecutor.initialize(task); + } + + @Override + public void execute() throws TaskExecutionException { + ConcurrentMap targetGroups = otelCommonExecutor.getTargetGroups(); + targetGroups.forEach((target, group) -> { + if (!group.hasTask()) { + log.warn("No tasks configured for group {}", target); + return; + } + startGroupTask(target, group); + }); + } + + private void startGroupTask(GroupKey key, OtelTaskGroup group) { + log.info("Starting group task: {}:{}", key, group.getTaskIds()); + group.startGroupTask(() -> collectMetricsForGroup(group, key)); + } + + private void collectMetricsForGroup(OtelTaskGroup group, GroupKey key) { + try { + Set taskSet = group.getTaskIds(); + MeterProviderContext meterProviderContext = group.getMeterProviderContext(); + // 实际收集逻辑 + if (meterProviderContext != null) { + List collectors = group.getMergedCollectorList(); + Map dynamicMetrics = oshiServerCollector.dynamicCollectMap(collectors); + List multiMetrics = oshiServerCollector.multiDynamicCollect(collectors); + log.info("Successfully collected {} host metrics and {} host multi-metrics for group [{}, tasks={}] " + + "collectors ->{} ", dynamicMetrics.size(), multiMetrics.size(), key, taskSet, collectors.size()); + meterProviderContext.refreshDynamicMetrics(dynamicMetrics); + meterProviderContext.refreshMultiDynamicMetrics(multiMetrics, taskSet); + meterProviderContext.exportMetrics(); + } + } catch (AgentException ex) { + log.error("Metric collection failed for group {}", key.getDataSendTarget(), ex); + } + } + + /** + * stop + * + * @param event shutdown event + */ + void onStop(@Observes ShutdownEvent event) { + otelCommonExecutor.stop(); + } + + @Override + public boolean hasTaskRunning() { + return otelCommonExecutor.hasTaskRunning(); + } + + /** + * Remove task + * + * @param taskId Task ID + */ + @Override + public void cancel(Long taskId) { + otelCommonExecutor.cancal(taskId); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/HostStaticExecutor.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/HostStaticExecutor.java new file mode 100644 index 000000000..1c5fc09d6 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/HostStaticExecutor.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.core; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import org.opengauss.agent.client.HostFixedMetricsClient; +import org.opengauss.agent.client.ServerClientFactory; +import org.opengauss.agent.entity.HostBaseInfo; +import org.opengauss.agent.entity.TaskDefinition; +import org.opengauss.agent.exception.TaskExecutionException; +import org.opengauss.agent.service.task.TaskExecutor; + +/** + * HostStaticExecutor + * + * @author: wangchao + * @Date: 2025/4/9 10:35 + * @Description: HostStaticCollector + * @since 7.0.0-RC2 + **/ +@ApplicationScoped +public class HostStaticExecutor implements TaskExecutor { + HostFixedMetricsClient hostFixedMetricsClient; + @Inject + ServerClientFactory clientFactory; + + @Override + public void initialize(TaskDefinition taskDefinition) throws TaskExecutionException { + hostFixedMetricsClient = clientFactory.createHostFixedMetricsClient(); + } + + @Override + public void execute() throws TaskExecutionException { + HostBaseInfo hostBaseInfo = OshiServerCollector.fixedCollect(); + hostFixedMetricsClient.sendHostBaseInfo("", hostBaseInfo); + } + + @Override + public void cancel(Long taskId) { + } + + @Override + public boolean hasTaskRunning() { + return false; + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/MeterProviderContext.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/MeterProviderContext.java new file mode 100644 index 000000000..f0964f61e --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/MeterProviderContext.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.core; + +import cn.hutool.core.lang.Pair; +import cn.hutool.core.util.StrUtil; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.ObservableDoubleGauge; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.agent.entity.task.TaskMetricsDefinitionVo; +import org.opengauss.agent.service.otel.HttpMetricExporter; +import org.opengauss.agent.vo.MetricData; +import org.opengauss.agent.vo.MetricKey; +import org.opengauss.agent.vo.MultiValueMetric; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * MeterProviderContext + * + * @author: wangchao + * @Date: 2025/5/16 16:43 + * @since 7.0.0-RC2 + **/ +@Slf4j +public class MeterProviderContext { + @Getter + boolean isRegister = false; + final OpenTelemetrySdk sdk; + final SdkMeterProvider meterProvider; + final HttpMetricExporter exporter; + final Meter meter; + ConcurrentMap gaugesMetricDataMap = new ConcurrentHashMap<>(); + ConcurrentMap gaugesMap = new ConcurrentHashMap<>(); + List metricsDefinitions; + List collectors; + Map> taskMetricKeys = new ConcurrentHashMap<>(); + + /** + * Constructor for MeterProviderContext + * + * @param sdk OpenTelemetrySdk + * @param meterProvider meter provider + * @param exporter exporter + * @param meter meter + */ + public MeterProviderContext(OpenTelemetrySdk sdk, SdkMeterProvider meterProvider, HttpMetricExporter exporter, + Meter meter) { + this.sdk = sdk; + this.meterProvider = meterProvider; + this.exporter = exporter; + this.meter = meter; + } + + /** + * Initialize the MeterProviderContext + * + * @param metrics metrics + * @param collectors collectors + * @param taskMetricKeys taskMetricKeys + */ + public void initialize(List metrics, List collectors, + Map> taskMetricKeys) { + this.metricsDefinitions = metrics; + this.collectors = collectors; + this.taskMetricKeys = taskMetricKeys; + registerMetrics(); + } + + /** + * Register metrics + */ + void registerMetrics() { + isRegister = true; + for (TaskMetricsDefinitionVo metric : metricsDefinitions) { + String prop = metric.getProp(); + if (StrUtil.isEmpty(prop)) { + MetricKey metricKey = MetricKey.of(metric.getName()); + gaugesMetricDataMap.put(metricKey, new MetricData()); + gaugesMap.put(metricKey, meter.gaugeBuilder(metric.getName()) + .setUnit(Objects.isNull(metric.getUnit()) ? "" : metric.getUnit()) + .setDescription(Objects.isNull(metric.getDescription()) ? "" : metric.getDescription()) + .buildWithCallback(measurement -> { + MetricData data = gaugesMetricDataMap.get(metricKey); + measurement.record(data.getValue().get()); + })); + } + } + } + + /** + * Refresh dynamic metrics value + * + * @param dynamicMetrics dynamic metrics + */ + public void refreshDynamicMetrics(Map dynamicMetrics) { + if (dynamicMetrics == null || dynamicMetrics.isEmpty()) { + return; + } + dynamicMetrics.forEach((metricName, value) -> { + MetricKey metricKey = MetricKey.of(metricName); + MetricData metricData = gaugesMetricDataMap.get(metricKey); + if (metricData != null) { + metricData.getValue().set(value); + } else { + log.warn("============== Metric {} not found in gaugesMetricDataMap ===============", metricName); + } + }); + } + + /** + * Refresh multi value dynamic metrics + * + * @param multiDynamicMetrics multi dynamic metrics + * @param activeTaskIds active task ids + */ + public void refreshMultiDynamicMetrics(List multiDynamicMetrics, Set activeTaskIds) { + // 清理无效任务指标 + cleanupStaleMetrics(activeTaskIds); + // 更新当前指标 + Map metricsDefinitionVoMap = metricsDefinitions.stream() + .collect(Collectors.toMap(TaskMetricsDefinitionVo::getName, Function.identity())); + multiDynamicMetrics.forEach(metricValue -> { + updateOtelMetrics(metricsDefinitionVoMap.get(metricValue.getName()), metricValue.getPropValue(), + metricValue.getPropValue().getKey()); + }); + } + + /** + * Update otel metrics + * + * @param metric metric + * @param doublePair value + * @param propValue property value + */ + void updateOtelMetrics(TaskMetricsDefinitionVo metric, Pair doublePair, String propValue) { + // 仅更新数值,不重建指标 + MetricKey key = MetricKey.of(metric.getName(), propValue); + // 根据属性值动态创建或者更新指标实例 + MetricData metricData = gaugesMetricDataMap.computeIfAbsent(key, k -> { + // 动态注册新属性实例 + ObservableDoubleGauge gauge = meter.gaugeBuilder(metric.getName()) + .setUnit(metric.getUnit()) + .setDescription(metric.getDescription()) + .buildWithCallback(measurement -> { + MetricData data = gaugesMetricDataMap.get(key); + measurement.record(data.getValue().get(), data.getAttributes()); + }); + gaugesMap.put(key, gauge); + return new MetricData(); + }); + // 更新数值和属性 + metricData.getValue().set(doublePair.getValue()); + if (StrUtil.isNotEmpty(propValue)) { + metricData.setAttributes(Attributes.of(AttributeKey.stringKey(metric.getProp()), propValue)); + } + } + + /** + * Cleanup stale metrics + * + * @param activeTaskIds the active task ids + */ + void cleanupStaleMetrics(Set activeTaskIds) { + Set staleTasks = new HashSet<>(taskMetricKeys.keySet()); + staleTasks.removeAll(activeTaskIds); + staleTasks.forEach(taskId -> { + taskMetricKeys.get(taskId).forEach(gaugesMetricDataMap::remove); + taskMetricKeys.remove(taskId); + }); + } + + /** + * Shutdown the MeterProviderContext + */ + public void shutdown() { + gaugesMap.forEach((metric, metricGauges) -> { + metricGauges.close(); + }); + gaugesMetricDataMap.clear(); + gaugesMap.clear(); + if (exporter != null) { + exporter.shutdown().join(100, TimeUnit.MILLISECONDS); + } + if (meterProvider != null) { + meterProvider.shutdown().join(100, TimeUnit.MILLISECONDS); + } + if (sdk != null) { + sdk.shutdown().join(100, TimeUnit.MILLISECONDS); + } + collectors.clear(); + metricsDefinitions.clear(); + taskMetricKeys.clear(); + isRegister = false; + } + + /** + * Export metrics + */ + public void exportMetrics() { + exporter.flush(); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/OsMetricExecutor.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/OsMetricExecutor.java new file mode 100644 index 000000000..079d48117 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/OsMetricExecutor.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.core; + +import cn.hutool.core.util.StrUtil; +import io.quarkus.runtime.ShutdownEvent; +import io.quarkus.runtime.StartupEvent; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.agent.config.AppConfig; +import org.opengauss.agent.entity.OsCmdResult; +import org.opengauss.agent.entity.TaskDefinition; +import org.opengauss.agent.entity.task.TaskMetricsDefinitionVo; +import org.opengauss.agent.exception.TaskExecutionException; +import org.opengauss.agent.service.task.TaskExecutor; +import org.opengauss.agent.service.task.group.GroupKey; +import org.opengauss.agent.service.task.group.OtelTaskGroup; +import org.opengauss.agent.utils.MathUtils; +import org.opengauss.agent.utils.OsCommandUtils; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * OsMetricExecutor + * + * @author: wangchao + * @Date: 2025/4/9 10:35 + * @Description: OsMetricExecutor + * @since 7.0.0-RC2 + **/ +@Slf4j +@ApplicationScoped +public class OsMetricExecutor implements TaskExecutor { + OtelCommonExecutor otelCommonExecutor; + @Inject + AppConfig appConfig; + + /** + * initialize OsMetricExecutor instance when application start + * + * @param event startup event + */ + void onStart(@Observes StartupEvent event) { + otelCommonExecutor = new OtelCommonExecutor(appConfig, log); + log.info("initialize os metric task executor : {}", appConfig); + } + + @Override + public void initialize(TaskDefinition task) throws TaskExecutionException { + log.info("initialize os metric task: {}", task); + otelCommonExecutor.initialize(task); + } + + @Override + public void execute() throws TaskExecutionException { + ConcurrentMap targetGroups = otelCommonExecutor.getTargetGroups(); + targetGroups.forEach((target, group) -> { + if (!group.hasTask()) { + log.warn("No os metric tasks configured for group {}", target); + return; + } + // start schedule + startGroupTaskOfOsMetricExecutor(target, group); + }); + } + + private void startGroupTaskOfOsMetricExecutor(GroupKey key, OtelTaskGroup group) { + log.info("Starting group task: {}:{}", key, group.getTaskIds()); + group.startGroupTask(new OsMetricsCollectorTask(group)); + } + + @Override + public void cancel(Long taskId) { + otelCommonExecutor.cancal(taskId); + } + + /** + * Stop the executor. + * + * @param event the shutdown event + */ + void onStop(@Observes ShutdownEvent event) { + otelCommonExecutor.stop(); + } + + @Override + public boolean hasTaskRunning() { + return otelCommonExecutor.hasTaskRunning(); + } + + static class OsMetricsCollectorTask implements Runnable { + private final GroupKey groupKey; + private final OtelTaskGroup group; + + public OsMetricsCollectorTask(OtelTaskGroup group) { + this.groupKey = group.getGroupKey(); + this.group = group; + } + + @Override + public void run() { + MeterProviderContext meterProviderContext = group.getMeterProviderContext(); + if (meterProviderContext == null) { + log.warn("MeterProviderContext is null for group {}", groupKey); + return; + } + Map dynamicMetrics = new ConcurrentHashMap<>(); + group.getTasks().forEach((taskId, taskDefinition) -> { + collectTaskMetrics(taskDefinition, dynamicMetrics); + }); + meterProviderContext.refreshDynamicMetrics(dynamicMetrics); + log.info("Successfully collected {} os metrics for group [{}, tasks={}] collectors", dynamicMetrics.size(), + groupKey, group.getTaskIds()); + meterProviderContext.exportMetrics(); + } + + private void collectTaskMetrics(TaskDefinition taskDefinition, Map dynamicMetrics) { + if (StrUtil.isNotEmpty(taskDefinition.getOperateObj())) { + log.warn("not support os task collect {},it must be metric collect {} . task={}", + taskDefinition.getTaskName(), taskDefinition.getOperateObj(), taskDefinition.getTaskId()); + } + List metricsDefinitionList = taskDefinition.getMetricsDefinitionList(); + for (TaskMetricsDefinitionVo definitionVo : metricsDefinitionList) { + OsCmdResult res = OsCommandUtils.execute(definitionVo.getCollectCmd()); + if (res.isSuccess()) { + dynamicMetrics.put(definitionVo.getName(), MathUtils.doubleValueOf(res.output())); + } else { + log.error("Failed to collect os metric {} {} for task {}: {}", definitionVo.getName(), + definitionVo.getCollectCmd(), taskDefinition.getTaskId(), res.output()); + } + } + } + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/OsPipeExecutor.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/OsPipeExecutor.java new file mode 100644 index 000000000..4ae7316d8 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/OsPipeExecutor.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.core; + +import jakarta.enterprise.context.ApplicationScoped; + +import org.opengauss.agent.entity.TaskDefinition; +import org.opengauss.agent.exception.TaskExecutionException; +import org.opengauss.agent.service.task.TaskExecutor; + +/** + * OsPipeExecutor + * + * @author: wangchao + * @Date: 2025/4/9 10:44 + * @Description: OsPipeExecutor + * @since 7.0.0-RC2 + **/ +@ApplicationScoped +public class OsPipeExecutor implements TaskExecutor { + @Override + public void initialize(TaskDefinition taskDefinition) throws TaskExecutionException { + } + + @Override + public void execute() throws TaskExecutionException { + } + + @Override + public void cancel(Long taskId) { + } + + @Override + public boolean hasTaskRunning() { + return false; + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/OshiServerCollector.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/OshiServerCollector.java new file mode 100644 index 000000000..6b17b632f --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/OshiServerCollector.java @@ -0,0 +1,367 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.core; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Pair; +import lombok.extern.slf4j.Slf4j; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HWDiskStore; +import oshi.hardware.NetworkIF; +import oshi.software.os.OSFileStore; +import oshi.software.os.OperatingSystem; +import oshi.util.Util; + +import org.opengauss.agent.entity.HostBaseInfo; +import org.opengauss.agent.utils.MathUtils; +import org.opengauss.agent.vo.MultiValueMetric; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * OshiServerCollector + * + * @author: wangchao + * @Date: 2025/4/8 17:04 + * @Description: OshiServerCollector + * @since 7.0.0-RC2 + **/ +@Slf4j +public class OshiServerCollector { + private static final SystemInfo SYSTEM_INFO = new SystemInfo(); + private static final CentralProcessor CPU = SYSTEM_INFO.getHardware().getProcessor(); + private static final OperatingSystem OS = SYSTEM_INFO.getOperatingSystem(); + private static final GlobalMemory MEMORY = SYSTEM_INFO.getHardware().getMemory(); + private static final List DISKS = SYSTEM_INFO.getHardware().getDiskStores(); + private static final List FILE_STORES = SYSTEM_INFO.getOperatingSystem() + .getFileSystem() + .getFileStores(); + private static final List NETWORK_IFS = SYSTEM_INFO.getHardware().getNetworkIFs(); + + private final List collectors = Arrays.asList(new MemoryTotalCollector(), new CpuUsageCollector(), + new MemoryAvailableCollector(), new MemoryUsageCollector()); + private final List multiCollectors = Arrays.asList(new NetMultiDynamicCollector(), + new DiskMultiDynamicCollector(), new FileStoreMultiDynamicCollector()); + + /** + * collect host base info + * + * @return HostBaseInfo + */ + public static HostBaseInfo fixedCollect() { + // 主机名 + String hostName = OS.getNetworkParams().getHostName(); + // CPU 信息 + String cpuModel = CPU.getProcessorIdentifier().getName(); + long cpuFreq = CPU.getProcessorIdentifier().getVendorFreq(); + int physicalCores = CPU.getPhysicalProcessorCount(); + int logicalCores = CPU.getLogicalProcessorCount(); + String architecture = CPU.getProcessorIdentifier().getMicroarchitecture(); + // 操作系统信息 + String osName = OS.getFamily(); + String osVersion = OS.getVersionInfo().getVersion(); + String osBuild = OS.getVersionInfo().getBuildNumber(); + return HostBaseInfo.builder() + .hostName(hostName) + .cpuModel(cpuModel) + .cpuFreq(cpuFreq) + .cpuArchitecture(architecture) + .physicalCores(physicalCores) + .logicalCores(logicalCores) + .osName(osName) + .osVersion(osVersion) + .osBuild(osBuild) + .build(); + } + + /** + * collect dynamic info + * + * @param collectorList collector list + * @return dynamic info + */ + public Map dynamicCollectMap(List collectorList) { + Map result = new HashMap<>(); + collectors.stream().filter(collector -> collectorList.contains(collector.getName())).forEach(collector -> { + result.put(collector.getName(), collector.collect().getValue()); + }); + return result; + } + + /** + * multi dynamic collect + * + * @param collectorList collector list + * @return multi value metric list + */ + public List multiDynamicCollect(List collectorList) { + List result = new ArrayList<>(); + multiCollectors.stream().filter(collector -> collector.containsAny(collectorList)).forEach(collector -> { + result.addAll(collector.collect()); + }); + return result; + } + + /** + * dynamic collect interface + */ + private interface DynamicCollector { + /** + * dynamic collect + * + * @return result + */ + Pair collect(); + + /** + * collector metric name + * + * @return name + */ + String getName(); + } + + /** + * multi dynamic collect interface + */ + private interface MultiDynamicCollector { + /** + * collect data + * + * @return result + */ + List collect(); + + /** + * check target names contains any metric name in collector + * + * @param targetNames target names + * @return true if contains any metric name + */ + boolean containsAny(List targetNames); + } + + /** + * Net dynamic collector + */ + private static class NetMultiDynamicCollector implements MultiDynamicCollector { + private static final int INTERVAL_MILLIS = 1000; + private static final List NAMES = List.of("system.net.bytes.received", "system.net.bytes.sent"); + + @Override + public List collect() { + // 记录初始值 + long[] initialRecv = new long[NETWORK_IFS.size()]; + long[] initialSent = new long[NETWORK_IFS.size()]; + for (int i = 0; i < NETWORK_IFS.size(); i++) { + NetworkIF netIf = NETWORK_IFS.get(i); + if (!netIf.isConnectorPresent() || netIf.getName().startsWith("lo")) { + continue; + } + netIf.updateAttributes(); + initialRecv[i] = netIf.getBytesRecv(); + initialSent[i] = netIf.getBytesSent(); + } + // 等待采样间隔 + Util.sleep(INTERVAL_MILLIS); + List list = new ArrayList<>(); + // 计算流量差值 + for (int i = 0; i < NETWORK_IFS.size(); i++) { + NetworkIF netIf = NETWORK_IFS.get(i); + if (!netIf.isConnectorPresent() || netIf.getName().startsWith("lo")) { + continue; + } + netIf.updateAttributes(); + double recv = MathUtils.divide1024(netIf.getBytesRecv() - initialRecv[i]); + double sent = MathUtils.divide1024(netIf.getBytesSent() - initialSent[i]); + list.add(MultiValueMetric.of(getName(0), netIf.getName(), recv)); + list.add(MultiValueMetric.of(getName(1), netIf.getName(), sent)); + } + return list; + } + + private String getName(int index) { + return NAMES.get(index); + } + + @Override + public boolean containsAny(List targetNames) { + if (CollUtil.isEmpty(targetNames)) { + return false; + } + return !Collections.disjoint(NAMES, targetNames); + } + } + + /** + * Disk dynamic collector + */ + private static class DiskMultiDynamicCollector implements MultiDynamicCollector { + private static final List NAMES = List.of("system.disk.bytes.read", "system.disk.bytes.write", + "system.disk.total"); + + @Override + public List collect() { + List list = new ArrayList<>(); + DISKS.forEach(disk -> { + String diskName = disk.getName(); + list.add(MultiValueMetric.of(getName(0), diskName, disk.getWriteBytes() / 1e9)); + list.add(MultiValueMetric.of(getName(1), diskName, disk.getReadBytes() / 1e9)); + list.add(MultiValueMetric.of(getName(2), diskName, disk.getSize() / 1e9)); + }); + return list; + } + + private String getName(int index) { + return NAMES.get(index); + } + + @Override + public boolean containsAny(List targetNames) { + if (CollUtil.isEmpty(targetNames)) { + return false; + } + return !Collections.disjoint(NAMES, targetNames); + } + } + + /** + * FileStore dynamic collector + */ + private static class FileStoreMultiDynamicCollector implements MultiDynamicCollector { + private static final List NAMES = List.of("system.disk.fs.total", "system.disk.fs.used", + "system.disk.fs.free"); + + @Override + public List collect() { + List list = new ArrayList<>(); + FILE_STORES.forEach(fs -> { + String mountPoint = fs.getMount().replace("\\", ""); + list.add(MultiValueMetric.of(getName(0), mountPoint, fs.getTotalSpace() / 1e9)); + list.add(MultiValueMetric.of(getName(1), mountPoint, fs.getUsableSpace() / 1e9)); + list.add(MultiValueMetric.of(getName(2), mountPoint, fs.getFreeSpace() / 1e9)); + }); + return list; + } + + private String getName(int index) { + return NAMES.get(index); + } + + @Override + public boolean containsAny(List targetNames) { + if (CollUtil.isEmpty(targetNames)) { + return false; + } + return !Collections.disjoint(NAMES, targetNames); + } + } + + /** + * CpuUsageCollector + */ + private static class CpuUsageCollector implements DynamicCollector { + private static final int INTERVAL_MILLIS = 1000; + + @Override + public Pair collect() { + // 第一次采样(必须初始化) + long[][] prevTicks = CPU.getProcessorCpuLoadTicks(); + try { + Thread.sleep(INTERVAL_MILLIS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + // 第二次采样并计算 + double[] load = CPU.getProcessorCpuLoadBetweenTicks(prevTicks); + double totalUsage = calculateTaskManagerStyle(load); + return new Pair(getName(), totalUsage); + } + + /** + * Calculate the total utilization rate according to the logic of the task manager + * + * @param perCoreLoad Utilization array for each logical core (0.0-1.0) + * @return Total utilization percentage + */ + private static double calculateTaskManagerStyle(double[] perCoreLoad) { + double sum = 0.0; + for (double core : perCoreLoad) { + sum += core; + } + // Key formula: Total utilization rate=Sum of utilization rates of each core/Number of logical cores * 100 + return (sum / Runtime.getRuntime().availableProcessors()) * 100; + } + + @Override + public String getName() { + return "system.cpu.usage"; + } + } + + /** + * MemoryTotalCollector + */ + private static class MemoryTotalCollector implements DynamicCollector { + @Override + public Pair collect() { + return new Pair<>(getName(), MathUtils.convertBytesToGigabytes(MEMORY.getTotal())); + } + + @Override + public String getName() { + return "system.memory.total"; + } + } + + /** + * MemoryAvailableCollector + */ + private static class MemoryAvailableCollector implements DynamicCollector { + @Override + public Pair collect() { + return new Pair(getName(), MathUtils.convertBytesToGigabytes(MEMORY.getAvailable())); + } + + @Override + public String getName() { + return "system.memory.available"; + } + } + + /** + * MemoryUsageCollector + */ + private static class MemoryUsageCollector implements DynamicCollector { + @Override + public Pair collect() { + return new Pair(getName(), + MathUtils.calculateUtilizationPercentage(MEMORY.getAvailable(), MEMORY.getTotal())); + } + + @Override + public String getName() { + return "system.memory.usage"; + } + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/OtelCommonExecutor.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/OtelCommonExecutor.java new file mode 100644 index 000000000..6b6198e65 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/OtelCommonExecutor.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.core; + +import lombok.Getter; + +import org.opengauss.agent.config.AppConfig; +import org.opengauss.agent.entity.TaskDefinition; +import org.opengauss.agent.exception.TaskExecutionException; +import org.opengauss.agent.service.task.group.GroupKey; +import org.opengauss.agent.service.task.group.OtelTaskGroup; +import org.slf4j.Logger; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * OtelCommonExecutor + * + * @author: wangchao + * @Date: 2025/4/9 10:35 + * @Description: OtelCommonExecutor + * @since 7.0.0-RC2 + **/ +public class OtelCommonExecutor { + private final Logger log; + private final Map taskIdToGroupKey = new ConcurrentHashMap<>(); + @Getter + private final ConcurrentMap targetGroups = new ConcurrentHashMap<>(); + private final AppConfig appConfig; + + /** + * Constructor + * + * @param appConfig AppConfig + * @param log Logger + */ + public OtelCommonExecutor(AppConfig appConfig, Logger log) { + this.log = log; + this.appConfig = appConfig; + } + + /** + * Initialize + * + * @param task TaskDefinition + * @throws TaskExecutionException TaskExecutionException + */ + public void initialize(TaskDefinition task) throws TaskExecutionException { + GroupKey key = new GroupKey(task); + taskIdToGroupKey.put(task.getTaskId(), key); + OtelTaskGroup otelTaskGroup = targetGroups.compute(key, (k, group) -> { + if (group == null) { + OtelTaskGroup newGroup = new OtelTaskGroup(key); + newGroup.addTask(task); + newGroup.createContext(appConfig); + return newGroup; + } else { + synchronized (this) { + if (!group.validateTaskConsistency(task)) { + throw new TaskExecutionException("Task conflicts with existing group configuration"); + } + group.addTask(task); + // re-create context + group.recreateContext(appConfig); + } + return group; + } + }); + log.info("Initialized task execute environment {} :tasks:{}", key, otelTaskGroup.getTaskIds()); + } + + /** + * Has task running + * + * @return boolean + */ + public boolean hasTaskRunning() { + return targetGroups.values().stream().anyMatch(OtelTaskGroup::hasTask); + } + + /** + * Remove task + * + * @param taskId Task ID + */ + public void cancal(Long taskId) { + GroupKey groupKey = taskIdToGroupKey.remove(taskId); + if (groupKey == null) { + log.warn("Task {} not found in the executor", taskId); + return; + } + OtelTaskGroup group = targetGroups.get(groupKey); + if (group == null) { + log.warn("Group for task {} not found in the executor", taskId); + return; + } + synchronized (group) { + group.removeTask(taskId); + if (group.isEmpty()) { + group.getMeterProviderContext().shutdown(); + targetGroups.remove(groupKey); + log.info("Removed group {} due to no tasks", groupKey); + } else { + try { + group.recreateContext(appConfig); // re-create the context with the remaining tasks + log.info("Recreated context for group {} after task removal", groupKey); + } catch (TaskExecutionException e) { + log.error("Failed to update context after task removal", e); + } + } + } + } + + /** + * Stop + */ + public void stop() { + targetGroups.forEach((target, group) -> { + group.stopPeriodicCollection(); + }); + targetGroups.clear(); + taskIdToGroupKey.clear(); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/TaskExecutionRecordService.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/TaskExecutionRecordService.java new file mode 100644 index 000000000..68831c8fc --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/core/TaskExecutionRecordService.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.core; + +import cn.hutool.core.io.FileUtil; +import io.quarkus.arc.Unremovable; +import jakarta.enterprise.context.ApplicationScoped; + +import org.opengauss.agent.entity.TaskExecution; + +import java.io.File; +import java.util.List; + +/** + * TaskExecutionException + * + * @author: wangchao + * @Date: 2025/4/9 17:26 + * @Description: TaskExecutionRecordService + * @since 7.0.0-RC2 + **/ +@Unremovable +@ApplicationScoped +public class TaskExecutionRecordService { + private static final File HISTORY = new File("history.txt"); + + /** + * save record to history.txt file + * + * @param record record + */ + public void save(String record) { + FileUtil.appendUtf8Lines(List.of(record), HISTORY); + } + + /** + * save record to history.txt file + * + * @param record record + */ + public void save(TaskExecution record) { + FileUtil.appendUtf8Lines(List.of(record), HISTORY); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/group/GroupKey.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/group/GroupKey.java new file mode 100644 index 000000000..0c9d3911c --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/group/GroupKey.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.group; + +import lombok.EqualsAndHashCode; +import lombok.Getter; + +import org.opengauss.agent.entity.TaskDefinition; +import org.opengauss.agent.enums.StoragePolicy; + +/** + * GroupKey + * + * @author: wangchao + * @Date: 2025/5/16 16:40 + * @since 7.0.0-RC2 + **/ +@Getter +@EqualsAndHashCode +public class GroupKey { + private final String dataSendTarget; + private final long period; + private final StoragePolicy storagePolicy; + + /** + * Constructor + * + * @param task task definition + */ + public GroupKey(TaskDefinition task) { + this.dataSendTarget = task.getDataSendTarget(); + this.period = task.getPeriod(); + this.storagePolicy = task.getStoragePolicy(); + } + + @Override + public String toString() { + return "groupKey=[" + dataSendTarget + "," + period + "," + storagePolicy + "]"; + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/group/OtelTaskGroup.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/group/OtelTaskGroup.java new file mode 100644 index 000000000..ea3545998 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/group/OtelTaskGroup.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.group; + +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import lombok.extern.slf4j.Slf4j; + +import org.apache.http.client.utils.URIBuilder; +import org.opengauss.agent.config.AppConfig; +import org.opengauss.agent.exception.TaskExecutionException; +import org.opengauss.agent.service.otel.HttpMetricExporter; +import org.opengauss.agent.service.task.core.MeterProviderContext; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * OtelTaskGroup + * + * @author: wangchao + * @Date: 2025/5/16 16:44 + * @since 7.0.0-RC2 + **/ +@Slf4j +public class OtelTaskGroup extends TaskGroup { + /** + * PARAM_TASK instrumentation scope name + */ + protected static final String INSTRUMENTATION_SCOPE_NAME = "datakit.agent.metrics"; + + volatile MeterProviderContext meterProviderContext; + + /** + * Constructor for OtelTaskGroup + * + * @param key group key + */ + public OtelTaskGroup(GroupKey key) { + super(key); + } + + /** + * getMeterProviderContext + * + * @return MeterProviderContext + */ + public MeterProviderContext getMeterProviderContext() { + return meterProviderContext; + } + + /** + * createContext + * + * @param appConfig AppConfig + * @throws TaskExecutionException TaskExecutionException + */ + public void createContext(AppConfig appConfig) throws TaskExecutionException { + try { + if (meterProviderContext != null && meterProviderContext.isRegister()) { + return; + } + String httpTarget = new URIBuilder(appConfig.getAppServerUrl()).setPath(getGroupKey().getDataSendTarget()) + .build() + .toString(); + Map params = new HashMap<>(); + params.put(PARAM_TASK, new ArrayList<>(getTasks().keySet())); + params.put(PARAM_AGENT, appConfig.getAgentId()); + log.info("recreateContext httpTarget={},params={}", httpTarget, params); + HttpMetricExporter exporter = HttpMetricExporter.createWithParams(httpTarget, params); + PeriodicMetricReader reader = PeriodicMetricReader.builder(exporter) + .setInterval(Duration.ofMillis(getGroupKey().getPeriod())) + .build(); + SdkMeterProvider meterProvider = SdkMeterProvider.builder().registerMetricReader(reader).build(); + OpenTelemetrySdk sdk = OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build(); + Meter meter = sdk.getMeter(INSTRUMENTATION_SCOPE_NAME); + meterProviderContext = new MeterProviderContext(sdk, meterProvider, exporter, meter); + meterProviderContext.initialize(getMergedMetrics(), getMergedCollectorList(), getTaskMetricKeys()); + } catch (Exception e) { + throw new TaskExecutionException("Invalid create MeterProviderContext : " + e.getMessage()); + } + } + + /** + * recreateContext + * + * @param appConfig AppConfig + * @throws TaskExecutionException TaskExecutionException + */ + public void recreateContext(AppConfig appConfig) throws TaskExecutionException { + try { + if (meterProviderContext != null) { + meterProviderContext.shutdown(); + } + createContext(appConfig); + } catch (TaskExecutionException e) { + throw new TaskExecutionException("recreate Context Invalid target URL: " + e.getMessage()); + } + } + + /** + * stop periodic collection + */ + public void stopPeriodicCollection() { + super.stopPeriodicCollection(); + if (meterProviderContext != null) { + meterProviderContext.shutdown(); + } + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/group/TaskGroup.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/group/TaskGroup.java new file mode 100644 index 000000000..64ca96195 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/service/task/group/TaskGroup.java @@ -0,0 +1,325 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service.task.group; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Pair; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import retrofit2.Call; +import retrofit2.Response; + +import org.apache.http.client.utils.URIBuilder; +import org.opengauss.agent.client.DynamicHttpClientBuilder; +import org.opengauss.agent.client.MetricHttpClient; +import org.opengauss.agent.config.AppConfig; +import org.opengauss.agent.entity.TaskDefinition; +import org.opengauss.agent.entity.task.TaskMetricsDefinitionVo; +import org.opengauss.agent.exception.TaskExecutionException; +import org.opengauss.agent.vo.MetricKey; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * TaskGroup + * + * @author: wangchao + * @Date: 2025/5/16 16:44 + * @since 7.0.0-RC2 + **/ +@Slf4j +public class TaskGroup { + /** + * TaskGroup agentId + */ + protected static final String PARAM_AGENT = "agentId"; + + /** + * TaskGroup taskIds + */ + protected static final String PARAM_TASK = "taskIds"; + + private ScheduledExecutorService groupScheduler = Executors.newScheduledThreadPool(1); + private ScheduledFuture collectionTask = null; + @Getter + private final GroupKey groupKey; + @Getter + private Map tasks = new ConcurrentHashMap<>(); + private MetricHttpClient metricHttpClient; + private Map params = new HashMap<>(); + private String httpTarget; + + /** + * Constructor for OtelTaskGroup + * + * @param key group key + */ + public TaskGroup(GroupKey key) { + this.groupKey = key; + } + + /** + * Create context + * + * @param appConfig appConfig + * @throws TaskExecutionException TaskExecutionException + */ + public void createContext(AppConfig appConfig) throws TaskExecutionException { + try { + if (metricHttpClient != null) { + return; + } + httpTarget = new URIBuilder(appConfig.getAppServerUrl()).setPath(groupKey.getDataSendTarget()) + .build() + .toString(); + metricHttpClient = DynamicHttpClientBuilder.createHttpClient(httpTarget); + params.put(PARAM_TASK, new ArrayList<>(tasks.keySet())); + params.put(PARAM_AGENT, appConfig.getAgentId()); + log.info("create Context httpTarget={},params={}", httpTarget, params); + } catch (Exception e) { + throw new TaskExecutionException("Invalid create MeterProviderContext : " + e.getMessage()); + } + } + + /** + * Send data + * + * @param taskId taskId + * @param dataList dataList + */ + public void sendData(Long taskId, List> dataList) { + try { + Call voidCall = metricHttpClient.sendDataMetrics(httpTarget, taskId, getAgentId(), dataList); + log.debug("Export http info | {}: {}", httpTarget, taskId); + Response response = voidCall.execute(); // 同步执行(或使用enqueue异步) + if (!response.isSuccessful()) { + log.error("Export failed | Code: {}", response.code()); + } + } catch (IOException e) { + log.error("Export http error | {}:{}: {}", httpTarget, taskId, e.getMessage()); + } + } + + private Long getAgentId() { + Object object = params.get(PARAM_AGENT); + if (object == null) { + throw new TaskExecutionException("Invalid create MeterProviderContext : agentId is null"); + } + if (object instanceof Long) { + return (Long) object; + } else { + throw new TaskExecutionException("Invalid create MeterProviderContext : agentId is not Long"); + } + } + + /** + * Refresh context + * + * @throws TaskExecutionException TaskExecutionException + */ + public void refreshContext() throws TaskExecutionException { + try { + params.remove(PARAM_TASK); + params.put(PARAM_TASK, new ArrayList<>(tasks.keySet())); + } catch (Exception e) { + throw new TaskExecutionException("Invalid create MeterProviderContext : " + e.getMessage()); + } + } + + /** + * Get taskIds + * + * @return Set taskIds + */ + public Set getTaskIds() { + return tasks.keySet(); + } + + /** + * task is empty + * + * @return boolean + */ + public boolean isEmpty() { + return !tasks.isEmpty(); + } + + /** + * add task + * + * @param task TaskDefinition + */ + public void addTask(TaskDefinition task) { + tasks.put(task.getTaskId(), task); + } + + /** + * Remove task + * + * @param taskId taskId + */ + public void removeTask(Long taskId) { + tasks.remove(taskId); + } + + /** + * Start the group task + * + * @param runnable the runnable task + */ + public void startGroupTask(Runnable runnable) { + if (this.collectionTask == null) { + log.info("Started metric collection schedule for group {} ,tasks={}", groupKey, tasks.keySet()); + // 按组周期启动定时收集 + this.collectionTask = groupScheduler.scheduleAtFixedRate(runnable, initialDelay(), groupKey.getPeriod(), + TimeUnit.MILLISECONDS); + } + } + + /** + * half of group period as initial delay + * + * @return initial delay + */ + private long initialDelay() { + return groupKey.getPeriod() / 2; + } + + /** + * check if the group has task + * + * @return boolean + */ + public boolean hasTask() { + return !tasks.isEmpty(); + } + + /** + * stop periodic collection + */ + public void stopPeriodicCollection() { + if (collectionTask != null) { + collectionTask.cancel(false); + } + if (groupScheduler != null) { + groupScheduler.shutdownNow(); + try { + if (!groupScheduler.awaitTermination(1, TimeUnit.SECONDS)) { + groupScheduler.shutdownNow(); + } + } catch (InterruptedException e) { + groupScheduler.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + if (tasks != null) { + tasks.clear(); + } + } + + /** + * Get the merged collector list + * + * @return collector list + */ + public List getMergedCollectorList() { + return Optional.ofNullable(tasks) + .orElseGet(Collections::emptyMap) + .values() + .stream() + .flatMap(task -> task.getCollectorList().stream()) + .distinct() + .collect(Collectors.toList()); + } + + /** + * Get the collector list of the task + * + * @param taskId task id + * @return collector list + */ + public List getCollectorList(Long taskId) { + return tasks.get(taskId).getCollectorList(); + } + + /** + * Get the task metric keys + * + * @return task metric keys + */ + public Map> getTaskMetricKeys() { + return tasks.entrySet() + .stream() + .filter(entry -> entry.getKey() != null && entry.getValue() != null) + .flatMap(entry -> { + final Long taskId = entry.getKey(); + final List taskMetricDefs = entry.getValue().getMetricsDefinitionList(); + if (CollUtil.isEmpty(taskMetricDefs)) { + return Stream.empty(); + } + return taskMetricDefs.stream() + .filter(Objects::nonNull) + .map(metric -> Pair.of(taskId, MetricKey.of(metric.getName(), metric.getProp()))); + }) + .collect(Collectors.groupingBy(Pair::getKey, Collectors.mapping(Pair::getValue, Collectors.toList()))); + } + + /** + * Validate the task consistency + * + * @param newTask new task + * @return true if consistent, false otherwise + */ + public boolean validateTaskConsistency(TaskDefinition newTask) { + return Optional.ofNullable(tasks) + .orElseGet(Collections::emptyMap) + .values() + .stream() + .allMatch(existingTask -> existingTask.getPeriod() == newTask.getPeriod() && existingTask.getStoragePolicy() + .equals(newTask.getStoragePolicy())); + } + + /** + * Get all metrics from all tasks + * + * @return a list of metrics + */ + public List getMergedMetrics() { + return new ArrayList<>(Optional.ofNullable(tasks) + .orElseGet(Collections::emptyMap) + .values() + .stream() + .flatMap(task -> task.getMetricsDefinitionList().stream()) + .collect(Collectors.toMap(TaskMetricsDefinitionVo::getName, Function.identity(), (a, b) -> a)) + .values()); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/utils/DurationUtils.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/utils/DurationUtils.java new file mode 100644 index 000000000..c802cd323 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/utils/DurationUtils.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.utils; + +import org.opengauss.agent.constant.AgentConstants; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.Period; +import java.time.temporal.ChronoUnit; + +/** + * DurationUtils + * + * @author: wangchao + * @Date: 2025/6/6 16:25 + * @since 7.0.0-RC2 + **/ +public class DurationUtils { + /** + * Convert milliseconds to Duration isoString + * + * @param milliseconds milliseconds + * @return Duration isoString + */ + public static String formatInterval(long milliseconds) { + Duration duration = Duration.ofMillis(milliseconds); + String iso = duration.toString(); // 示例:PT5S + return iso.substring(2); // 移除 PT 前缀 → 5S + } + + /** + * Convert Duration isoString to milliseconds + * + * @param isoString isoString + * @return milliseconds + */ + public static long parseToMillis(String isoString) { + if (isoString.startsWith(AgentConstants.Duration.PREFIX_ONLE_TIME)) { + // 解析时间部分(小时/分/秒) + Duration duration = Duration.parse(isoString); + return duration.toMillis(); + } + // 分离日期部分(年/月/周/天)和时间部分(小时/分/秒) + String[] parts = isoString.split(AgentConstants.Duration.SPLIT_DATE_AND_TIME); + String datePart = parts[0].substring(1); // 去掉前缀P + String timePart = parts.length > 1 ? parts[1] : ""; + // 解析日期部分(年/月/周/天) + Period period = Period.parse(AgentConstants.Duration.PREFIX_DATE + datePart); + LocalDate start = LocalDate.now(); + LocalDate end = start.plus(period); + long days = ChronoUnit.DAYS.between(start, end); + // 解析时间部分(小时/分/秒) + Duration duration = Duration.parse(AgentConstants.Duration.PREFIX_ONLE_TIME + timePart); + // 合并总毫秒数 + return Duration.ofDays(days).plus(duration).toMillis(); + } + + /** + * Convert fixed time isoString to milliseconds + * + * @param isoString isoString + * @return milliseconds + */ + public static long parseFixedTimeToMillis(String isoString) { + // 分离日期部分(年/月/周/天)和时间部分(小时/分/秒) + String[] parts = isoString.split(AgentConstants.Duration.SPLIT_DATE_AND_TIME); + String datePart = parts[0]; + if (datePart.contains(AgentConstants.Duration.YEAR) || datePart.contains(AgentConstants.Duration.MONTH)) { + throw new IllegalArgumentException("fixed time period must be days"); + } + String timePart = parts.length > 1 ? parts[1] : ""; + // 解析 周/天 + Period period = Period.parse(datePart); + long days = period.getDays(); + // 解析时间部分(小时/分/秒) + Duration duration = Duration.parse(AgentConstants.Duration.PREFIX_ONLE_TIME + timePart); + // 合并总毫秒数 + return Duration.ofDays(days).plus(duration).toMillis(); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/utils/MathUtils.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/utils/MathUtils.java new file mode 100644 index 000000000..2e9a51b16 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/utils/MathUtils.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.utils; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; + +/** + * MathUtils + * + * @author: wangchao + * @Date: 2025/6/6 16:25 + * @since 7.0.0-RC2 + **/ +public class MathUtils { + private static final long LONG_1024_1024_1024 = 1024 * 1024 * 1024; + private static final BigDecimal DECIMAL_1024_1024_1024 = BigDecimal.valueOf(LONG_1024_1024_1024); + private static final BigDecimal DECIMAL_1024 = BigDecimal.valueOf(1024); + private static final BigDecimal HUNDRED = BigDecimal.valueOf(100); + private static final MathContext DIVISION_CONTEXT = new MathContext(6, RoundingMode.HALF_UP); // 预计算精度 + + /** + * calc double divide 1024*1024*1024 + * + * @param dividend dividend + * @return double + */ + public static double convertBytesToGigabytes(long dividend) { + return new BigDecimal(dividend).divide(DECIMAL_1024_1024_1024, DIVISION_CONTEXT).doubleValue(); + } + + /** + * calc double divide 1024 + * + * @param dividend dividend + * @return double + */ + public static double divide1024(long dividend) { + return new BigDecimal(dividend).divide(DECIMAL_1024, DIVISION_CONTEXT).doubleValue(); + } + + /** + * usage percentage percentage = (1.0 - available / total) * 100.0; + * + * @param available available + * @param total total + * @return double usage percentage + */ + public static double calculateUtilizationPercentage(long available, long total) { + if (total == 0 || available >= total) { + return 0.0; + } + BigDecimal used = BigDecimal.valueOf(total - available); + BigDecimal totalValue = BigDecimal.valueOf(total); + return used.multiply(HUNDRED).divide(totalValue, 4, RoundingMode.HALF_UP).doubleValue(); + } + + /** + * safe parse double value to double, if parse failed, return Double.NaN + * + * @param value value + * @return double + */ + public static Double doubleValueOf(String value) { + try { + return Double.valueOf(value); + } catch (NumberFormatException e) { + return Double.NaN; + } + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/utils/OsCommandUtils.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/utils/OsCommandUtils.java new file mode 100644 index 000000000..e657953ff --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/utils/OsCommandUtils.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.utils; + +import org.apache.commons.exec.CommandLine; +import org.apache.commons.exec.DefaultExecutor; +import org.apache.commons.exec.ExecuteException; +import org.apache.commons.exec.ExecuteWatchdog; +import org.apache.commons.exec.PumpStreamHandler; +import org.apache.commons.io.output.ByteArrayOutputStream; +import org.opengauss.agent.entity.OsCmdResult; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +/** + * OsCommandUtils + * + * @author: wangchao + * @Date: 2025/7/7 09:47 + * @since 7.0.0-RC2 + **/ +public class OsCommandUtils { + private static final Set ALLOWED_COMMANDS = ConcurrentHashMap.newKeySet(); + private static final Pattern SAFE_ARG_PATTERN = Pattern.compile("^[a-zA-Z0-9\\s\\-\\.]+$"); + + /** + * force refresh allowed command Collection + * + * @param allowedCommands allowed command Collection + */ + public static void forceRefreshAllowedCommand(List allowedCommands) { + ALLOWED_COMMANDS.addAll(allowedCommands); + } + + /** + * get allowed command Collection + * + * @return allowed command Collection + */ + public static Set getAllowedCommands() { + return ALLOWED_COMMANDS; + } + + /** + * os cmd execute + * + * @param commandLine os cmd + * @return OsCmdResult + */ + public static OsCmdResult execute(String commandLine) { + if (!isSafeCommand(commandLine)) { + return new OsCmdResult(-1, "Unsafe command detected: " + commandLine); + } + CommandLine cmdLine = CommandLine.parse(commandLine); + DefaultExecutor executor = new DefaultExecutor(); + executor.setExitValues(null); + executor.setWatchdog(new ExecuteWatchdog(60000)); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream); + executor.setStreamHandler(streamHandler); + try { + return new OsCmdResult(executor.execute(cmdLine), outputStream.toString(Charset.defaultCharset())); + } catch (ExecuteException e) { + return new OsCmdResult(e.getExitValue(), + "Execution failed: " + outputStream.toString(Charset.defaultCharset())); + } catch (IOException e) { + return new OsCmdResult(-1, "Cmd Error: " + e.getMessage()); + } finally { + try { + outputStream.close(); + } catch (IOException ignored) { + } + } + } + + /** + * check command is safe + * + * @param commandLine commandLine + * @return isSafeCommand + */ + protected static boolean isSafeCommand(String commandLine) { + if (commandLine == null || commandLine.trim().isEmpty()) { + return false; + } + String[] parts = commandLine.trim().split("\\s+", 2); + if (parts.length == 0) { + return false; + } + String commandName = parts[0].contains("/") ? parts[0].substring(parts[0].lastIndexOf('/') + 1) : parts[0]; + if (!ALLOWED_COMMANDS.contains(commandName)) { + return true; + } + if (parts.length > 1) { + String args = parts[1]; + return SAFE_ARG_PATTERN.matcher(args).matches(); + } + return true; + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/utils/RsaUtils.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/utils/RsaUtils.java new file mode 100644 index 000000000..99ab1f821 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/utils/RsaUtils.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.utils; + +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.CryptoException; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.codec.binary.Base64; +import org.opengauss.agent.exception.AgentException; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.atomic.AtomicReference; + +/** + * RsaUtils + * + * @author wangchao + * @since 2024/10/29 09:26 + */ +@Slf4j +public class RsaUtils { + private static final AtomicReference PUBLIC_KEY_CACHE = new AtomicReference<>(); + private static final AtomicReference PRIVATE_KEY_CACHE = new AtomicReference<>(); + + static { + initRsaKeyPair(); + } + + private static void initRsaKeyPair() { + try { + KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA"); + gen.initialize(4096); + KeyPair keyPair = gen.generateKeyPair(); + PUBLIC_KEY_CACHE.set(Base64.encodeBase64String(keyPair.getPublic().getEncoded())); + PRIVATE_KEY_CACHE.set(Base64.encodeBase64String(keyPair.getPrivate().getEncoded())); + } catch (NoSuchAlgorithmException e) { + log.error("RSA key pair generation failed", e); + throw new AgentException("Critical failure: RSA key generation failed", e); + } + } + + /** + * get public key + * + * @return public key + */ + public static String publicKey() { + if (StrUtil.isEmpty(PUBLIC_KEY_CACHE.get())) { + initRsaKeyPair(); + } + return PUBLIC_KEY_CACHE.get(); + } + + /** + * encrypt + * + * @param plainText plain text + * @return cipher text + */ + public static String encrypt(String plainText) { + return encrypt(plainText, PUBLIC_KEY_CACHE.get()); + } + + /** + * encrypt + * + * @param plainText plain text + * @param publicKey public key + * @return cipher text + */ + public static String encrypt(String plainText, String publicKey) { + RSA rsa = new RSA(null, publicKey); + byte[] encrypt = rsa.encrypt(StrUtil.bytes(plainText, CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey); + return Base64.encodeBase64String(encrypt); + } + + /** + * decrypt + * + * @param cipherText cipher text + * @return plain text + */ + public static String decrypt(String cipherText) { + return decrypt(cipherText, PRIVATE_KEY_CACHE.get()); + } + + /** + * decrypt + * + * @param cipherText cipher text + * @param privateKey private key + * @return plain text + */ + public static String decrypt(String cipherText, String privateKey) { + try { + RSA rsa = new RSA(privateKey, null); + byte[] decrypt = rsa.decrypt(Base64.decodeBase64(cipherText), KeyType.PrivateKey); + return StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8); + } catch (CryptoException e) { + log.error("RSA decryption failed for cipherText", e); + throw new SecurityException("Decryption failed. Possible tampering detected", e); + } + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/vo/MetricData.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/vo/MetricData.java new file mode 100644 index 000000000..2a34a0da9 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/vo/MetricData.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.vo; + +import io.opentelemetry.api.common.Attributes; +import lombok.Data; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * MetricData + * + * @author: wangchao + * @Date: 2025/5/6 09:55 + * @Description: MetricData + * @since 7.0.0-RC2 + **/ +@Data +public class MetricData { + private AtomicReference value = new AtomicReference<>(0.0); + private Attributes attributes = Attributes.empty(); +} + diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/vo/MetricKey.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/vo/MetricKey.java new file mode 100644 index 000000000..1952cd9ee --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/vo/MetricKey.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.vo; + +import lombok.Data; + +/** + * MetricKey + * + * @author: wangchao + * @Date: 2025/5/6 09:53 + * @Description: MetricKey + * @since 7.0.0-RC2 + **/ +@Data +public class MetricKey { + private final String metricName; + private final String propValue; + + /** + * Constructor + * + * @param metricName metric name + * @param propValue prop value + */ + private MetricKey(String metricName, String propValue) { + this.metricName = metricName; + this.propValue = propValue; + } + + /** + * Create MetricKey + * + * @param metricName metric name + * @param propValue prop value + * @return MetricKey + */ + public static MetricKey of(String metricName, String propValue) { + return new MetricKey(metricName, propValue); + } + + /** + * Create MetricKey + * + * @param metricName metric name + * @return MetricKey + */ + public static MetricKey of(String metricName) { + return new MetricKey(metricName, null); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/vo/MultiValueMetric.java b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/vo/MultiValueMetric.java new file mode 100644 index 000000000..2c55c2ac7 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/java/org/opengauss/agent/vo/MultiValueMetric.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.vo; + +import cn.hutool.core.lang.Pair; +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * MultiValueMetric + * + * @author: wangchao + * @Date: 2025/5/13 20:21 + * @since 7.0.0-RC2 + **/ +@Data +@AllArgsConstructor +public class MultiValueMetric { + private String name; + private Pair propValue; + + /** + * Create MultiValueMetric + * + * @param name metric name + * @param propName prop name + * @param propValue prop value + * @return MultiValueMetric + */ + public static MultiValueMetric of(String name, String propName, Double propValue) { + return new MultiValueMetric(name, Pair.of(propName, propValue)); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/main/resources/application.yml b/ER-Model/openGauss-datakit-agent/src/main/resources/application.yml new file mode 100644 index 000000000..58457a9ee --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/main/resources/application.yml @@ -0,0 +1,47 @@ +agent: + id: 1 + version: 7.0.0-RC3 + server: http://127.0.0.1:9494 + name: datakit-agent + heartbeat: + interval: 2 + break-wait-max-times: 3 + thread-pool: + max-threads: 10 + os: + command-write-list: df,free,top,ps,netstat +quarkus: + http: + port: 19001 + scheduler: + start-mode: forced + thread-pool: + core-threads: 1 + max-threads: 10 + keep-alive-time: 10000 + rest-client: + "agent.server": + uri: ${agent.server} + connect-timeout: 5000 + read-timeout: 10000 + log: + category: + "org.apache.http": + level: WARN + "io.vertx.core.impl.EventLoopContext": + level: WARN + "io.netty": + level: ERROR + file: + level: DEBUG + enable: true + path: /path/datakit_agent.log + max-size: 100MB + max-history: 30 + format: "%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p [%c{1.}] (%t) %s%e%n" + pattern: logs/datakit_agent.%d{yyyy-MM-dd}.log + console: + enable: true + level: DEBUG + color: true + format: "%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p [%c{1.}] (%t) %s%e%n" diff --git a/ER-Model/openGauss-datakit-agent/src/test/java/org/opengauss/agent/duration/DurationStringTest.java b/ER-Model/openGauss-datakit-agent/src/test/java/org/opengauss/agent/duration/DurationStringTest.java new file mode 100644 index 000000000..2d2565716 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/test/java/org/opengauss/agent/duration/DurationStringTest.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.duration; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.opengauss.agent.utils.DurationUtils; + +/** + * DurationStringTest + * + * @author: wangchao + * @Date: 2025/4/3 16:23 + * @Description: DurationStringTest + * @since 7.0.0-RC2 + **/ +public class DurationStringTest { + @Test + void testDurationToString() { + assertEquals("1S", DurationUtils.formatInterval(1000)); + assertEquals("1.2S", DurationUtils.formatInterval(1200)); + assertEquals("0.2S", DurationUtils.formatInterval(200)); + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit-agent/src/test/java/org/opengauss/agent/duration/DurationTest.java b/ER-Model/openGauss-datakit-agent/src/test/java/org/opengauss/agent/duration/DurationTest.java new file mode 100644 index 000000000..db8bffa78 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/test/java/org/opengauss/agent/duration/DurationTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.duration; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.opengauss.agent.utils.DurationUtils; + +import java.time.Duration; +import java.time.Period; + +/** + * DurationTest + * + * @author: wangchao + * @Date: 2025/4/16 10:43 + * @Description: DurationTest + * @since 7.0.0-RC2 + **/ +public class DurationTest { + @Test + public void test1() { + Duration p0 = Duration.parse("PT-1s"); + assertEquals(-1, p0.toSeconds()); + } + + @Test + public void test2() { + Duration p0 = Duration.parse("PT0s"); + assertEquals(0, p0.toSeconds()); + } + + @Test + public void test3() { + Duration p0 = Duration.parse("P1D"); + assertEquals(86400, p0.toSeconds()); + } + + @Test + public void test4() { + Period p0 = Period.parse("P2W"); + long seconds = Duration.ofDays(p0.getDays()).toSeconds(); + assertEquals(1209600, seconds); + } + + @Test + public void test5() { + Period p0 = Period.parse("P3M2D"); + assertEquals(3, p0.getMonths()); + assertEquals(2, p0.getDays()); + } + + @Test + public void test6() { + Duration p0 = Duration.parse("PT0.01s"); + assertEquals(10, p0.toMillis()); + } + + @Test + public void test7() { + Duration parse = Duration.parse("PT10s"); + assertEquals(10000, parse.toMillis()); + } + + @Test + public void test8() { + Duration parse2 = Duration.parse("PT2M10s"); + assertEquals(130000, parse2.toMillis()); + } + + @Test + public void test9() { + Duration parse3 = Duration.parse("PT1H2M10s"); + assertEquals(3730000, parse3.toMillis()); + } + + @Test + public void test10() { + Duration parse4 = Duration.parse("P1D"); + assertEquals(86400000, parse4.toMillis()); + } + + @Test + public void test11() { + Duration parse5 = Duration.parse("P100D"); + assertEquals(8640000000L, parse5.toMillis()); + } + + @Test + public void test12() { + long parse6 = DurationUtils.parseFixedTimeToMillis("P3W4DT5H6M7.5S"); // 固定时间周期 3周4天5小时6分7.5秒 + assertEquals(2178367500L, parse6); + } + + @Test + public void test13() { + String input = "P1Y2M3W4DT5H6M7.5S"; // 1年2月3周4天5小时6分7.5秒 + long totalMs = DurationUtils.parseToMillis(input); + assertEquals(38984767500L, totalMs); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/test/java/org/opengauss/agent/utils/MathUtilsTest.java b/ER-Model/openGauss-datakit-agent/src/test/java/org/opengauss/agent/utils/MathUtilsTest.java new file mode 100644 index 000000000..62cdfce84 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/test/java/org/opengauss/agent/utils/MathUtilsTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.utils; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * MathUtilsTest + * + * @author: wangchao + * @Date: 2025/7/3 16:12 + * @since 7.0.0-RC2 + **/ +public class MathUtilsTest { + /** + * Test convertBytesToGigabytes method. + */ + @Test + public void testConvertBytesToGigabytes() { + // Test normal cases + assertEquals(1.0, MathUtils.convertBytesToGigabytes(1024L * 1024 * 1024), 0.0001); + assertEquals(1.5, MathUtils.convertBytesToGigabytes(1024L * 1024 * 1024 * 3 / 2), 0.0001); + // Test boundary cases + assertEquals(0.0, MathUtils.convertBytesToGigabytes(0), 0.0001); + assertEquals(9.31323E-10, MathUtils.convertBytesToGigabytes(1), 0.0001); + // Test large value + assertEquals(1000.0, MathUtils.convertBytesToGigabytes(1024L * 1024 * 1024 * 1000), 0.0001); + } + + /** + * Test divide1024 method. + */ + @Test + public void testDivide1024() { + // Test normal cases + assertEquals(1.0, MathUtils.divide1024(1024), 0.0001); + assertEquals(1.5, MathUtils.divide1024(1536), 0.0001); + // Test boundary cases + assertEquals(0.0, MathUtils.divide1024(0), 0.0001); + assertEquals(0.000976562, MathUtils.divide1024(1), 0.0001); + // Test large value + assertEquals(1000.0, MathUtils.divide1024(1024 * 1000), 0.0001); + } + + /** + * Test calculateUtilizationPercentage method. + */ + @Test + public void testCalculateUtilizationPercentage() { + // Test normal cases + assertEquals(50.0, MathUtils.calculateUtilizationPercentage(500, 1000), 0.0001); + assertEquals(33.4, MathUtils.calculateUtilizationPercentage(666, 1000), 0.0001); + // Test boundary cases + // total = 0 + assertEquals(0.0, MathUtils.calculateUtilizationPercentage(0, 0), 0.0001); + // available = 0 + assertEquals(100.0, MathUtils.calculateUtilizationPercentage(0, 1000), 0.0001); + // available = total + assertEquals(0.0, MathUtils.calculateUtilizationPercentage(1000, 1000), 0.0001); + // Test edge cases + assertEquals(99.9999, MathUtils.calculateUtilizationPercentage(1, 1000000), 0.0001); + assertEquals(0.0001, MathUtils.calculateUtilizationPercentage(999999, 1000000), 0.0001); + } +} diff --git a/ER-Model/openGauss-datakit-agent/src/test/java/org/opengauss/agent/utils/OsCommandUtilsTest.java b/ER-Model/openGauss-datakit-agent/src/test/java/org/opengauss/agent/utils/OsCommandUtilsTest.java new file mode 100644 index 000000000..e8df376a7 --- /dev/null +++ b/ER-Model/openGauss-datakit-agent/src/test/java/org/opengauss/agent/utils/OsCommandUtilsTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.util.ReflectionUtils; + +import java.util.List; + +/** + * OsCommandUtilsTest + * + * @author: wangchao + * @Date: 2025/7/7 10:16 + * @since 7.0.0-RC2 + **/ +public class OsCommandUtilsTest { + @Test + public void testAllowedCommandReflection() { + String command = "echo hello"; + ReflectionUtils.findMethod(OsCommandUtils.class, "isSafeCommand", String.class).ifPresent(method -> { + ReflectionUtils.makeAccessible(method); + assertEquals(true, ReflectionUtils.invokeMethod(method, OsCommandUtils.class, command)); + }); + } + + @Test + public void testAllowedCommandProtected() { + String command = "echo hello"; + assertEquals(true, OsCommandUtils.isSafeCommand(command)); + } + + @Test + public void testForceRefreshAllowedCommand() { + String command = "msg ax"; + OsCommandUtils.forceRefreshAllowedCommand(List.of(command)); + assertEquals(true, OsCommandUtils.isSafeCommand(command)); + } +} diff --git a/ER-Model/openGauss-datakit/.gitignore b/ER-Model/openGauss-datakit/.gitignore new file mode 100644 index 000000000..15450adfa --- /dev/null +++ b/ER-Model/openGauss-datakit/.gitignore @@ -0,0 +1,44 @@ +###################################################################### +# Build Tools +**/.DS_Store +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup +*.swp + +!*/build/*.java +!*/build/*.html +!*/build/*.xml diff --git a/ER-Model/openGauss-datakit/README.md b/ER-Model/openGauss-datakit/README.md new file mode 100644 index 000000000..11bcdd168 --- /dev/null +++ b/ER-Model/openGauss-datakit/README.md @@ -0,0 +1,180 @@ +## 代码结构说明 +``` +├── visualtool-api //服务端API模块 +│   ├── pom.xml +│   ├── com.xx.ops //运维业务包 +│   ├── com.xx.modeling //数据建模业务包 +├── visualtool-common //公共工具模块 +│   ├── pom.xml +│   ├── src +├── visualtool-framework //集成框架模块 +│   ├── pom.xml +│   ├── src +├── visualtool-service //系统业务代码模块(service、mapper、domain、xml) +│   ├── pom.xml +│   ├── com.xx.xx.ops //运维业务包 +│   ├── com.xx.xx.modeling //数据建模业务包 +├── visualtool-ui //前端模块 +``` +## 数据库对象说明 + +| 名称 | 类型 | 说明 | +|----------------|-----|---------------| +| sq_sys_log_config_id | 序列 | 系统日志配置表自增序列 | +| sq_sys_menu_id | 序列 | 系统菜单表自增序列 | +| sq_sys_log_id | 序列 | 系统操作日志表自增序列 | +| sq_sys_plugin_config_data_id | 序列 | 系统插件配置数据表自增序列 | +| sq_sys_plugin_config_id | 序列 | 系统插件配置表自增序列 | +| sq_sys_plugin_id | 序列 | 系统插件表自增序列 | +| sq_sys_role_id | 序列 | 系统角色表自增序列 | +| sq_sys_user_id | 序列 | 系统用户表自增序列 | +| sq_sys_white_list_id | 序列 | 系统安全白名单表自增序列 | +| sys_log_config | 表 | 系统日志配置表 | +| sys_menu | 表 | 系统菜单表 | +| sys_oper_log | 表 | 系统操作日志表 | +| sys_plugin_config | 表 | 系统插件配置表 | +| sys_plugin_config_data | 表 | 系统插件配置数据表 | +| sys_plugins | 表 | 系统插件表 | +| sys_role | 表 | 系统角色表 | +| sys_role_menu | 表 | 系统角色与菜单关系表 | +| sys_user | 表 | 系统用户表 | +| sys_user_role | 表 | 系统用户与角色关系表 | +| sys_white_list | 表 | 系统安全白名单表 | +| ops_az | 表 | 系统可用区信息表 | +| ops_backup | 表 | 系统备份信息表 | +| ops_check | 表 | 系统一键自检信息表 | +| ops_cluster | 表 | 系统集群信息表 | +| ops_cluster_node | 表 | 系统集群节点信息表 | +| ops_encryption | 表 | 系统密钥信息表 | +| ops_host | 表 | 系统主机信息表 | +| ops_host_user | 表 | 系统主机用户信息表 | +| ops_jdbcdb_cluster | 表 | 系统数据库资源集群表 | +| ops_jdbcdb_cluster_node | 表 | 系统数据库资源集群节点表 | +| ops_package_manager | 表 | 系统安装包管理表 | +| ops_wdr | 表 | 系统WDR表 | + + + +## 支持的服务器系统 +openEuler 20.3LTS(x86_x64,ARM),centos7.x(x86_x64) +## 安装部署 + +### jar包安装 +```shell +#1、创建datakit工作目录,并在工作目录中创建存放系统运行数据的子目录 +mkdir -p 自定义工作目录 +cd 自定义工作目录 +mkdir -p logs config ssl files +#2、将visualtool-main.jar包上传至 $自定义工作目录 下 +#3、修改application-temp.yml文件中的数据链链接ip、port、database、dbuser、dbpassword。将修改后的配置文件application-temp.yml传至 $自定义工作目录/config/下 +#4、修改配置文件中平台工作目录 +sed -i s#/ops#$(pwd)#g config/application-temp.yml +#4、创建ssl文件 +keytool -genkey -noprompt \ + -dname "CN=opengauss, OU=opengauss, O=opengauss, L=Beijing, S=Beijing, C=CN"\ + -alias opengauss\ + -storetype PKCS12 \ + -keyalg RSA \ + -keysize 2048 \ + -keystore $(pwd)/ssl/keystore.p12 \ + -validity 3650 \ + -storepass 123456 +#5、执行启动命令 +nohup java -Xms2048m -Xmx4096m -jar $(pwd)/visualtool-main.jar \ + --spring.profiles.active=temp >$(pwd)/logs/visualtool-main.out 2>&1 & +``` + +### docker安装 +```shell +#1、创建datakit工作目录,并在工作目录中创建存放系统运行数据的子目录 +mkdir -p 自定义工作目录 +cd 自定义工作目录 +mkdir -p logs config ssl files +#2、将visualtool-main.jar包上传至 $自定义工作目录 下 +#3、修改application-temp.yml文件中的数据链链接ip、port、database、dbuser、dbpassword。将修改后的配置文件application-temp.yml传至 $自定义工作目录/config/下 +#4、创建ssl文件 +keytool -genkey -noprompt \ + -dname "CN=opengauss, OU=opengauss, O=opengauss, L=Beijing, S=Beijing, C=CN"\ + -alias opengauss\ + -storetype PKCS12 \ + -keyalg RSA \ + -keysize 2048 \ + -keystore $(pwd)/ssl/keystore.p12 \ + -validity 3650 \ + -storepass 123456 +#5、编辑Dockerfile +vi Dockerfile +#复制以下内容进行粘贴 +FROM openjdk:11 +ENV TZ=Asia/Shanghai +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +EXPOSE 9494 5432 +WORKDIR /ops/ +ENTRYPOINT ["java","-Xms2048m","-Xmx4096m", "-jar", "visualtool-main.jar"] +#6、构建镜像 +docker build -f Dockerfile -t datakit:5.0.0 . +#7、启动容器 +docker run -idt -p 9494:9494 \ + -v /etc/localtime:/etc/localtime:ro \ + -v $(pwd):/ops \ + --name datakit datakit:5.0.0 \ + --spring.profiles.active=temp +``` + +## 升级步骤 +### jar包升级 +```shell +#1、将新的jar包传至 自定义工作目录 下,替换原有visualtool-main.jar +cd 自定义工作目录 +#2、关闭原有进程后,再执行启动命令 +nohup java -Xms2048m -Xmx4096m -jar $(pwd)/visualtool-main.jar \ + --spring.profiles.active=temp >$(pwd)/logs/visualtool-main.out 2>&1 & +``` +### docker安装升级 +```shell +#1、将新的jar包传至 自定义工作目录 下,替换原有visualtool-main.jar +#2、重启容器 +docker restart datakit +``` +### 平台配置修改 +#### 端口修改 +在工作目录的config目录中,修改配置文件,将默认9494端口修改为自定义端口,之后重启服务 + +### 注意事项: +1、当前平台运行依赖于openJdk11。 + +2、平台使用的数据库,当前仅支持openGauss数据库,并且需要提前创建database。 + +3、需要将部署服务器IP配置在平台使用的数据库(openGauss)的白名单列表中。 + +4、平台默认的登录账号密码:admin/admin123,请在首次登录后及时修改密码。 + +## 卸载步骤 +1、停止java进程或者docker容器,删除工作目录。 + +2、手动清理数据库中所有的表和序列。 + +## 后端说明 +> 1、后端返回给前端的响应编码,统一在org.opengauss.admin.common.enums.ResponseCode中定义,按照规则划分模块,规则如下: ++ 501xx 一体化模块;比如50101、50102 + +## 前端说明 + +## 主程序接口暴露 +为了更好的通过插件扩展系统功能,主程序需要暴露接口给插件扩展和使用。主要有两类接口需要暴露: ++ 给插件扩展的Interface +> 在visualtool-service模块的org.opengauss.admin.system.plugin.extract包里定义。 ++ 给插件调用的Service, +> 在visualtool-service模块的org.opengauss.admin.system.plugin.facade包里定义。 + +## 相关文档 +[openGauss安装文档]( https://docs.opengauss.org/zh/docs/3.0.0/docs/installation/%E5%8D%95%E8%8A%82%E7%82%B9%E5%AE%89%E8%A3%85.html ) + +[主程序开放接口文档]( https://fullstack-dao.feishu.cn/docx/doxcnIa9e0ChR4bJWlx4IyBfzjf ) + +[主程序前端开发规范指导]( https://fullstack-dao.feishu.cn/docx/doxcnyE9BNt2mm0WV5o2dPqxAec ) + +[插件开发手册]( https://fullstack-dao.feishu.cn/docx/doxcnu2EjetnyXmL1sYIooyrivp ) + +[主程序扩展手册]( https://fullstack-dao.feishu.cn/docx/doxcnV63pz1w4bn4Zn1y2lxwJnf ) + diff --git a/ER-Model/openGauss-datakit/Third_Party_Open_Source_Software_List.yaml b/ER-Model/openGauss-datakit/Third_Party_Open_Source_Software_List.yaml new file mode 100644 index 000000000..63e7328e8 --- /dev/null +++ b/ER-Model/openGauss-datakit/Third_Party_Open_Source_Software_List.yaml @@ -0,0 +1,259 @@ +spring-boot-starter-web: + groupId: org.springframework.boot + artifactId: spring-boot-starter-web + version: 2.5.6 + url: https://github.com/spring-projects/spring-boot +spring-brick: + groupId: com.gitee.starblues + artifactId: spring-brick + version: 3.1.0 + url: https://gitee.com/starblues/springboot-plugin-framework-parent/tree/master/spring-brick-bootstrap +spring-brick-bootstrap: + groupId: com.gitee.starblues + artifactId: spring-brick-bootstrap + version: 3.1.0 + url: https://gitee.com/starblues/springboot-plugin-framework-parent/tree/master/spring-brick-bootstrap +lombok: + groupId: org.projectlombok + artifactId: lombok + version: 1.18.24 + url: http://github.com/projectlombok/lombok +druid-spring-boot-starter: + groupId: com.alibaba + artifactId: druid-spring-boot-starter + version: 1.2.6 + url: https://wenshao@github.com/alibaba/druid.git +fastjson: + groupId: com.alibaba + artifactId: fastjson + version: 1.2.78 + url: https://github.com/alibaba/fastjson2 +mybatis-plus-boot-starter: + groupId: com.baomidou + artifactId: mybatis-plus-boot-starter + version: 3.5.0 + url: https://github.com/baomidou/mybatis-plus +baomidou: + groupId: com.baomidou + artifactId: mybatis-plus-boot-starter + version: 3.5.0 + url: https://github.com/baomidou/mybatis-plus +jsch: + groupId: com.github.mwiede + artifactId: jsch + version: 0.2.9 + url: https://github.com/mwiede/jsch +javax.servlet-api: + groupId: javax.servlet + artifactId: javax.servlet-api + version: 4.0.1 + url: https://github.com/javaee/servlet-spec +hutool-all: + groupId: cn.hutool + artifactId: hutool-all + version: 5.3.5 + url: https://github:com/looly/hutool +junit: + groupId: junit + artifactId: junit + version: 4.13 + url: https://github.com/junit-team/junit4 + +opengauss-jdbc: + groupId: org.opengauss + artifactId: opengauss-jdbc + version: 3.0.0 + url: https://gitee.com/opengauss/openGauss-connector-jdbc +knife4j-spring-boot-starter: + groupId: com.github.xiaoymin + artifactId: knife4j-spring-boot-starter + version: 3.0.3 + url: https://gitee.com/xiaoym/knife4j +javax.annotation-api: + groupId: javax.annotation + artifactId: javax.annotation-api + version: 1.3.2 + url: http://jcp.org/en/jsr/detail?id=250 +istack-commons-runtime: + groupId: com.sun.istack + artifactId: istack-commons-runtime + version: 3.0.11 + url: https://github.com/eclipse-ee4j/jaxb-istack-commons +FastInfoset: + groupId: com.sun.istack + artifactId: FastInfoset + version: 1.2.18 + url: https://projects.eclipse.org/projects/ee4j.jaxb-impl +jaxb-api: + groupId: javax.xml.bind + artifactId: jaxb-api + version: 2.4.0-b180725.0427 + url: https://github.com/javaee/jaxb-spec +jaxb-runtime: + groupId: org.glassfish.jaxb + artifactId: jaxb-runtime + version: 2.4.0-b180725.0644 + url: http://jaxb.java.net +commons-lang3: + groupId: org.apache.commons + artifactId: commons-lang3 + version: 3.12.0 + url: https://commons.apache.org/proper/commons-lang/ +commons-io: + groupId: commons-io + artifactId: commons-io + version: 2.14.0 + url: https://commons.apache.org/proper/commons-io/ +commons-fileupload: + groupId: commons-fileupload + artifactId: commons-fileupload + version: 1.4 + url: http://commons.apache.org/proper/commons-fileupload/ +snakeyaml: + groupId: org.yaml + artifactId: snakeyaml + version: 1.28 + url: http://www.snakeyaml.org +jjwt: + groupId: io.jsonwebtoken + artifactId: jjwt + version: 0.9.1 + url: https://github.com/jwtk/jjwt +guava: + groupId: com.google.guava + artifactId: guava + version: 20.0 + url: https://github.com/google/guava/ +commons-pool2: + groupId: org.apache.commons + artifactId: commons-pool2 + version: 2.9.0 + url: https://commons.apache.org/proper/commons-pool/ +commons-codec: + groupId: commons-codec + artifactId: commons-codec + version: 1.15 + url: https://commons.apache.org/proper/commons-codec/ +easyexcel: + groupId: com.alibaba + artifactId: easyexcel + version: 3.1.1 + url: https://github.com/alibaba/easyexcel/easyexcel +spring-boot-starter-websocket: + groupId: org.springframework.boot + artifactId: spring-boot-starter-websocket + version: 2.5.6 + url: https://github.com/spring-projects/spring-boot +oshi-core: + groupId: com.github.oshi + artifactId: oshi-core + version: 5.8.0 + url: https://github.com/oshi/oshi.git +spring-boot-starter-actuator: + groupId: org.springframework.boot + artifactId: spring-boot-starter-actuator + version: 2.5.6 + url: https://github.com/spring-projects/spring-boot +micrometer-registry-prometheus: + groupId: io.micrometer + artifactId: micrometer-registry-prometheus + version: 1.7.5 + url: https://github.com/micrometer-metrics/micrometer + + +antv/x6: + cpeName: antv/x6 + version: 1.32.10 + url: https://github.com/antvis/X6 +antv/x6-vue-shapeantv/x6: + cpeName: antv/x6-vue-shape + version: 1.4.2 + url: https://github.com/antvis/X6/pkgs/npm/x6-vue-shape +arco-design/web-vue: + cpeName: arco-design/web-vue + version: 2.37.3 + url: https://github:com/arco-design/arco-design-vue +highlightjs/vue-plugin: + cpeName: highlightjs/vue-plugin + version: 2.1.0 + url: https://github.com/highlightjs/vue-plugin +vueuse/core: + cpeName: vueuse/core + version: 7.3.0 + url: https://github.com/vueuse/vueuse +animate.css: + cpeName: animate.css + version: 4.1.1 + url: https://github.com/animate-css/animate.css +axios: + cpeName: axios + version: 0.27.2 + url: https://github.com/axios/axios +core-js: + cpeName: core-js + version: 3.8.3 + url: https://github.com/zloirock/core-js +dayjs: + cpeName: dayjs + version: 1.11.4 + url: https://github.com/iamkun/dayjs/ +echarts: + cpeName: echarts + version: 5.2.2 + url: https://github.com/apache/echarts +highlight.js: + cpeName: highlight.js + version: 11.6.0 + url: https://github.com/highlightjs/highlight.js/ +mitt: + cpeName: mitt + version: 3.0.0 + url: https://github.com/developit/mitt +nprogress: + cpeName: nprogress + version: 0.2.0 + url: https://github.com/rstacruz/nprogress +pinia: + cpeName: pinia + version: 2.0.17 + url: https://github.com/vuejs/pinia +query-string: + cpeName: query-string + version: 7.1.1 + url: https://github.com/sindresorhus/query-string +jsencrypt: + cpeName: jsencrypt + version: 3.0.0-rc.1 + url: https://github.com/travist/jsencrypt +vue: + cpeName: vue + version: 3.2.13 + url: https://github.com/vuejs/vue +vue-echarts: + cpeName: vue-echarts + version: 6.2.3 + url: https://github.com/ecomfe/vue-echarts +vue-grid-layout: + cpeName: vue-grid-layout + version: 3.0.0-beta1 + url: https://github.com/jbaysolutions/vue-grid-layout +vue-i18n: + cpeName: vue-i18n + version: 9.3.0-beta.6 + url: https://github.com/intlify/vue-i18n-next +vue-router: + cpeName: vue-router + version: 4.0.3 + url: https://github.com/vuejs/router +vue-winbox: + cpeName: vue-winbox + version: 0.1.0 + url: https://github.com/wobsoriano/vue-winbox +wujie-vue3: + cpeName: wujie-vue3 + version: 1.0.0 + url: https://github.com/Tencent/wujie +xterm: + cpeName: xterm + version: 4.19.0 + url: https://github.com/xtermjs/xterm.js diff --git a/ER-Model/openGauss-datakit/config/application-temp.yml b/ER-Model/openGauss-datakit/config/application-temp.yml new file mode 100644 index 000000000..67ac6b76d --- /dev/null +++ b/ER-Model/openGauss-datakit/config/application-temp.yml @@ -0,0 +1,50 @@ +system: + # File storage path + defaultStoragePath: /ops/files + # Whitelist control switch + whitelist: + enabled: false +server: + port: 9494 + ssl: + key-store: /ops/ssl/keystore.p12 + key-store-password: '******' + key-store-type: PKCS12 + enabled: true + servlet: + context-path: / +logging: + file: + path: /ops/logs/ +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + # For openGauss + # driver-class-name: org.opengauss.Driver + # url: jdbc:opengauss://ip:port/database?currentSchema=public&batchMode=off + # username: dbuser + # password: dbpassword + druid: + test-while-idle: true + test-on-borrow: true + validation-query: "select 1" + validation-query-timeout: 10000 + connection-error-retry-attempts: 0 + break-after-acquire-failure: true + max-wait: 300000 + keep-alive: true + max-active: 30 + min-evictable-idle-time-millis: 600000 +management: + server: + port: 9494 +plugins: + oauth-login: + # A unique identifier obtained by registering with DevKit + client-id: your_client_id + # A secret key paired with the client_id + client-secret: your_client_secret + datakit-url: https://ip:port + devkit-url: https://ip:port + # SSL certificate + ssl-key: your_ssl_key \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/config/settings.xml b/ER-Model/openGauss-datakit/config/settings.xml new file mode 100644 index 000000000..aea01b88f --- /dev/null +++ b/ER-Model/openGauss-datakit/config/settings.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + aliyun-maven + aliyun maven + http://maven.aliyun.com/nexus/content/groups/public/ + central + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/cov-analysis/pom.xml b/ER-Model/openGauss-datakit/cov-analysis/pom.xml new file mode 100644 index 000000000..16b273925 --- /dev/null +++ b/ER-Model/openGauss-datakit/cov-analysis/pom.xml @@ -0,0 +1,138 @@ + + + + org.opengauss + visualtool-parent + ${admin.version} + ../../pom.xml + + 4.0.0 + pom + cov-analysis + + + cov-analysis + + + UTF-8 + UTF-8 + 7.0.0-RC3 + 17 + ${java.version} + ${java.version} + true + + + + org.opengauss + visualtool-api + ${project.version} + + + org.opengauss + visualtool-service + ${project.version} + + + + org.opengauss + visualtool-common + ${project.version} + + + org.opengauss + jsch-session-pool + ${project.version} + + + org.opengauss + openGauss-datakit-agent + ${project.version} + + + org.opengauss + visualtool-framework + ${project.version} + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + + + + + org.apache.maven.plugins + maven-clean-plugin + ${maven-clean-plugin.version} + + + org.codehaus.mojo + versions-maven-plugin + ${versions-maven-plugin.version} + + false + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + ${java.home}/lib/rt.jar + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + merge-results + test + + merge + + + + + ${project.basedir}/../ + + ./**/target/jacoco.exec + + + + ${project.basedir}/target/aggregate.exec + + + + verify + + report-aggregate + + + + + + + + ${project.artifactId} + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/doc/DataKit Agent Interface Documentation.md b/ER-Model/openGauss-datakit/doc/DataKit Agent Interface Documentation.md new file mode 100644 index 000000000..1da88e8a2 --- /dev/null +++ b/ER-Model/openGauss-datakit/doc/DataKit Agent Interface Documentation.md @@ -0,0 +1,84 @@ +**openGauss DataKit Agent 文档** + +# **关于本手册** + +## **Agent介绍** + +DataKit 在用户添加管理服务器时,可完成服务器Agent安装和启动。DataKit通过给代理Agent服务下发热问你,并由代理Agent实现任务执行。 +任务类型包括:命令执行,数据采集等多种任务。具体任务定义通过任务模版方式实现。 +## **主要功能介绍** + +Agent主要包含以下几个功能特性: + +- 通过服务器管理安装卸载Agent代理 +- 任务管理,负责任务定义、存储、下发以及任务执行状态 +- 任务执行引擎,支持多种类型任务执行与调度 +- 数据采集,通过配置采集任务,从不同数据源采集指定数据并返回到DataKit分类存储 + +## **预期读者** + +- 用户 +- 测试人员 +- 开发人员 + +## **版本及功能列表** + +| **版本号** | **新增功能** | **发布时间** | +|-----------| --- |-----------| +| V7.00-RC2 | 首次发布 | 2025-9-30 | + +## **Agent 任务配置规则** + +### **概述** + +DataKit根据任务配置规则,创建响应的任务模版。根据任务模版绑定对应Host或者DB实例,从而创建具体执行任务实例。在DataKit启动时将任务实例下发给相关代理Agent进程,完成任务执行。 + +### **任务模板定义** +#### 采集任务名称 +任务名称不可重复,约定一个采集任务采集一组相关指标数据,并安装一致的存储策略存储在对应表中。 +#### 采集任务类型 + +任务类型包含OSHI固定指标任务,OSHI动态指标任务,OS指标任务,DB指标任务,OS管道任务,DB管道任务 +- OSHI固定指标任务,通过OSHI采集服务器固定指标任务 +- OSHI动态指标任务,通过OSHI采集服务器动态指标任务 +- OS指标任务,通过配置OS命令采集OS相关指标任务 +- DB指标任务,通过配置数据库示例,通过SQL采集数据库相关数据 +- OS管道任务,通过配置Linux命令,采集相关指定数据 +#### 采集任务所属分组 +对运行在Agent的采集任务进行分组。待设计 +#### 采集任务所属插件 +当前任务归属插件ID或者平台基座 +#### 采集任务操作对象类型 +任务执行对象类型,包括OSHI,OS,DB,HTTP +#### 采集任务操作对象 +获取任务数据需要执行命令,可以是OS命令,查询SQL/explain语句,或者部分操作数据库命令(白名单) +- OS:sh xx.sh /python yy.py/ top / free -g +- DB :JDBC执行对应SQL,仅支持select/explain +- OSHI: Agent内置OSHI框架,并提供部分操作系统指标数据采集实现 +- HTTP:实现数据接收以及转发处理。提供固定数据接收地址,以及默认转发地址,并运行任务自定义转发地址 +#### 采集任务频率 +采集频率为任务执行间隔,默认单位秒,使用ISO-8601持续时间规则表示 +- 频率>=0 执行单位为妙,eg. P1Y2M3W4DT5H6M7.5S +- 频率==0 任务为一次性任务,执行完任务后立即退出 eg. PT0S +- 频率==-1 任务启动后不主动退出,常驻内存,直到用户主动关闭任务 eg. PT-1S +#### 采集任务上报指标 +上报指标定义了当前任务指定采集的数据字段名称,数据描述信息,数据类型,数据单位,采集命令,属性等信息。 +模板定义的采集字段详细信息通过t_task_schema_definition表绑定之间的关系。 +#### 采集任务上报数据存储策略 +采集任务数据存储策略包括,自定义存储策略,实时存储策略,历史存储策略,指纹存储策略,树状存储策略 + +- 自定义存储策略:开发者自行增加数据处理逻辑,实现数据处理。 +- 实时存储策略:保持一个周期内的实时采集任务数据,不记录历史数据。指标任务数据存储在固定表agent_metric_real_time中,管道任务表根据任务动态生成相应实时数据存储表。 +- 历史存储策略:保存一定历史周期内是采集任务数据。指标任务数据存储在固定表agent_metric_historical中,管道任务根据任务名称动态生成对应的历史表 +- 指纹去重策略:任务收集数据按照行表存储,并增加指纹去重算法。去重必须指定相关字段名称。(待开发) +- 树状结构存储策略:动态创建专属任务数据表(待开发) +#### 采集任务上报数据保留周期 +用于定期清理历史类型任务表存储的过期数据。配置格式采用ISO-8601持续时间规则表示。 + +#### 采集任务上报数据接收地址 +数据接收地址,可选填。开发者指定其他地址,需自行处理数据接收以及数据处理逻辑。 +当前支持的数据接收地址包括: +- /receive/fixed/host/info : 默认接收Host固定指标数据,并持久化到Host基本信息表 +- /receive/metrics : 默认接收DataKitOTel数据,并将指标数据存储到任务指定表中 +- /receive/pipeline : 默认接收DataKit可变类型数据,并将数据存储到任务指定表中。 + diff --git a/ER-Model/openGauss-datakit/doc/DataKit Configuration Documentation.md b/ER-Model/openGauss-datakit/doc/DataKit Configuration Documentation.md new file mode 100644 index 000000000..38243b1ad --- /dev/null +++ b/ER-Model/openGauss-datakit/doc/DataKit Configuration Documentation.md @@ -0,0 +1,272 @@ +## 集成配置 + +项目基座代码已经配置完成,无需改动。 + +### 引入repository声明 + +```XML + + + gitee-eb-repo + The Maven Repository on Gitee + https://gitee.com/agile-adept-team/spring-brick-eb/tree/master/maven-repo + + +``` + +### 引入框架依赖 + +```XML + + com.gitee.starblues + spring-brick + 3.1.0 + +``` + +### 配置文件加入配置 + +```YAML +plugin: + mainPackage: org.opengauss.admin + runMode: dev + pluginPath: + - ~/visualtool-plugin +``` + +> `mainPackage`: 主程序最大范围包名。 +> +> ``` +> runMode`: 运行模式。可配置`dev`/`prod +> ``` +> +> `pluginPath`: 插件目录或者插件上级目录,可配置多个。 + +### Spring-Boot 启动类改造 + +```Java +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class AdminApplication implements SpringBootstrap { + public static void main(String[] args) { + SpringMainBootstrap.launch(AdminApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 服务启动成功 ლ(´ڡ`ლ)゙"); + } + + @Override + public void run(String[] args) throws Exception { + // 在该实现方法中, 和SpringBoot使用方式一致 + SpringApplication.run(AdminApplication.class, args); + } +} +``` + +### 配置maven打包 + +```XML + + + + com.gitee.starblues + spring-brick-maven-packager + 3.1.0 + + main + + org.opengauss.admin.AdminApplication + jar + + + + + + repackage + + + + + + org.apache.maven.plugins + maven-clean-plugin + + + org.codehaus.mojo + exec-maven-plugin + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.0 + + + copy-web + generate-resources + + copy-resources + + + ${basedir}/src/main/resources + + + ../visualtool-ui/dist + static + + + + + + + + ${project.artifactId} + +``` + +## 插件分类 + +### **增强插件** + +不能独立运行的插件,而是对现有某功能的增强,包括数据和接口实现,因此又可以细分为接口扩展和数据扩展。插件调用通常是在某个业务选项列表中增加可选项,选择后进行调用或者使用。 + +> **接口扩展**:插件本身包含数据和业务逻辑,数据通常在插件安装时添加到主程序的数据表中,业务逻辑在主程序中类似于service方法一样的调用。 +> +> **数据扩展**:插件只包含数据,在插件安装时添加到主程序的数据表中。比如通过插件添加某一类数据资源等。 + +### **功能插件** + +能独立运行的插件,独立实现某个完整的功能或操作,比如某某模块。插件调用通常是在系统内添加菜单入口。 + +## 增强扩展 + +### 接口扩展 + +#### 定义接口 + +定义将来需要进行扩展的业务接口,比如示例中的通知接口,为了示例的简洁,已省略注释,如下: + +```Java +public interface NoticeExtract { + public void send(NoticeDTO noticeDTO); +} +``` + +#### 实现接口 + +接口实现分类两类,定义如下: + +> 默认实现:在系统中实现插件接口中各个方法。 +> +> 扩展实现:在插件中实现插件接口中的各个方法,进行扩展。 + +不管是哪一类的实现,都需要在类上使用注解进行标记,标记的目的是为了在容器中能定位和调用到到该实现类。 + +##### 注解 + +@Extract(bus = "业务标识", scene="场景标识", useCase="用例标识") + +> bus:业务标识(必填),比如notice +> +> scene:场景标识(选填),比如smsNotice +> +> useCase:用例标识(选填),比如smsNotice + +##### 案例 + +**默认自带实现,短信通知** + +```Java +@Service +public class SmsNoticeImpl implements NoticeExtract { + + @Override + public void send(NoticeDTO noticeDTO) { + System.out.println("通过短信发送通知,电话号码是:" + noticeDTO.getTelephone() + ",消息内容是:" + noticeDTO.getContent()); + } +} +``` + +**插件扩展实现,邮件通知** + +```Java +@Extract(bus = "notice", scene = "emailNotice") +public class EmailNoticeServiceImpl implements NoticeExtract { + + @Override + public void send(NoticeDTO noticeDTO) { + System.out.println("通过邮件发送通知,地址是是:" + noticeDTO.getEmail() + ",消息内容是:" + noticeDTO.getContent()); + } +} +``` + +#### 插件调用 + +默认实现调用,可以通过@Autowired注入 + +```Java +public class NoticeController { + + //注入默认实现 + @Autowired + private NoticeExtract noticeExtract; + + @GetMapping("/send") + @ApiOperation(value = "发送通知", notes = "发送通知") + public AjaxResult send() { + NoticeDTO noticeDTO = new NoticeDTO(); + noticeDTO.setContent("你好,发送通知"); + noticeDTO.setTelephone("17308404741"); + noticeDTO.setEmail("liboxiex@gmail.com"); + noticeExtract.send(noticeDTO); + return AjaxResult.success(); + } +} +``` + +调用后的结果如下: + +> 通过短信发送通知,电话号码是:17308404741,消息内容是:你好,发送通知 + + + +插件实现调用,不能通过@Autowired或者@Resource直接注入NoticeExtract。而是注入ExtractFactory,通过ExtractFactory传入实现类的标识,通过标识定位到具体的实现并且调用。示例: + +```Java +public class NoticeController { + + @Autowired + private ExtractFactory extractFactory; + + //默认实现 +// @Autowired +// private NoticeExtract noticeExtract; + + @GetMapping("/send") + @ApiOperation(value = "发送通知", notes = "发送通知") + public AjaxResult send() { + String bus = "notice"; + String scene = "emailNotice"; +// 调用插件实现 + NoticeExtract noticeExtract = extractFactory.getExtractByCoordinate(ExtractCoordinate.build(bus, scene, "")); + NoticeDTO noticeDTO = new NoticeDTO(); + noticeDTO.setContent("你好,发送通知"); + noticeDTO.setTelephone("17308404741"); + noticeDTO.setEmail("liboxiex@gmail.com"); + noticeExtract.send(noticeDTO); + return AjaxResult.success(); + } +} +``` + +调用结果如下: + +> 通过邮件发送通知,地址是:liboxiex@gmail.com,消息内容是:你好,发送通知。 + + + +### 数据扩展 + +提供对应的数据添加和删除方法,比如服务器资源信息未来需要扩展,则创建服务器资源service,并且增加save和delete方法。在插件的安装、启动、停止、卸载时调用save和delete方法进行数据操作。 + +> **save方法可以避免数据重复添加,delete方法可以根据非主键字段删除。** + +## 功能扩展 + +功能扩展原则上没有对主程序的强依赖,只需要遵循插件开发手册,开发完整功能即可。 \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/doc/DataKit Dev Setup.md b/ER-Model/openGauss-datakit/doc/DataKit Dev Setup.md new file mode 100644 index 000000000..b7e967e60 --- /dev/null +++ b/ER-Model/openGauss-datakit/doc/DataKit Dev Setup.md @@ -0,0 +1,144 @@ +# OpenGauss Datakit 开发环境搭建流程 + +## 推荐环境 +- java 17+ +- maven 3.9.0+ +- node v18+(含npm) + +此外,为保证依赖下载顺利,需配置好maven镜像源和node镜像源。 + +## 后端 + +### 工具 +- IDE:Intellij IDEA 2023.3.3 +- OS:OpenEuler 22.03 +- JDK:Java version: 17.0.10, vendor: Oracle Corporation +- Maven:3.9.0+ + +### 准备 +DataKit版本7.0.0-RC2开始,项目基础平台升级为Springboot 3.5.6,在项目编译前需本地编译且安装SpringBrick组件。 +1、下载SpringBrick项目源码,命令如下: +``` +git clone https://gitcode.com/wang4721/springboot-plugin-framework-parent.git +``` +2、进入springboot-plugin-framework-parent项目根目录,使用Maven进行编译安装 +``` +mvn clean install -Dmaven.test.skip=true +``` +3、执行成功后,从gitcode上下载DataKit项目,命令如下: +``` +git clone https://gitcode.com/opengauss/openGauss-workbench.git +``` + +安装好OpenGauss,详细步骤见[README.md](https://gitee.com/opengauss/openGauss-workbench/tree/master/README.md)。将整个openGauss-workbench文件夹导入IDEA。想调试插件的话,应该先运行一次根目录的`build.sh`,命令如下: +```shell +sh build.sh +``` + +### visualtool-api Debug流程 +项目以`visualtool-*`为核心框架,Debug流程时启动这个核心框架后再动态加载`plugins`下的各个插件。 + +要启动核心框架,需更改dev环境的配置文件,它在`openGauss-datakit/visualtool-api/src/main/resources/`下,也就是`application-dev.yml`文件,修改其中的url, username, password等字段,示例如下: +```ymal +system: + defaultStoragePath: /ops/debug_files + whitelist: + enabled: false +server: + port: 9494 + servlet: + context-path: / +logging: + file: + path: ./logs/ +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + # For openGauss + driver-class-name: org.opengauss.Driver + url: jdbc:opengauss://127.0.0.1:5432/db_datakit?currentSchema=public&batchMode=off + username: opengauss_test + password: Sample@123 +swagger: + enabled: true + pathMapping: /dev-api +plugin: + sortInitPluginIds: + decrypt: + enable: false + className: com.gitee.starblues.common.cipher.AesPluginCipher + plugins: + - pluginId: example-basic-1 + props: + secretKey: mmfvXes1XckCi8F/y9i0uQ== + version: 0.0.0 +``` + +配好`application-dev.yml`就可以调试了,这时用`visualtool-api`中的`AdminApplication.java`中的`org.opengauss.admin.AdminApplication`类启动调试即可。 + +### plugins Debug流程 +以上只启动了`visualtool-*`这个核心框架,想要调试`plugins`文件夹下的各个插件,则需要在项目根目录下创建`visualtool-plugin`文件夹,将`sh build.sh`生成的各插件的jar拖到这个文件夹下,重新启动框架即可。 + +### 前后端联调 +如果您有前后端联调的需求,同时也为方便起见,应在`openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/SecurityConfig.java`这个文件中, +- 修改`.anyRequest().authenticated()`字段为`.anyRequest().permitAll()` +- 修改`.headers().frameOptions().disable()`为`.headers().disable()` + +示例如下: +```java +... + httpSecurity + ... + .antMatchers("/prometheus").permitAll() +// .anyRequest().authenticated() + .anyRequest().permitAll() + .and() +// .headers().frameOptions().disable(); + .headers().disable(); +... +``` +这样修改后并非意味着一定不需要密码登录。这样做会有以下效果 +- 前端各个插件`yarn dev`时可以连接到后端,否则无法连接(需要登录) +- 前端访问一些涉及到查询登录用户的个人信息的api时仍然会无法访问,因为没有登录,但是绝大部分前端功能还是能用的 +- 访问后端`visualtool-ui`的`:9494`这个端口时还是会进入登录界面 +- 在修改代码重新运行后端的情况下,不刷新浏览器界面(即F5或者浏览器地址栏Enter一下)则不需要重新登录,刷新界面则需要在浏览器控制台运行如下命令清空一下localstorage: + ```javascript + localStorage.clear(); + ``` + 否则会卡在某个地方,建议遇到任何未知错误时都`localStorage.clear();`一下 + +至此,后端环境搭建部分告一段落。 + +## 前端 +### 工具 +- Node:v18.19.0 +- npm:10.2.3 +- yarn:1.22.21 +### 前端环境搭建 +前端调试需要先启动相应功能的后端服务。这里以调试base_ops插件的前端为例,后端自然需要将`base-ops-6.0.0-repackage.jar`放入`visualtool-plugin`中,同时运行以下命令: +```shell +cd plugins\base-ops\web-ui +# 执行npm install安装依赖 +npm install +# 然后执行 npm run dev 或者 yarn dev 启动前端服务 +yarn dev +``` +启动完成后,执行窗口会输出Local地址,在本例中为http://localhost:8081/static-plugin/base-ops/,然后就可以登录调试了。 + +### 可能遇到的问题 +``` +Watchpack Error (watcher): Error: ENOSPC: System limit for number of file watchers reached, watch 'xxxx' +``` +参考StackOverflow上这个帖子:[React Native Error: ENOSPC: System limit for number of file watchers reached](https://stackoverflow.com/questions/55763428/react-native-error-enospc-system-limit-for-number-of-file-watchers-reached)。 + +解决办法是更改系统设置如下: +```shell +# insert the new value into the system config +echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p + +# check that the new value was applied +cat /proc/sys/fs/inotify/max_user_watches + +# config variable name (not runnable) +fs.inotify.max_user_watches=524288 +``` \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/doc/DataKit Main Program Front-end Development Specification Document.md b/ER-Model/openGauss-datakit/doc/DataKit Main Program Front-end Development Specification Document.md new file mode 100644 index 000000000..dd2d8b9e7 --- /dev/null +++ b/ER-Model/openGauss-datakit/doc/DataKit Main Program Front-end Development Specification Document.md @@ -0,0 +1,141 @@ +## 技术选型 + +- vue3 +- 构建工具vue-cli +- UI框架arco.design +- 路由vue-router +- 状态管理器pinia +- 图表echarts + +## 开发与部署 + +推荐使用yarn包管理工具 + +### **安装依赖** + +```Bash +yarn +``` + +### **开发环境** + +```Bash +# 配置.env.development中的VUE_APP_BASE_API,默认为访问接口地址的/dev-api +# 访问本地后端服务,需修改vue.config.js的proxy的target地址 +yarn dev +``` + +### **生产构建** + +```Bash +yarn build +``` + +### **代码校验** + +```Bash +yarn lint +``` + +## 开发规范 + +### **开发工具** + +- 统一使用vs code开发 +- 前端依赖包安装使用yarn,镜像源使用淘宝镜像 +- 编码时tab统一为2个空格 + +### **命名规范** + +- 组件目录使用小驼峰命名方式,多单词拼接首字母写,如:userInfo +- 组件文件名使用大驼峰命名方式,多单词拼接首字母写,如:Manage.vue +- 变量名、方法名遵循驼峰命名规则:多单词拼接首字母写,如:userInfo +- 变量声明(let,const),定义常量使用const,定义变量尽量使用let + +### **使用ts es6风格进行编码** + +- 解构赋值 +- 箭头函数 +- 正确使用模块,如果模块只有一个输出值,就使用 export default,如果模块有多个输出值,就不使用 export default,export default 与普通的 export 尽量不要同时使用 +- 多个异步操作时使用Promise对象进行封装 +- 除了三目运算,if,else 等禁止简写 +- 有ts红色警告的地方需要进行处理 + +### **注释规则** + +- 公共组件的使用说明 +- 各组件中响应变量、重要函数或者类的说明 +- 复杂的业务逻辑处理说明 +- 已注释掉的代码要说明注释原因 +- 多重 if 判断语句需添加说明 +- 注释块必须以/**多行说明 */,单行注释使用// + +### **指令规范** + +- v-for 循环必须加上 key 属性,在整个 for 循环中 key 需要唯一 +- 避免 v-if 和 v-for 同时用在一个元素上 +- props 定义应该尽量详细 + +### **目录说明** + +- api 前端接口目录,其中interceptor.ts为统一拦截器 +- assets 静态文件如图片及公共样式,icons为svg图标,images是png图片目录,style为公共样式 +- components 公共组件目录 +- config 全局配置,主题、菜单获取方式、菜单宽度、菜单是否默认收起等 +- hooks 公共钩子方法 +- layout 布局组件 +- router 前端路由,菜单若采用了后端接口获取,此目录作为前端开发时使用的路由配置 +- store 状态管理目录,使用的是pinia +- types ts类型配置目录 +- utils 公共工具目录 +- views 页面目录,按文件夹划分,单个页面中使用的业务组件,需新建components文件夹放在其中 + +### **样式规范** + +- 界面级别vue组件使用统一dom结构 + +```HTML + + + + +``` + +- 组件的dom结构 + +```HTML + + + + +``` + +- 颜色值统一用css变量,不另外写色值,便于颜色的统一管理 +- 自定义的css变量统一在src/assets/style/global.less文件里面管理 + +### **提交规范** + +- 提交前先更新代码 +- 提交前执行yarn lint进行代码格式校验 +- 提交说明须简要描述当次提交的内容 \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/doc/DataKit Open Interface Documentation.md b/ER-Model/openGauss-datakit/doc/DataKit Open Interface Documentation.md new file mode 100644 index 000000000..73aabbe5c --- /dev/null +++ b/ER-Model/openGauss-datakit/doc/DataKit Open Interface Documentation.md @@ -0,0 +1,302 @@ +# 菜单接口 + +用于在插件安装和卸载是添加、删除插件自身菜单。 + +## **接口路径** + +org.opengauss.admin.system.plugin.facade.MenuFacade + +## **方法** + +### savePluginMenu + +添加菜单以及前端路由 + +**入参** + +| 参数名 | 类型 | 是否必填 | 说明 | +| ---------- | ------- | -------- | ------------------------------------------------- | +| pluginId | String | 是 | 插件标识,需要和pom中下的id保持一致。 | +| menuName | String | 是 | 菜单名称 | +| menuEnName | String | 是 | 菜单英文名称 | +| order | Integer | 是 | 菜单排序 | +| path | String | 否 | 菜单路由路径,默认为index。 | +| parentId | Integer | 否 | 父菜单,默认为根目录。 | +| openWay | Integer | 否 | 打开方式,暂时仅支持页面打开,默认为1。 | + +**出参** + +返回MenuVo对象,详情见下文。 + +**调用案例** + +```Java +//插件监听事件中获取Menufacade,仅显示相关代码 +MainApplicationContext context = ((ApplicationReadyEvent) event).getApplicationContext().getBean(MainApplicationContext.class); +SpringBeanFactory factory = context.getSpringBeanFactory(); +MenuFacade menuFacade = factory.getBean(MenuFacade.class); +menuFacade.savePluginMenu("plugin-book","图书管理插件","books manager","index",0); +``` + +### deletePluginMenu + +根据pluginId删除该插件下所有菜单与路由数据。 + +**入参** + +| 参数名 | 类型 | 是否必填 | 说明 | +| -------- | ------ | -------- | ------------------------------------------------- | +| pluginId | String | 是 | 插件标识,需要和pom中下的id保持一致。 | + +**出参** + +返回MenuVo对象,详情见下文。 + +**调用案例** + +```Java +//插件监听事件中获取Menufacade,仅显示相关代码 +MainApplicationContext context = ((ContextClosedEvent) event).getApplicationContext().getBean(MainApplicationContext.class); +SpringBeanFactory factory = context.getSpringBeanFactory(); +MenuFacade menuFacade = factory.getBean(MenuFacade.class); +menuFacade.deletePluginMenu("plugin-book"); +``` + +### savePluginRoute + +添加插件非菜单路由。 + +**入参** + +| 参数名 | 类型 | 是否必填 | 说明 | +| ---------- | ------- | -------- | ------------------------------------------------------------ | +| pluginId | String | 是 | 插件标识,需要和pom中下的id保持一致。 | +| menuName | String | 是 | 路由名称 | +| menuEnName | String | 是 | 路由英文名称 | +| path | String | 否 | 路由路径,默认为index,如需传递参数,则使用query的方式传参,并使用,例如index?id=${id}。 | +| parentId | Integer | 否 | 父菜单,默认为根目录。 | + +**出参** + +返回MenuVo对象,详情见下文。 + +### saveIndexInstanceRoute + +添加首页实例路由。 + +**入参** + +| 参数名 | 类型 | 是否必填 | 说明 | +| -------- | ------ | -------- | ------------------------------------------------------------ | +| pluginId | String | 是 | 插件标识,需要和pom中下的id保持一致。 | +| menuName | String | 是 | 菜单名称 | +| path | String | 否 | 菜单路由路径,默认为index,如需传递参数,则使用query的方式传参,并使用,例如index?id=${id}。 | + +**出参** + +返回MenuVo对象,详情见下文。 + +**调用案例** + +**调用案例** + +```Java +//插件监听事件中获取Menufacade,仅显示相关代码 +MainApplicationContext context = ((ContextClosedEvent) event).getApplicationContext().getBean(MainApplicationContext.class); +SpringBeanFactory factory = context.getSpringBeanFactory(); +MenuFacade menuFacade = factory.getBean(MenuFacade.class); +menuFacade.saveIndexInstanceRoute("moniter-plugin","实例监控插件","index"); +``` + +# 插件接口 + +用于获取插件本身相关的数据。 + +## 接口路径 + +org.opengauss.admin.system.plugin.facade.PluginFacade + +## 方法 + +### getPluginConfigData + +获取插件配置数据 + +**入参** + +| 参数名 | 类型 | 是否必填 | 说明 | +| -------- | ------ | -------- | ------------------------------------------------- | +| pluginId | String | 是 | 插件标识,需要和pom中下的id保持一致。 | + +**出参** + +返回json对象字符串,其中key为插件jar包中配置的**attrCode**。示例: + +```JSON +{ + + "esHost": "192.168.1.104", + "esPort": "23552" +} +``` + +# openGauss实例接口 + +用于获取openGauss集群及实例信息 + +## 接口路径 + +org.opengauss.admin.system.plugin.facade.OpsFacade + +## 方法 + +### listCluster + +查询集群列表 + +**入参** + +无 + +**出参** + +List 对象,详见对象结构说明。 + +# WebScoket接口 + +用于通过主程序向客户端推送消息。 + +## 接口路径 + +org.opengauss.admin.system.plugin.facade.WsFacade + +## 方法 + +### sendMessage + +推送消息到WebSocket客户端。 + +**入参** + +| 参数名 | 类型 | 是否必填 | 说明 | +| --------- | ------ | -------- | ----------------------------------------------- | +| pluginId | String | 是 | 插件标识,需要和pom中下的id保持一致 | +| sessionId | String | 是 | 会话ID | +| message | String | 是 | 消息内容 | + +# JDBC实例接口 + +用于获取JDBC集群及实例信息 + +## 接口路径 + +org.opengauss.admin.system.plugin.facade.JdbcDbClusterFacade + +## 方法 + +### page + +查询集群列表 + +**入参** + +name : 模糊搜索关键字 + +page: 分页参数 + +**出参** + +Page 对象,详见对象结构说明。 + +# 物理机资源列表 + +用于获取物理机资源信息 + +## 接口路径 + +org.opengauss.admin.system.plugin.facade.HostFacade + +## 方法 + +### listAll + +获取物理机资源列表 + +**入参** + +无 + +**出参** + +List 对象,详见对象结构说明。 + +# 对象结构说明 + +**MenuVo** + +| 属性名 | 说明 | +| -------- | -------------- | +| menuId | 菜单或路由ID | +| menuName | 菜单及路由名称 | +| parentId | 父级ID | + +**OpsClusterVO** + +| 属性名 | 说明 | +| ---------------- | ------------------------------------------------------- | +| clusterId | 集群标识 | +| clusterName | 集群名称 | +| version | 集群版本 ENTERPRISE企业版/MINIMAL_LIST极简版/LITE轻量版 | +| versionNum | 集群版本号 | +| databasePassword | 集群密码 | +| clusterNodes | 集群节点信息 | + +**OpsClusterNodeVO** + +| 属性名 | 说明 | +| -------------- | ------------ | +| nodeId | 节点标识 | +| clusterRole | 节点角色 | +| publicIp | 公网IP | +| privateIp | 内网IP | +| hostname | 主机名 | +| azName | AZ名 | +| dbPort | 数据库端口 | +| dbName | 数据库名 | +| dbUser | 数据库用户名 | +| dbUserPassword | 数据库密码 | +| hostPort | ssh端口 | +| rootPassword | root用户密码 | + +**JdbcDbClusterVO** + +| 属性名 | 说明 | +| ---------- | ------------------------------ | +| clusterId | 集群ID | +| name | 集群名称 | +| deployType | 部署类型:参考DeployTypeEnum | +| dbType | 数据库类型: 参考DbTypeEnum | +| nodes | 节点,参考 JdbcDbClusterNodeVO | + +**JdbcDbClusterNodeVO** + +| 属性名 | 说明 | +| ------------- | ---------------- | +| clusterNodeId | 节点ID | +| name | 节点名称 | +| ip | 节点ip地址 | +| port | 节点端口 | +| username | 数据库连接用户名 | +| password | 数据库连接密码 | +| url | 数据库连接串 | + +**OpsHostEntity** + +| 属性名 | 说明 | +| --------- | ------------ | +| hostId | 主机ID | +| hostname | 主机hostname | +| privateIp | 内网ip地址 | +| publicIp | 公网ip地址 | +| port | ssh端口 | +| azId | 所属AZ的ID | \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/doc/DataKit Plugin Development Documentation.md b/ER-Model/openGauss-datakit/doc/DataKit Plugin Development Documentation.md new file mode 100644 index 000000000..c344fec71 --- /dev/null +++ b/ER-Model/openGauss-datakit/doc/DataKit Plugin Development Documentation.md @@ -0,0 +1,838 @@ +# 插件分类 + +**增强插件**:不能独立运行的插件,而是对现有某功能的增强,包括数据和接口实现,因此又可以细分为接口扩展和数据扩展。插件调用通常是在某个业务选项列表中增加可选项,选择后进行调用或者使用。 + +> **接口扩展**:插件本身包含数据和业务逻辑,数据通常在插件安装时添加到主程序的数据表中,业务逻辑在主程序中类似于service方法一样的调用。 +> +> **数据扩展**:插件只包含数据,在插件安装时添加到主程序的数据表中。比如通过插件添加某一类数据资源等。 + +**功能插件**:能独立运行的插件,独立实现某个完整的功能或操作,比如某某模块。插件调用通常是在系统内添加菜单入口。 + +# 插件开发 + +## 集成配置 + +### 引入依赖 + +#### 引入SpringBoot依赖 + +- 如果插件的`spring-boot`版本与主程序**一致**,则引入如下依赖 + +```XML + + + org.springframework.boot + spring-boot-starter + 2.5.6 + +``` + +- 如果插件的`spring-boot`版本与主程序**不一致**,则引入如下依赖 + +```XML + + + org.springframework.boot + spring-boot-starter + ${spring-boot.version} + + + + org.springframework.boot + spring-boot-starter-aop + ${spring-boot.version} + +``` + +**推荐跟主程序的springboot保持一致版本!** + +#### 引入插件依赖 + +```XML + + + com.gitee.starblues + spring-brick-bootstrap + 3.1.0 + +``` + +#### 引入主程序依赖 + +```XML + + + org.opengauss + visualtool-service + 6.0.0 + + + + com.alibaba + druid-spring-boot-3-starter + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + org.springframework.boot + spring-boot-starter-data-redis + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + +``` + +#### 定义插件引导类 + +> 注意:插件引导类包必须在主程序的包名之下,主程序的包名是org.opengauss.admin,因此插件的引导类必须在org.opengauss.admin.xxx之下。 + +定义插件`main`入口类, 继承`SpringPluginBootstrap`类, 然后在`main`函数中实例化当前引导类,并执行`run`方法即可。实现如下: + +```Java +//如果不需要操作数据库和redis,则关闭掉相关的自动装配 +@SpringBootApplication(exclude={ + DataSourceAutoConfiguration.class, + HibernateJpaAutoConfiguration.class, + RedisAutoConfiguration.class, + RedisRepositoriesAutoConfiguration.class +}) +public class EmailNoticeApplication extends SpringPluginBootstrap { + + public static void main(String[] args) { + new EmailNoticeApplication().run(args); + System.out.printf("邮件通知插件启动成功"); + } +} +``` + +## 接口扩展插件开发 + +实现主程序中定义的接口,并在各个方法中完成相应的业务逻辑,同时在类上面加上注解@Extract,配置该插件独有的标识,该标识在主程序中调用时需要用到。 + +@Extract(bus = "业务标识", scene="场景标识", useCase="用例标识") + +> bus:业务标识(必填),比如notice +> +> scene:场景标识(选填),比如smsNotice +> +> useCase:用例标识(选填),比如smsNotice + +案例: + +```Java + +@Extract(bus = "notice", scene = "emailNotice") +public class EmailNoticeServiceImpl implements NoticeExtract { + + @Override + public void send(NoticeDTO noticeDTO) { + System.out.println("通过邮件发送通知,地址是是:" + noticeDTO.getEmail() + ",消息内容是:" + noticeDTO.getContent()); + } +} +``` + +## 数据扩展插件开发 + +1. 定义监听类,在启动和停止事件中,调用主程序菜单接口的添加和删除方法。菜单接口说明详见文档[主程序开放接口](https://fullstack-dao.feishu.cn/docx/doxcnIa9e0ChR4bJWlx4IyBfzjf) + +> 在Listener中无法使用@Autowired或者@Resource注入业务操作service,因此只能通过注入主程序Bean的第三种方式获取。 + +案例: + +```Java +public class ImplListener implements ApplicationListener { + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ApplicationEnvironmentPreparedEvent) { + System.out.println("扩展实现初始化环境变量"); + } else if (event instanceof ApplicationPreparedEvent) { + System.out.println("扩展实现环境初始化完成"); + } else if (event instanceof ContextRefreshedEvent) { + System.out.println("扩展实现ApplicationContext被刷新"); + } else if (event instanceof ApplicationReadyEvent) { + MainApplicationContext context = ((ApplicationReadyEvent) event).getApplicationContext().getBean(MainApplicationContext.class); + SpringBeanFactory factory = context.getSpringBeanFactory(); + ISysPluginResourceService service = factory.getBean(ISysPluginResourceService.class); + SysPluginResource resource = new SysPluginResource(); + resource.setResourceName("plugin-impl数据资源"); + resource.setIp("127.0.0.1"); + resource.setUserName("xielibo"); + resource.setUserPassword("123123"); + service.savePluginResource(resource); + System.out.println("扩展实现插件已经启动完成"); + } else if (event instanceof ContextClosedEvent) { + MainApplicationContext context = ((ApplicationReadyEvent) event).getApplicationContext().getBean(MainApplicationContext.class); + SpringBeanFactory factory = context.getSpringBeanFactory(); + ISysPluginResourceService service = factory.getBean(ISysPluginResourceService.class); + SysPluginResource resource = new SysPluginResource(); + resource.setId(1); + service.deletePluginResource(resource); + System.out.println("扩展实现插件停止"); + } + } +} +``` + +1. 配置监听类 + +```YAML +context: + listener: + classes: com.fsd.admin.plugins.listener.ImplListener +``` + +**注意:主程序中的service必须有显性的save和delete方法,并且save方法可以避免数据重复添加,delete方法可以根据非主键字段删除。** + +安装/启用插件后将会调用save方法添加数据到对应的表中,停止/卸载插件将会调用delete方法将数据删除。 + +## 功能插件开发 + +功能插件是运行在主程序上的独立功能的插件,是具备完整业务操作的模块。此类插件包括前端代码,并且打包在jar包运行。 + +### 前端技术选型 + +建议使用vue,构建工具使用vue-cli。因vite暂不稳定,使用vite带来的部署问题平台不提供支持。 + +**前端插件中避免使用以下第三方库/插件/工具:** + +```Bash +# 构建工具 +vite + +# 第三方插件 +monaco-editor +``` + +### 前端主题切换 + +**平台默认为亮色模式** + +设置深色模式时,通过**localStorage.setItem('opengauss-theme', 'dark')**存储深色主题的参数到浏览器的localStorage中,插件可以通过**localStorage.getItem('opengauss-theme')**获取,如果值为dark字符串,则是深色模式,否则为亮色模式。 + +**监听平台主题变化** + +平台切换主题时会触发bus事件,插件页面通过bus事件监听获取相应的主题值变化来进行插件的主题调整,监听方法如下: + +```JavaScript +// 监听 opengauss-theme-change 主题切换事件 +window.$wujie?.bus.$on('opengauss-theme-change', val => { + console.log(val) +}) +``` + +### 前端国际化 + +**平台使用vue-i18n进行国际化** + +插件需要进行国际化时,可以直接通过**localStorage.getItem('locale')**获取平台当前的语言进行插件的国际化处理,建议也使用vue-i18n插件进行国际化,与平台使用的插件保持一致 + +**监听平台语言变化** + +平台切换语言时会触发bus事件,插件页面通过bus事件监听获取相应的语言值变化来进行插件的语言调整,监听方法如下: + +```JavaScript +// 监听 opengauss-locale-change 语言切换事件 +window.$wujie?.bus.$on('opengauss-locale-change', val => { + console.log(val) +}) +``` + +### 插件间页面跳转示例 + +在插件里面获取主应用的jump方法进行跳转 + +```JavaScript +// jump方法的参数是一个route对象 +window.$wujie?.props.methods.jump({ + name: `${路由name}`, + query: { + xxx: 'abc' + } +}) +``` + +**${路由name}参数**为:固定前缀(Static-plugin+插件ID)+插件路由路径分段的首字母大写。 + +**例如**: + +跳转插件路由为/ebbc/abc/get,跳转插件ID为check,则此处参数为:Static-pluginCheckEbbcAbcGet + +给插件传参通过url的query参数进行传递,在插件中按下面的方法进行获取 + +```JavaScript +window.$wujie?.props.data.xxx +``` + +**注:**当跳转插件中的非菜单页面时,比如插件的某个详情页,需要将此详情页的路由通过主程序的savePluginRoute接口添加为**隐藏菜单路由**,否则无法跳转。 + +### 前端资源配置 + +在插件中的配置文件中配置静态资源所在目录 + +```YAML +spring: + resources: + static-locations: classpath:static +``` + +### 前端访问路径 + +**静态资源的访问规则为:****`http://ip:port/static-plugin/插件id/具体插件的资源路径`** + +> 插件的前端访问需要结合主程序的菜单、路由框架整体上进行设计。 + +### 前端vue.config.js配置 + +为了解决插件页面的css和js等资源文件能正常访问,需要在vue.config.js中配置publicUrl为前端固定路径前缀(**`static-plugin`**)+pluginId。案例: + +```JavaScript +publicPath: "/static-plugin/xielibo-plugin-page/", +``` + +### 后端API**访问规则** + +**在****前端****中请求插件的****api****时,需要加上api前缀。例如:http://ip:port/{pluginRestPathPrefix}/{pluginId}/,规则如下:** + +- `pluginRestPathPrefix`: 主程序配置文件中`pluginRestPathPrefix`的配置值, 默认为: `plugins` +- `pluginId`: 插件id, 如果主程序配置文件中`enablePluginIdRestPathPrefix`配置值为 `true`, 则使用插件id作为前缀,否则不使用插件id作为前缀。 + +**案例**: + +插件ID:xxxx + +后端API原始地址:/test/get/123 + +前端调用地址:/plugins/xxxx/test/get/123 + +### 添加到主程序菜单 + +1. 定义监听类,在启动和停止事件中,调用主程序菜单接口的添加和删除方法。菜单接口说明详见文档 + +[主程序开放接口](https://fullstack-dao.feishu.cn/docx/doxcnIa9e0ChR4bJWlx4IyBfzjf) + +```Java +public class MyListener implements ApplicationListener { + + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ApplicationEnvironmentPreparedEvent) { + System.out.println("扩展实现初始化环境变量"); + } else if (event instanceof ApplicationPreparedEvent) { + System.out.println("扩展实现环境初始化完成"); + } else if (event instanceof ContextRefreshedEvent) { + System.out.println("扩展实现ApplicationContext被刷新"); + } else if (event instanceof ApplicationReadyEvent) { + //此处只能通过注入主程序Bean的第三种方式获取 + MainApplicationContext context = ((ApplicationReadyEvent) event).getApplicationContext().getBean(MainApplicationContext.class); + SpringBeanFactory factory = context.getSpringBeanFactory(); + MenuFacade menuFacade = factory.getBean(MenuFacade.class); + menuFacade.savePluginMenu("plugin-book", "图书管理插件", "books manager", "index"); + System.out.println("扩展实现插件已经启动完成"); + } else if (event instanceof ContextClosedEvent) { + MainApplicationContext context = ((ContextClosedEvent) event).getApplicationContext().getBean(MainApplicationContext.class); + SpringBeanFactory factory = context.getSpringBeanFactory(); + MenuFacade menuFacade = factory.getBean(MenuFacade.class); + menuFacade.deletePluginMenu("plugin-book"); + System.out.println("扩展实现插件停止"); + } + } +} +``` + +1. 配置监听类 + +```YAML +context: + listener: + classes: com.fsd.admin.plugins.listener.MyListener +``` + +## 操作日志打印 + +平台提供了操作日志异步打印的工具。在插件脚手架的**LogAspect类**中已经集成,在需要打印的controller的方法上使用自定义注解@Log即可。该注解有以下参数: + +| **参数名** | **类型** | **说明** | +|--------------------|--------------|-------------------------------------------------| +| title | string | 所属模块,建议格式:“插件ID”-模块。 | +| businessType | BusinessType | 操作类型,包括(*OTHER、INSERT、UPDATE、DELETE等*)默认为OTHER。 | +| operatorType | OperatorType | 操作来源,传入枚举类OperatorType.*PLUGIN*。 | +| isSaveRequestData | boolean | 是否保存请求参数。默认为true | +| isSaveResponseData | boolean | 是否保存响应参数。默认为true | + +示例: + +```Java +/** + * 添加 + */ +@Log(title = "base-ops-dataflow",operatorType = OperatorType.PLUGIN,businessType = BusinessType.INSERT) +@RequestMapping("/add") +public AjaxResult add(@RequestBody ModelingDataFlowEntity dataFlowData) { + return toAjax(modelingDataFlowService.insertDataFlow(dataFlowData)); +} +``` + +## 登录用户Token传参 + +平台登录后会将用户token存储在浏览器的localStorage中,名称为**opengauss-token**。 + +在插件中,获取token并传参有如下2步: + +1. 通过**localStorage.getItem('opengauss-token')**获取平台存储的登录用户token +2. 在每个请求发起的地方,在请求头header中加上**Authorization**参数,值为**'Bearer 上一步中获取的token'**。建议使用axios,直接在axios拦截器的请求头拦截中,给每个请求加上headers。 + +示例: + +```JavaScript +axios.interceptors.request.use( + (config) => { + const token = localStorage.getItem('opengauss-token') + if (token) { + if (!config.headers) { + config.headers = {} + } + config.headers.Authorization = `Bearer ${token}` + } + return config + }, + (error) => { + // do something + return Promise.reject(error) + } +) +``` + +## 插件授权扩展信息 + +平台支持插件自定义上报插件扩展信息及插件升级授权,主程序中定义了插件扩展信息的接口,插件只需实现扩展信息接口,即可上报插件信息到主程序中。 + +### PluginExtensionInfoExtract接口 + +该接口用于扩展插件信息,插件需要实现该扩展接口,实现`getPluginExtensionInfo`方法,并且在实现类上面添加Extract注解。 + +#### Extract注解 + +用于标识该实现扩展的唯一性,方便在主程序中调用时能定位到具体的实现。 其中有三个值: + +* `bus`: 业务标识,必须使用 **插件id**。【必选】 +* `scene`: 场景标识。【可选】 +* `useCase`: 用例标识。 【可选】 + +#### 示例 + +```java + +@Extract(bus = "visualtool-plugin") +public class ExtensionInfoServiceImpl implements PluginExtensionInfoExtract { + + @Override + public PluginExtensionInfoDto getPluginExtensionInfo() { + PluginExtensionInfoDto dto = new PluginExtensionInfoDto(); + dto.setPluginId("visualtool-plugin"); + dto.setPluginName("visualtool plugin"); + dto.setPluginHome("https://opengauss.org/zh/"); + dto.setPluginDevelopmentCompany("openGauss"); + dto.setPhoneNumber("400-400-4000"); + dto.setEmail("contact@opengauss.org"); + dto.setCompanyAddress("xx路xx号"); + dto.setAuthAddress("/test-plugin/license"); + dto.setPluginLicenseType(PluginLicenseType.FREE); + dto.setPluginExpirationTime(new Date()); + return dto; + } +} +``` + +#### 参数说明 + +在 PluginExtensionInfoDto 中有以下参数,插件可以在接口实现中自定义对应的参数值,来完善插件扩展信息: + +| 参数 | 类型 | 是否必填 | 可选值 | 说明 | +|--------------------------|-------------------|------|------------------------------|----------------------------------| +| pluginId | String | 是 | - | 插件id | +| pluginLicenseType | PluginLicenseType | 是 | OPEN_SOURCE|FREE |TRIAL|PAID | 插件类型(开源|免费|试用|正式) | +| pluginName | String | 否 | - | 插件名称 | +| pluginIntroduction | String | 否 | - | 插件简介 | +| pluginHome | String | 否 | - | 插件主页 | +| pluginActivationTime | Date | 否 | - | 插件激活时间 | +| PluginExpirationTime | Date | 否 | - | 插件有效期 | +| pluginDevelopmentCompany | String | 否 | - | 开发公司 | +| phoneNumber | String | 否 | - | 联系电话 | +| email | String | 否 | - | 联系邮箱 | +| companyAddress | String | 否 | - | 公司地址 | +| userGuide | String | 否 | - | 使用指导 | +| demoAddress | String | 否 | - | demo地址 | +| authAddress | String | 否 | - | 授权地址(可以是插件内部的一个路由跳转地址或外部的一个跳转链接) | +| customize | String | 否 | - | 自定义信息(暂不使用) | + +## Websocket使用 + +插件框架本身不支持在插件内使用websocket,为了支持插件内可以使用ws的能力,主程序中做了对ws请求的转发,同时提供推送消息给客户端的接口给插件调用,以满足插件的双向通信需求。 + +### **SocketExtract接口** + +该接口用于Websocket的连接与通信,插件需要扩展改接口,实现**onOpen**、**processMessage、onClose**方法,并**在类上加上Extract注解。** + +#### Extract注解 + +用于标识该实现扩展的唯一性,方便在主程序中调用时能定位到具体的实现。 其中有三个值: + +- `bus`: 业务标识,建议把插件ID当做前缀。【必选】 +- `scene`: 场景标识。【可选】 +- `useCase`: 用例标识。 【可选】 + +示例: + +```Java + +@Extract(bus = "plugin1-handler") +public class SocketMessageHandler implements SocketExtract { + + @Override + public void onOpen(String pluginId, String sessionId, Session session) { + System.out.println("连接成功。。。。。。。。"); + } + + @Override + public void processMessage(String sessionId, String message) { + System.out.println("接收到消息并处理。。。。。。。。" + message); + } + + @Override + public void onClose(String pluginId, String sessionId) { + System.out.println("连接关闭。。。。。。。。"); + } +} +``` + +#### 连接成功 + +**onOpen方法**会在连接成功时触发插件的回调。 + +#### 接收消息 + +**processMessage方法**。该方法会接收到客户端发送的消息。 + +#### 连接关闭 + +**onClose方法**会在连接关闭时触发插件的回调。 + +### 推送消息 + +调用主程序暴露的**WsFacade接口的sendMessage方法(主程序接口注入方式见下文)**。该方法有三个参数,分别是: + +- `pluginId`: 插件自己的ID。 +- `sessionId`: ws会话ID。 +- `message`: 消息内容。 + +示例: + +```Java +@Autowired +@AutowiredType(AutowiredType.Type.PLUGIN_MAIN) +private WsFacade wsFacade; + +@PostMapping("/send") +public AjaxResult send(String sessionId,String message) { + wsFacade.sendMessage("datasync-mysql", sessionId, message); + return AjaxResult.success(); +} +``` + +### 连接地址 + +**前端****ws连接地址为:**`wss://ip:9494/ws/插件ID/ws会话ID`** + +> ws会话ID由插件自己生成和维护。 + +## 插件中抛出异常 + +平台已经做了全局异常捕获处理和自定义**异常类CustomException**,插件中无需做全局捕获处理,只需要抛出该异常类即可,传入code和message。 + +**插件中使用的异常code,需要由OPS SIG组分配。** + +示例: + +```Java +@GetMapping("/test") +@Log(title = "sync-mysql", businessType = BusinessType.OTHER, operatorType = OperatorType.PLUGIN) +public AjaxResult test() { + syncDataService.testLog(); + if (1 == 1) { + throw new CustomException("插件中测试抛出全局异常", 伪代码code); + } + return AjaxResult.success(); +} +``` + +调用效果: + +![img](https://fullstack-dao.feishu.cn/space/api/box/stream/download/asynccode/?code=YTQyMDhkZDM3NzkwZjFlZjQ3YTdkOGJhNjgwZTM5NmVfd3pXZjZESWZWczQ3Z2JzMVRDbWdyTVNPbDlpd2tXMUNfVG9rZW46Ym94Y25USno1cGRFVk52ZTNDTkZxU0sxcTZnXzE2NzM4NzY5NzY6MTY3Mzg4MDU3Nl9WNA) + +## 插件扩展参数配置 + +在插件中可以配置自定义的参数,以提供给平台获取使用,比如插件的Logo、插件的主题等。 + +### 使用方式 + +在插件中实现com.gitee.starblues.core.PluginExtensionInfo接口接口。例如: + +```Java + +@Component +public class PluginExtensionInfoConfig implements PluginExtensionInfo { + + @Override + public Map extensionInfo() { + Map map = new HashMap<>(); + map.put("logo", "PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAzMTAgMzEwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAzMTAgMzEwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGQ9Ik0zMDAuNTY0LDE3OS4zMTFMMjgyLjQsMTY4LjgyNGMwLjQ4OS00LjU0MywwLjc0Ny05LjE1NCwwLjc0Ny0xMy44MjRzLTAuMjU4LTkuMjgxLTAuNzQ3LTEzLjgyNGwxOC4xNjQtMTAuNDg3CgljMy44ODEtMi4yNDEsNi42NTgtNS44Niw3LjgxNi0xMC4xOTFjMS4xNi00LjMzLDAuNTY0LTguODU0LTEuNjc2LTEyLjczNWwtMzQuOTQzLTYwLjUyNGMtMi45OS01LjE4LTguNTY0LTguMzk2LTE0LjU1MS04LjM5NgoJYy0yLjkzLDAtNS44MjYsMC43NzgtOC4zNzcsMi4yNTFMMjMwLjYxOSw1MS42MWMtNy40MDItNS40MjktMTUuNDA2LTEwLjA4My0yMy44OTMtMTMuODQyVjE2Ljc4MwoJQzIwNi43MjcsNy41MjksMTk5LjE5NSwwLDE4OS45NDUsMGgtNjkuODkxYy05LjI1NCwwLTE2Ljc4MSw3LjUyOS0xNi43ODEsMTYuNzgzdjIwLjk4NWMtOC40ODYsMy43NTktMTYuNDksOC40MTMtMjMuODk0LDEzLjg0MgoJTDYxLjE2NCw0MS4wOTRjLTIuNTUxLTEuNDczLTUuNDQ1LTIuMjUtOC4zNzUtMi4yNWMtNS45ODYsMC0xMS41NjMsMy4yMTUtMTQuNTUzLDguMzk1TDMuMjk1LDEwNy43NjIKCWMtMi4yNDIsMy44ODEtMi44MzYsOC40MDYtMS42NzQsMTIuNzM2YzEuMTU2LDQuMzMsMy45MzUsNy45NDksNy44MTQsMTAuMTkxTDI3LjYsMTQxLjE3NmMtMC40ODksNC41NDMtMC43NDcsOS4xNTQtMC43NDcsMTMuODI0CglzMC4yNTgsOS4yODEsMC43NDcsMTMuODI0TDkuNDM1LDE3OS4zMTFjLTMuODc5LDIuMjQxLTYuNjU4LDUuODYtNy44MTQsMTAuMTkxYy0xLjE2Miw0LjMzLTAuNTY4LDguODU1LDEuNjc0LDEyLjczNWwzNC45NDEsNjAuNTI0CgljMi45OSw1LjE4LDguNTY2LDguMzk1LDE0LjU1Myw4LjM5NWMyLjkzLDAsNS44MjQtMC43NzcsOC4zNzUtMi4yNUw3OS4zOCwyNTguMzljNy40MDMsNS40MjksMTUuNDA3LDEwLjA4MywyMy44OTQsMTMuODQydjIwLjk4NgoJYzAsNC40ODIsMS43NDQsOC42OTUsNC45MTQsMTEuODY2YzMuMTc0LDMuMTY5LDcuMzg1LDQuOTE2LDExLjg2Nyw0LjkxNmg2OS44OTFjOS4yNSwwLDE2Ljc4MS03LjUyOSwxNi43ODEtMTYuNzgydi0yMC45ODYKCWM4LjQ4Ni0zLjc1OSwxNi40OS04LjQxMywyMy44OTMtMTMuODQybDE4LjIxNSwxMC41MTdjMi41NTEsMS40NzMsNS40NDcsMi4yNSw4LjM3NywyLjI1YzUuOTg2LDAsMTEuNTYxLTMuMjE1LDE0LjU1MS04LjM5NQoJbDM0Ljk0My02MC41MjNjMi4yNC0zLjg4MSwyLjgzNi04LjQwNiwxLjY3Ni0xMi43MzZDMzA3LjIyMywxODUuMTcyLDMwNC40NDUsMTgxLjU1MywzMDAuNTY0LDE3OS4zMTF6IE0xNTUsMjQ2LjEwMQoJYy0xOC4yMywwLTM1LjIwNy01LjM1Ny00OS40NDktMTQuNTc5bDMwLjgwMS0zMC44MDRjNi40NDksMi43NzIsMTMuNDUsNC4yNCwyMC42NzcsNC4yNDFjMC4wMDIsMCwwLjAwMywwLDAuMDA0LDAKCWMxNC4wMTEsMCwyNy4xNzUtNS40NjYsMzcuMDY0LTE1LjM5YzEzLjUtMTMuNTM2LDE4LjU0MS0zMy4zNjMsMTMuMTU1LTUxLjc0M2MtMC4zMTMtMS4wNjktMS4xNjgtMS44OTQtMi4yNDgtMi4xNjkKCWMtMS4wNzgtMC4yNzctMi4yMjUsMC4wNC0zLjAxMSwwLjgyOWwtMzIuOTcsMzMuMDY5Yy0xLjk3OS0wLjgwNC02Ljk2MS0zLjU0Mi0xNi4xODYtMTIuNzM2CgljLTkuMjI2LTkuMTk3LTExLjk3Ni0xNC4xNzMtMTIuNzgzLTE2LjE0OGwzMi45NjYtMzMuMDY5YzAuNzg3LTAuNzg5LDEuMDk4LTEuOTM1LDAuODItMy4wMTNjLTAuMjc4LTEuMDc5LTEuMTA1LTEuOTMxLTIuMTc1LTIuMjQxCgljLTQuNzUxLTEuMzc4LTkuNjc2LTIuMDc4LTE0LjYzNy0yLjA3OGMtMTQuMDE2LDAtMjcuMTgxLDUuNDY0LTM3LjA2OSwxNS4zODVjLTkuODczLDkuOTAzLTE1LjI5OSwyMy4wNTctMTUuMjgsMzcuMDM5CgljMC4wMSw3LjIwNCwxLjQ3NiwxNC4xOCw0LjI0LDIwLjYwNGwtMzAuNzIyLDMwLjcyNEM2OS4xNDUsMTg5Ljg2OCw2My44OTYsMTczLjA0Nyw2My44OTYsMTU1CgljMC01MC4zMTMsNDAuNzg3LTkxLjEwMiw5MS4xMDQtOTEuMTAyczkxLjEwMiw0MC43ODksOTEuMTAyLDkxLjEwMlMyMDUuMzE2LDI0Ni4xMDEsMTU1LDI0Ni4xMDF6Ii8+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPg=="); + map.put("theme", "dark"); + map.put("pluginType", 1); + map.put("isNeedConfigured", 1); + map.put("configAttrs", "[{\"attrCode\":\"esHost\",\"attrLabel\":\"ES服务器\"},{\"attrCode\":\"esPort\",\"attrLabel\":\"端口\"}]"); + return map; + } +} +``` + +### 当前平台支持的扩展参数 + +| 参数名称 | 类型 | 是否必填 | 描述 | +|------------------|---------|------|------------------------------------| +| logo | String | 是 | 必须是svg格式,传值内容必须是svg代码的Base64编码 | +| pluginType | Integer | 否 | 插件类型 | +| isNeedConfigured | Integer | 否 | 是否需要在主程序填写配置信息,默认0,1为需要 | +| configAttrs | List | 否 | 配置属性集合,json字符串。具体内容见下 | +| theme | String | 否 | 插件主题;可选项:dark(深色)light(浅色),默认light | + +**configAttrs单个元素** + +| 参数名称 | 类型 | 是否必填 | 描述 | +|-----------|--------|------|--------| +| attrLabel | String | 是 | 属性标签 | +| attrCode | String | 是 | 属性Code | + +## Maven打包配置 + +### 插件信息配置 + +主要在 `configuration`节点下进行详细配置,说明如下: + +#### mode 【必配】 + +当前节点主要是设置将插件打成开发/生产模式运行的插件。 + +```XML +prod +``` + +| 参数名称 | 类型 | 是否必填 | 描述 | +|------|--------|------|------------------------| +| mode | String | 是 | 打包模式。可选参数:`dev`、`prod` | + +#### pluginInfo【必配】 + +当前节点主要是定义插件信息. + +```XML + + + email-notice + org.opengauss.admin.plugin.EmailNoticeApplication + 1.0.0 + xielibo + 邮件通知插件 + +``` + +**注意:插件id和版本号中不能出现** **`@`****、** **`,`****特殊符号** + +| 参数名称 | 类型 | 是否必填 | 描述 | +|----------------|--------|------|-----------------------| +| id | String | 是 | 定义插件全局唯一id | +| bootstrapClass | String | 是 | 插件引导类包名 | +| version | String | 是 | 插件版本号。版本号要求见如下`版本号规则` | +| provider | String | 是 | 插件提供开发者名称 | +| description | String | 是 | 插件描述信息 | + +#### 完整样例 + +```XML + + + + + + com.gitee.starblues + spring-brick-maven-packager + 3.1.0 + + + + + + com.gitee.starblues + spring-brick-maven-packager + + prod + + email-notice + org.opengauss.admin.plugin.EmailNoticeApplication + 1.0.0 + xielibo + 邮件通知插件 + + + + + + repackage + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + org.apache.maven.plugins + maven-clean-plugin + + + org.codehaus.mojo + exec-maven-plugin + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + + copy-web + generate-resources + + copy-resources + + + ${basedir}/src/main/resources + + + 工程中前端构建后的文件目录 + 后端中存放前端资源的目录 + + + + + + + + +``` + +### 编译插件 + +执行maven命令:`mvn clean package`进行编译/打包 + +## **插件中注入主程序Bean** + +### 方式1: 直接注入式 + +可直接通过`@Autowired`或 `@Resource`注解进行注入,支持`属性注解`、`构造器`、`set方法`注入。 + +为了用户可自主控制注入,框架中提供了额外注解`@AutowiredType`进行控制注入类型: + +- `@AutowiredType(AutowiredType.Type.PLUGIN)`: **【默认类型】**直接从插件中进行注入。 +- `@AutowiredType(AutowiredType.Type.MAIN)`: 直接从主程序进行注入。 +- `@AutowiredType(AutowiredType.Type.PLUGIN_MAIN)`: 优先从插件中注入,如果不存在, 则从主程序进行注入。 +- `@AutowiredType(AutowiredType.Type.MAIN_PLUGIN)`: 优先从主程序中注入,如果不存在,则从插件中进行注入。 + +**注入案例** + +```Java + +@Service +public class BasicService { + + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private MenuFacade menuFacade; + + +} +``` + +### 方式2: 全局配置式 + +在插件中可通过如下方式进行全局配置注入类型。使用步骤如下: + +1. 实现`AutowiredTypeDefiner`对象, 定义全局注入配置。 + +```Java +public class AutowiredTypeDefinerImpl implements AutowiredTypeDefiner { + + @Override + public void config(AutowiredTypeDefinerConfig config) { + config + .add(AutowiredType.Type.MAIN, "org.opengauss.admin.system.plugin.facade.MenuFacade") + .add(AutowiredType.Type.MAIN, DataSource.class); + } +} +``` + +1. 在插件引导类重写`autowiredTypeDefiner`方法,返回`AutowiredTypeDefiner`实现对象。 + +```Java + +@SpringBootApplication +public class Basic1Plugin extends SpringPluginBootstrap { + + @Override + protected AutowiredTypeDefiner autowiredTypeDefiner() { + return new AutowiredTypeDefinerImpl(); + } +} +``` + +### 方式3: 代码调用式 + +通过代码直接从主程序获取`Bean`, 使用方式如下: + +直接注入`MainApplicationContext`,调用其`getSpringBeanFactory()`方法获取`SpringBeanFactory`,然后获取主程序中`Bean` + +```Java + +@Service +public class BasicService { + + @Autowired + private MainApplicationContext context; + + public void getName() { + SpringBeanFactory factory = context.getSpringBeanFactory(); + MenuFacade menuFacade = factory.getBean(MenuFacade.class); + } + +} +``` diff --git a/ER-Model/openGauss-datakit/doc/DataKit Product Manual - Platform.md b/ER-Model/openGauss-datakit/doc/DataKit Product Manual - Platform.md new file mode 100644 index 000000000..b03cc5900 --- /dev/null +++ b/ER-Model/openGauss-datakit/doc/DataKit Product Manual - Platform.md @@ -0,0 +1,454 @@ +**openGauss DataKit Platform产品使用手册** + +# **文档历史** + +| **修订日期** | **修订内容** | **版本号** | **编写人** | +| --- | --- | --- | --- | +| 2022/12/18 | 初稿 | 1.0 | 张绍鹏 | +| 2022/12/21 | 内部评审后修改 | 1.1 | 张绍鹏 | +| 2023/1/13 | 按要求改为md格式 | 1.2 | 张绍鹏 | + +# **关于本手册** + +## **产品介绍** + +openGauss的安装、运维场景对于初级用户或单纯想要测试openGauss数据库基本特性的使用者来说技术难度较大、过程较为复杂、学习曲线较为陡峭,尤其企业版安装对一般用户来说操作难度很大。使用可视化运维平台可以屏蔽openGauss的技术细节,让普通用户能够快速上手体验功能,让运维人员能够快速在企业环境中部署、卸载各类openGauss集群,减少了用户的学习成本和运维成本,实现了对openGauss各种常见操作的可视化,屏蔽了各种不同openGauss版本中的运维命令差异,可以让用户使用相同的方式操作数据库,不用知道命令细节也可以使用openGauss数据库的各项能力,让用户可以专注于自身的业务领域。 + +因此需要开发一些有针对性的运维监控工具,为不同配置不同运维要求的客户提供运维技术支撑,这些都将是openGauss社区的宝贵资产。而社区急需一个一体化的平台通过插件的方式将这些工具进行整合,并支持方便快捷的个性化配置。对于社区合作伙伴已经开发的Web工具,合作伙伴也可以根据《插件开发指南》的要求自行改造为插件,整合到一体化平台中。 + +本产品是基于Web的openGauss的可视化的一体化平台系统,目的是方便客户使用和管理openGauss可视化工具,可以为客户降低openGauss数据库安装使用门槛,做到安全中心管理,插件管理,以及其它功能包括一键化部署、卸载、组件化安装、多版本升级、日常运维和。本文档主要适用openGauss3.0.0+版本。 + +## **主要功能介绍** + +本产品主要包含以下几个功能特性: + +- 插件平台(详见平台开发文档,该手册不再赘述) +- 用于插件开发的前后端一体的开发框架(详见插件开发文档,该手册不再赘述) +- Mysql数据迁移工具插件实现 +- 安全中心(账号管理、角色权限管理和白名单管理) +- 插件管理模块 +- 日志中心,包括操作日志和系统日志(日志级别/切割规则/保留天数的设置和日志下载) +- 国际化(中/英) +- 主题切换(黑/白) +- 基础模块(用户中心、修改个人密码、登录/登出) + +## **预期读者** + +- 用户 +- 测试人员 +- 开发人员 + +## **名词解释** + +| **名词** | **解释** | **备注** | +| ---------- | -------------------------- | -------------------------------------------------- | +| 超级管理员 | 系统安装后即可用的内置账号 | 该账号不可删除,而且是唯一可以访问“安全中心”的账号 | +| | | | +| | | | +| | | | +| | | | +| | | | +| | | | +| | | | + +## + +## **版本及功能列表** + +| **版本号** | **新增功能** | **发布时间** | +| --- | --- | --- | +| V1.0 | 首次发布 | 2022-12-18 | + +## **平台基础模块(登录/登出/用户中心/密码)** + +### **概述** + +平台基础模块包括登录平台,登出平台,修改密码,用户中心。该工具是一个本地(内网)工具,不是面向公网开放的平台,所以没有注册用户的需求,但是有用户管理能力,超级管理员可在平台内部新增用户来达到用户管理的能力。 + +### **角色** + +所有用户 + +### **界面** + +1、登录平台 + +![platform-login](./_resources/platform-login.png) + +2、退出登录 + +![platform-logout](./_resources/platform-logout.png) + +3、修改密码 + +![platform-change-password-menu](./_resources/platform-change-password-menu.png) + + + +platform-user-change-password + + + +4、用户中心 + +点击用户中心 + +![platform-user-center-menu](./_resources/platform-user-center-menu.png) + +点击用户名旁边的“编辑”图标 + +![platform-user-center-view](./_resources/platform-user-center-view.png) + +修改用户信息,包括: + +- 账号名 +- 用户昵称 +- 手机号码(系统会检查唯一性) +- 邮箱(optional) + +![platform-edit-user-info](./_resources/platform-edit-user-info.png) + +点击更换头像,弹窗选择图片上传,Upload success后显示出头像 + +![platform-upload-avatar](./_resources/platform-upload-avatar.png) + +![platform-new-avatar](./_resources/platform-new-avatar.png) + +## **国际化和黑白主题** + +### **概述** + +本平台支持国际化热切换(中文和英文),支持黑白主题切换。 + +### **角色** + +所有用户 + +### **界面** + +1. 切换语言 + +![platform-change-language](./_resources/platform-change-language.png) + +**切换到英文后的效果:** + +![platform-English](./_resources/platform-English.png) + +1. 切换主题 + +**点击右上角的太阳/月亮图标即可切换主题:** + +platform-theme-light + +点击右上角太阳/月亮按钮进行黑白主题toggle切换 + +![platform-theme-dark](./_resources/platform-theme-dark.png) + +黑色主题的效果: + +![platform-theme-darkmode](./_resources/platform-theme-darkmode.png) + +## **插件管理页面** + +### **概述** + +在本页面可以完成对插件的集中管理,包括以下操作:插件一览,安装插件,卸载插件,停用插件,启用插件等。 + +### **角色** + +拥有插件管理权限的角色可以访问 + +### **界面** + +1、插件一览 + +![platform-plugin-mgmt](./_resources/platform-plugin-mgmt.png) + +**说明:** + +每个插件包括以下信息: + +- 图标 +- 名称 +- 版本 +- 开发者 +- 简介 + +2、安装插件 + +![platform-plugin-upload-jar](./_resources/platform-plugin-upload-jar.png) + +**说明:** + +- 在插件管理页面,点击右上角“安装插件”按钮 +- 将插件jar包拖拽到窗口里 或 打开文件选择器选择插件jar包 +- 点击“播放”按钮进行安装 +- 安装成功后:插件一览列表会出现对应的插件信息卡片,左侧系统菜单会出现插件相关的菜单项。 + +3、卸载插件 + +为了提供更灵活的菜单配置能力,平台允许插件创建二级菜单。又为了让同类插件的菜单可以归类于一级菜单下,平台也允许插件在其它插件菜单下创建二级菜单,这样就会存在插件菜单间的依赖,所以在卸载插件时要注意: + +*如果其它插件在你的插件菜单下创建了子菜单项,那你需要先卸载那个插件消除依赖后才能卸载你的插件。* + +插件被卸载后,该插件下的所有Tab标签页都会被自动关闭。 + +## **安全中心** + +### **概述** + +安全中心提供登录权限和账号管理,以及访问白名单管理。 + +### **角色** + +出于安全性设计的考量,平台只允许超级管理员(即安装后就有的系统内置账号)可以访问安全中心。其它Role配置权限时甚至看不到安全中心的菜单,所以也无法给其它Role配置安全中心的权限。 + +### **界面** + +1、白名单管理 + +![platform-security-whitelist](./_resources/platform-security-whitelist.png) + + + +platform-security-whitelist-add + +**说明:** + +- 左侧菜单栏找到“安全中心-访问白名单”菜单,点击打开“白名单管理”页面; +- 点击右上角“添加白名单”按钮 +- 在弹出框中填写白名单名称和允许访问的IP地址,多个IP地址间用逗号隔开,确认即可完成添加。 + +2、角色与权限菜单 + +本系统支持通过创建特定的权限组合来管理用户权限,权限组合被定义为“角色”,一个用户只能拥有一个角色。 + +![platform-security-role-list](./_resources/platform-security-role-list.png) + +点击“添加角色” + +创建或编辑“角色”时,可以为“角色”配置权限范围,包括已经安装的插件的访问权限。 + +*注意:出于安全性设计的考虑,任何添加的”角色“都无法拥有”安全中心“的访问权限,”安全中心“的权限被固定在”超级管理员“身上。* + +platform-security-add-role + + + +**说明:** + +- 左侧菜单栏找到“安全中心-角色菜单”,点击打开“角色菜单”管理页面; +- 点击右上角“添加角色”按钮 +- 在弹出框中填写角色名称,并勾选允许访问的菜单项,可以选择多个菜单项,可以全选,可以添加备注,确认即可完成添加。 + +3、账号与权限 + +![platform-security-account-list](./_resources/platform-security-account-list.png) + +platform-security-add-user + +**说明:** + +- 左侧菜单栏找到“安全中心-账号与权限”,点击打开“账号与权限”管理页面; +- 点击右上角“添加用户”按钮 +- 在弹出框中填写以下字段: + - 账号:登录用户名 + - 密码:登录密码 + - 昵称: + - 手机号码:唯一手机号 + - 角色:不选择角色则没有任何权限 + - 状态:启用或停用 + - 备注:可选填 + +点击确定完成添加账号。 + +![platform-security-reset-password-dialog](./_resources/platform-security-reset-password-dialog.png) + +点击“重置密码”可以为某个用户重置密码 + +## **日志中心** + +### **概述** + +日志中心提供系统日志管理和操作日志管理功能。 + +### **角色** + +拥有日志中心访问权限的账号均可以访问该功能。 + +### **界面** + +1、系统日志 + +![platform-logs-system-list](./_resources/platform-logs-system-list.png) + + + +platform-logs-system-configuration + + + +**说明:** + +- 左侧菜单栏找到“日志中心-系统日志”,点击打开“系统日志”管理页面; +- 可以通过搜索找到对应的日志文件,点击下载日志文件 +- 点击右上角“日志设置”按钮 +- 在弹出框中进行系统日志配置: + - 日志输出级别:四个级别DEBUG,INFO,WARN,ERROR + - 保留天数:输入天数,操作天数的日志会被自动清除 + - 单个日志文件大小:单个日志文件达到这个限制会自动创建新的日志文件 + - 最大占用空间:所有日志文件被允许占用的最大存储空间 + +2. 操作日志 + +![platform-logs-operation-list](./_resources/platform-logs-operation-list.png) + +platform-logs-operation-details + + + +**说明:** + +1左侧菜单栏找到“日志中心-操作日志”,点击打开“操作日志”管理页面; + +2可以通过搜索找到对应的日志文件 + +3点击“详情”查看该操作的详细参数 + +## 设备管理 + +用户通过【资源中心】-【设备管理】进入本功能画面,在这个画面上,左侧为物理机基本信息,右侧为实时监控和操作面板,监控指标包括网络速率、CPU使用率、内存使用率、磁盘使用率。用户可以根据ip、操作系统、标签对设备进行筛选。在root密码没有选择保存的情况下,实时数据将无法获取。 + +![](./_resources/equipment-manage.png) + +点击【创建】按钮可以对物理机进行创建,在弹出的对话框中,用户需要输入内网IP、外网IP、ssh端口号、root密码等创建物理机。点击标签下拉框,可以选择标签,也可以输入字符来创建标签。点击连通性测试可以测试到设备的ssh连接是否正常。 + +![](./_resources/equipment-manage-create.png) + +点击操作面板的【编辑】可以对物理机进行编辑,包括ip、标签、密码等的修改。 + +![](./_resources/equipment-manage-edit.png) + +点击【标签设置】,可以对勾选的多个设备批量设置标签。 + +![](./_resources/equipment-manage-tag-batch.png) + +点击【标签管理】,可以展示当前所有的标签和关联的主机数量,用户可以在这里对标签做统一的修改或者删除。 + +![](./_resources/equipment-manage-tag-manage.png) + +在需要大批量导入的场景中,用户可以点击【批量导入】按钮,在弹出的对话框中点击【导入模板下载】下载导入模板。 + +![](./_resources/server-manage-template-download.png) + +打开下载好的模板文件,输入对应的序号、服务器名称、内网IP等信息。 + +![](./_resources/server-manage-template.png) + +点击【选择文件】将需要导入的文件上传,然后点击【确定】开始解析。 + +![](./_resources/server-manage-parsing-files.png) + +等待解析完成,完成后会出现解析结果,如果导入数据有误,点击【下载错误报告按钮】下载错误报告查看具体原因。 + +![](./_resources/server-manage-import-completed.png) + +打开错误报告文件会显示导入失败的记录并显示其失败原因。 + +![](./_resources/server-manage-error-reporting.png) + +## 实例管理 + +用户通过【资源中心】-【实例管理】进入本功能画面,在这个画面上,分为节点信息和集群信息两级展示,集群信息一层展示集群名称、数据库类型、运行状态等基本信息,节点一层展示节点所在主机的基本信息、角色、运行状态、实时监控数据等。 + +用户可以在这个画面上根据集群名称、ip地址、数据库类型对实例进行过滤搜索。 + +![](./_resources/jdbc-manage.png) + +点击【创建】后,用户在新增对话框内可以输入对应数据库节点的连接信息和扩展属性,并可以测试连通性。 + +在不勾选自定义名称的情况下将以前两个节点的IP地址和端口进行组合作为集群名称。 + +![](./_resources/jdbc-manage-create.png) + +在需要大批量导入的场景中,用户可以先点击【下载模板】下载导入模板 +然后点击【点击上传】进行导入,导入中出现的问题将提示用户进行修改。 + +## 安装包管理 + +【安装包管理】支持【新增安装包】、【批量检查】、【批量删除】。 + +![](./_resources/package-management.png) + +【新增安装包】支持openEluer20.03、openEluer22.03、centOs7三个操作系统,其余选项根据机器类型进行选择,下载链接自动关联,点击确定即可下载成功。 + +![](./_resources/package-management-add.png) + +下载成功后,点击【批量检查】或者【检查】可检查安装包是否下载成功、点击【更新】可替换安装包。 + +![](./_resources/package-management-other.png) + +【更新安装包】界面(点击【离线上传】进行替换安装包) + +![](./_resources/package-management-update.png) + +## 集群管理 + +### 集群列表 + +【集群列表】展示已安装或者已导入datakit中的集群,有启停、卸载、备份数据、删除等功能。 + +![](./_resources/cluster-manage-list.png) + +### 并行安装任务 + +#### 草稿箱 + +【草稿箱】中的集群任务通过【创建并行安装任务】创建、创建成功后如果【环境监测】成功,则可以进行【发布】,【发布】后进入【任务列表】界面进行安装。 + +![](./_resources/cluster-manage-task-draft.png) + +![](./_resources/cluster-manage-task-draft-publish.png) + +点击【复制】可以快速复制操作系统、架构、安装包相同的安装任务、删除支持【批量删除】和单个【删除】、【编辑】可以修改任务的配置、【执行日志下载】可以下载日志查看“创建”-“执行成功”的流程 + +![](./_resources/cluster-manage-task-draft-other.png) + +点击【任务ID】可以查看【任务详情】,点击【编辑】进行修改。 + +![](./_resources/cluster-manage-task-look.png) + +![](./_resources/cluster-manage-task-detail.png) + +#### 任务列表 + +任务列表界面展示通过环境检查的安装任务、可以点击【一键执行】或者【批量执行】进行安装。 + +![](./_resources/cluster-manage-tasks-list-execute.png) + + +## **系统设置** + +### **概述** + +系统设置可以让用户设置整个Datakit平台上的全局配置。 + +### **角色** + +拥有系统设置权限的账号均可以访问该功能。 + +### **界面** + +用户通过点击右上角【系统设置】菜单进入该功能。 +![](./_resources/sys-setting-enter.jpg) +在这个画面上包含文件上传路径、迁移套件在线下载地址、迁移套件安装包名称、迁移套件jar包名称四个设置项。 +![](./_resources/sys-setting.png) +其中文件上传路径指的是所有通过Datakit页面上传文件的存放路径,该路径必须以/结尾,并且不能与其它用户使用同一个文件夹以防止文件互相覆盖。 + +文件上传路径的初始默认值位于基座工程的sql文件中,路径为visualtool-api/src/main/resources/db/openGauss-visualtool.sql。 +如需更改文件上传路径的默认值/ops/files,可以修改sys_setting表中的初始数据为其它路径。 +![](./_resources/sys-setting-file-path-sql.jpg) + +迁移相关的三个设置:迁移套件在线下载地址、迁移套件安装包名称、迁移套件jar包名称用于数据迁移在线安装迁移套件时作为默认值。 +其中在线下载地址指的是下载迁移套件的路径,迁移套件jar名称指的解压缩安装包后启动目标文件的名称。 + diff --git a/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-list.png b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-list.png new file mode 100644 index 000000000..d0b78450b Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-list.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-detail.png b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-detail.png new file mode 100644 index 000000000..6a4d1afae Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-detail.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-draft-other.png b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-draft-other.png new file mode 100644 index 000000000..2afe02f6f Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-draft-other.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-draft-publish.png b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-draft-publish.png new file mode 100644 index 000000000..a2823562a Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-draft-publish.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-draft.png b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-draft.png new file mode 100644 index 000000000..5a74b9387 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-draft.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-look.png b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-look.png new file mode 100644 index 000000000..2901b9cd2 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-task-look.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-tasks-list-execute.png b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-tasks-list-execute.png new file mode 100644 index 000000000..d4dd98196 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-tasks-list-execute.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-tasks-list.png b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-tasks-list.png new file mode 100644 index 000000000..14533e13a Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/cluster-manage-tasks-list.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/equipment-manage-create.png b/ER-Model/openGauss-datakit/doc/_resources/equipment-manage-create.png new file mode 100644 index 000000000..924410de2 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/equipment-manage-create.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/equipment-manage-edit.png b/ER-Model/openGauss-datakit/doc/_resources/equipment-manage-edit.png new file mode 100644 index 000000000..d64710ee1 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/equipment-manage-edit.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/equipment-manage-tag-batch.png b/ER-Model/openGauss-datakit/doc/_resources/equipment-manage-tag-batch.png new file mode 100644 index 000000000..981cd50d9 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/equipment-manage-tag-batch.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/equipment-manage-tag-manage.png b/ER-Model/openGauss-datakit/doc/_resources/equipment-manage-tag-manage.png new file mode 100644 index 000000000..354e40d87 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/equipment-manage-tag-manage.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/equipment-manage.png b/ER-Model/openGauss-datakit/doc/_resources/equipment-manage.png new file mode 100644 index 000000000..296af0f3a Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/equipment-manage.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/jdbc-manage-create.png b/ER-Model/openGauss-datakit/doc/_resources/jdbc-manage-create.png new file mode 100644 index 000000000..7411c0483 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/jdbc-manage-create.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/jdbc-manage.png b/ER-Model/openGauss-datakit/doc/_resources/jdbc-manage.png new file mode 100644 index 000000000..6f169fda7 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/jdbc-manage.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/package-management-add.png b/ER-Model/openGauss-datakit/doc/_resources/package-management-add.png new file mode 100644 index 000000000..d38995f87 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/package-management-add.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/package-management-other.png b/ER-Model/openGauss-datakit/doc/_resources/package-management-other.png new file mode 100644 index 000000000..6047cb058 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/package-management-other.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/package-management-update.png b/ER-Model/openGauss-datakit/doc/_resources/package-management-update.png new file mode 100644 index 000000000..332f09278 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/package-management-update.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/package-management.png b/ER-Model/openGauss-datakit/doc/_resources/package-management.png new file mode 100644 index 000000000..c2bc8eac7 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/package-management.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-English.png b/ER-Model/openGauss-datakit/doc/_resources/platform-English.png new file mode 100644 index 000000000..adad9bbfb Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-English.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-change-language.png b/ER-Model/openGauss-datakit/doc/_resources/platform-change-language.png new file mode 100644 index 000000000..acbbc7e80 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-change-language.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-change-password-menu.png b/ER-Model/openGauss-datakit/doc/_resources/platform-change-password-menu.png new file mode 100644 index 000000000..c8b96099e Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-change-password-menu.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-edit-user-info.png b/ER-Model/openGauss-datakit/doc/_resources/platform-edit-user-info.png new file mode 100644 index 000000000..2e2c008de Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-edit-user-info.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-login.png b/ER-Model/openGauss-datakit/doc/_resources/platform-login.png new file mode 100644 index 000000000..08a0cd5a3 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-login.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-logout.png b/ER-Model/openGauss-datakit/doc/_resources/platform-logout.png new file mode 100644 index 000000000..0f1ecdab8 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-logout.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-logs-operation-details.png b/ER-Model/openGauss-datakit/doc/_resources/platform-logs-operation-details.png new file mode 100644 index 000000000..00478e69f Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-logs-operation-details.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-logs-operation-list.png b/ER-Model/openGauss-datakit/doc/_resources/platform-logs-operation-list.png new file mode 100644 index 000000000..09e4d6417 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-logs-operation-list.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-logs-system-configuration.png b/ER-Model/openGauss-datakit/doc/_resources/platform-logs-system-configuration.png new file mode 100644 index 000000000..ea4d8d941 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-logs-system-configuration.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-logs-system-list.png b/ER-Model/openGauss-datakit/doc/_resources/platform-logs-system-list.png new file mode 100644 index 000000000..097d52e91 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-logs-system-list.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-new-avatar.png b/ER-Model/openGauss-datakit/doc/_resources/platform-new-avatar.png new file mode 100644 index 000000000..bc17abf89 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-new-avatar.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-plugin-mgmt.png b/ER-Model/openGauss-datakit/doc/_resources/platform-plugin-mgmt.png new file mode 100644 index 000000000..b3cd255a4 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-plugin-mgmt.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-plugin-upload-jar.png b/ER-Model/openGauss-datakit/doc/_resources/platform-plugin-upload-jar.png new file mode 100644 index 000000000..658b62296 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-plugin-upload-jar.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-security-account-list.png b/ER-Model/openGauss-datakit/doc/_resources/platform-security-account-list.png new file mode 100644 index 000000000..b8fc4fca1 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-security-account-list.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-security-add-role.png b/ER-Model/openGauss-datakit/doc/_resources/platform-security-add-role.png new file mode 100644 index 000000000..7ac2342f0 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-security-add-role.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-security-add-user.png b/ER-Model/openGauss-datakit/doc/_resources/platform-security-add-user.png new file mode 100644 index 000000000..4e795726d Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-security-add-user.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-security-reset-password-dialog.png b/ER-Model/openGauss-datakit/doc/_resources/platform-security-reset-password-dialog.png new file mode 100644 index 000000000..01720f2a8 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-security-reset-password-dialog.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-security-role-list.png b/ER-Model/openGauss-datakit/doc/_resources/platform-security-role-list.png new file mode 100644 index 000000000..cd122e0e7 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-security-role-list.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-security-whitelist-add.png b/ER-Model/openGauss-datakit/doc/_resources/platform-security-whitelist-add.png new file mode 100644 index 000000000..9c0cf41f2 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-security-whitelist-add.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-security-whitelist.png b/ER-Model/openGauss-datakit/doc/_resources/platform-security-whitelist.png new file mode 100644 index 000000000..26edfefa7 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-security-whitelist.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-theme-dark.png b/ER-Model/openGauss-datakit/doc/_resources/platform-theme-dark.png new file mode 100644 index 000000000..c5fe9308c Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-theme-dark.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-theme-darkmode.png b/ER-Model/openGauss-datakit/doc/_resources/platform-theme-darkmode.png new file mode 100644 index 000000000..e03ae3c28 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-theme-darkmode.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-theme-light.png b/ER-Model/openGauss-datakit/doc/_resources/platform-theme-light.png new file mode 100644 index 000000000..96fefd855 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-theme-light.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-upload-avatar.png b/ER-Model/openGauss-datakit/doc/_resources/platform-upload-avatar.png new file mode 100644 index 000000000..b4b89b544 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-upload-avatar.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-user-center-menu.png b/ER-Model/openGauss-datakit/doc/_resources/platform-user-center-menu.png new file mode 100644 index 000000000..831bf27ac Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-user-center-menu.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-user-center-view.png b/ER-Model/openGauss-datakit/doc/_resources/platform-user-center-view.png new file mode 100644 index 000000000..a05519655 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-user-center-view.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/platform-user-change-password.png b/ER-Model/openGauss-datakit/doc/_resources/platform-user-change-password.png new file mode 100644 index 000000000..1c4cf2f34 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/platform-user-change-password.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/plugin-develop-1.png b/ER-Model/openGauss-datakit/doc/_resources/plugin-develop-1.png new file mode 100644 index 000000000..7c29da4e5 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/plugin-develop-1.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/plugin-mysql-migrition.01.png b/ER-Model/openGauss-datakit/doc/_resources/plugin-mysql-migrition.01.png new file mode 100644 index 000000000..31f9c9bfb Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/plugin-mysql-migrition.01.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/server-manage-error-reporting.png b/ER-Model/openGauss-datakit/doc/_resources/server-manage-error-reporting.png new file mode 100644 index 000000000..3b9ea2d83 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/server-manage-error-reporting.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/server-manage-import-completed.png b/ER-Model/openGauss-datakit/doc/_resources/server-manage-import-completed.png new file mode 100644 index 000000000..4f9e1eae1 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/server-manage-import-completed.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/server-manage-parsing-files.png b/ER-Model/openGauss-datakit/doc/_resources/server-manage-parsing-files.png new file mode 100644 index 000000000..a0e3b2bcf Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/server-manage-parsing-files.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/server-manage-template-download.png b/ER-Model/openGauss-datakit/doc/_resources/server-manage-template-download.png new file mode 100644 index 000000000..dd5e73514 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/server-manage-template-download.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/server-manage-template.png b/ER-Model/openGauss-datakit/doc/_resources/server-manage-template.png new file mode 100644 index 000000000..a30f596a4 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/server-manage-template.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/sys-setting-enter.jpg b/ER-Model/openGauss-datakit/doc/_resources/sys-setting-enter.jpg new file mode 100644 index 000000000..86ddf09ff Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/sys-setting-enter.jpg differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/sys-setting-file-path-sql.jpg b/ER-Model/openGauss-datakit/doc/_resources/sys-setting-file-path-sql.jpg new file mode 100644 index 000000000..72c89e679 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/sys-setting-file-path-sql.jpg differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/sys-setting-file-path.png b/ER-Model/openGauss-datakit/doc/_resources/sys-setting-file-path.png new file mode 100644 index 000000000..908ea3af8 Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/sys-setting-file-path.png differ diff --git a/ER-Model/openGauss-datakit/doc/_resources/sys-setting.png b/ER-Model/openGauss-datakit/doc/_resources/sys-setting.png new file mode 100644 index 000000000..8aae8405a Binary files /dev/null and b/ER-Model/openGauss-datakit/doc/_resources/sys-setting.png differ diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/.gitignore b/ER-Model/openGauss-datakit/jsch-session-pool/.gitignore new file mode 100644 index 000000000..5ff6309b7 --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/pom.xml b/ER-Model/openGauss-datakit/jsch-session-pool/pom.xml new file mode 100644 index 000000000..0f1ebdd6e --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/pom.xml @@ -0,0 +1,95 @@ + + + 4.0.0 + + org.opengauss + visualtool-parent + ${admin.version} + ../../pom.xml + + jsch-session-pool + jsch-session-pool + + + + 17 + ${java.version} + ${java.version} + UTF-8 + + + + + + com.github.mwiede + jsch + ${jsch.version} + + + org.projectlombok + lombok + + + + org.apache.commons + commons-pool2 + + + cn.hutool + hutool-all + ${hutool.version} + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-resources-plugin + ${maven-resources-plugin.version} + + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + attach-sources + + jar + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/readme.md b/ER-Model/openGauss-datakit/jsch-session-pool/readme.md new file mode 100644 index 000000000..dde4403ba --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/readme.md @@ -0,0 +1,134 @@ +# JSCH Session Pool + +一个支持多主机多用户的JSCH Session连接池库,提供高效的SFTP/EXEC操作能力。 + +## 特性 + +- 多主机多用户Session池管理 +- 连接复用,减少认证开销 +- 大文件传输进度监控 +- 并发安全设计 +- 完善的连接池统计 +- 支持公钥/密码两种认证方式 + +## 安装 + +在您的项目中添加Maven依赖: + +```xml + + org.opengauss + jsch-session-pool + xxx + +``` + +## 快速开始 +- 基本使用 +```java +// 创建配置 +SftpConfig config = JschUtils.createBasicConfig("sftp.example.com", "user", "pass"); +// 上传文件 +SftpOperations.upload(config, "local.txt","/remote/local.txt", + JschUtils.createConsoleProgressCallback("Upload")); + // 下载文件 + SftpOperations.download(config, "/remote/largefile.zip","download.zip", + JschUtils.createConsoleProgressCallback("Download")); + // 关闭所有连接池(应用退出时调用) + JschSessionPool.closeAllPools(); +``` + +高级配置 +```java +// 创建高性能连接池配置 +GenericObjectPoolConfig poolConfig = JschUtils.createHighPerfPoolConfig(); + +// 创建带密钥的配置 +SftpConfig config = new SftpConfig("sftp.example.com", "user", null) + .withPrivateKey("/path/to/private_key") + .withKnownHosts("/path/to/known_hosts") + .withTimeout(15000) + .withPoolConfig(poolConfig); + +// 使用自定义进度回调 +ProgressCallback callback = new ProgressCallback() { + @Override + public void progress(long current, long total) { + // 自定义进度处理 + } + + // ...其他方法实现 +}; + +// 上传大文件 +SftpOperations.upload(config, "largefile.zip", "/remote/largefile.zip", callback); +``` + +连接池监控 +```java +// 获取连接池统计信息 +String stats = JschSessionPool.getPoolStats(); +System.out.println(stats); + +// 输出示例 +/* +SFTP Connection Pool Statistics: +======================================== +Host: sftp1.example.com:22, User: user1, Auth: password + Active: 2, Idle: 3, Waiters: 0 + Created: 5, Destroyed: 0 +---------------------------------------- +Host: sftp2.example.com:22, User: user2, Auth: key + Active: 5, Idle: 2, Waiters: 1 + Created: 8, Destroyed: 1 +---------------------------------------- +*/ +``` + +最佳实践 +应用退出时关闭连接池 + +```java +Runtime.getRuntime().addShutdownHook(new Thread(JschSessionPool::closeAllPools)); +``` + +合理配置连接池 + +```java +// 根据业务需求调整 +GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig<>(); +poolConfig.setMaxTotal(20); // 最大连接数 +poolConfig.setMaxIdle(10); // 最大空闲连接 +poolConfig.setMinIdle(5); // 最小空闲连接 +进度回调节流 +``` + +```java +ProgressCallback callback = new ProgressCallback() { +private long lastUpdate = 0; + + @Override + public void progress(long current, long total) { + long now = System.currentTimeMillis(); + if (now - lastUpdate > 1000) { // 每秒更新一次 + updateProgressUI(current, total); + lastUpdate = now; + } + } + // ...其他方法 +}; +``` + +# 服务器SSH服务配置 +调整 SSH 服务配置(/etc/ssh/sshd_config),放宽并发限制: + +## 编辑配置 +sudo vi /etc/ssh/sshd_config + +## 调整以下参数(根据需求设置) + MaxStartups 100:30:200 未认证连接:最大100,30%概率拒绝超过100的连接,最多200 + + MaxSessions 50 每个连接的最大会话数 + +## 重启SSH服务 +sudo systemctl restart sshd diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/ChannelProvider.java b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/ChannelProvider.java new file mode 100644 index 000000000..34461764f --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/ChannelProvider.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + */ + +package org.opengauss.jsch.pool; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; + +/** + * ChannelProvider + * + * @author: wangchao + * @Date: 2025/8/4 16:26 + * @since 7.0.0-RC2 + **/ +public class ChannelProvider { + /** + * get sftp channel + * + * @param session session + * @return sftp channel + * @throws JSchException JSchException + */ + public static ChannelSftp getSftpChannel(Session session) throws JSchException { + if (!session.isConnected()) { + throw new JSchException("Session is not connected"); + } + Channel channel = session.openChannel("sftp"); + if (channel instanceof ChannelSftp sftp) { + sftp.connect(); + return sftp; + } + throw new JSchException("Channel is not sftp"); + } + + /** + * get exec channel + * + * @param session session + * @return exec channel + * @throws JSchException JSchException + */ + public static ChannelExec getExecChannel(Session session) throws JSchException { + if (!session.isConnected()) { + throw new JSchException("Session is not connected"); + } + Channel channel = session.openChannel("exec"); + if (channel instanceof ChannelExec exec) { + return exec; + } + throw new JSchException("Channel is not exec"); + } + + /** + * close channel + * + * @param channel channel + */ + public static void closeChannel(Channel channel) { + if (channel != null && channel.isConnected()) { + channel.disconnect(); + } + } +} diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/ExecOperations.java b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/ExecOperations.java new file mode 100644 index 000000000..fac749163 --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/ExecOperations.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + */ + +package org.opengauss.jsch.pool; + +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; + +import cn.hutool.core.thread.ThreadUtil; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.jsch.pool.config.SessionConfig; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * ExecOperations + * + * @author: wangchao + * @Date: 2025/8/4 16:20 + * @since 7.0.0-RC2 + **/ +@Slf4j +public class ExecOperations { + private static final int DEFAULT_TIMEOUT = 30000; + + /** + * semaphore size=4 , used to control the number of concurrent threads + * fair mode to avoid thread starvation + */ + private static final Semaphore SEMAPHORE = new Semaphore(4, true); + + /** + * execute single command + * + * @param config session config + * @param command command + * @return exec result + * @throws JSchException exec exception + */ + public static ExecResult executeCommand(SessionConfig config, String command) throws JSchException { + if (command.contains("nohup")) { + executeCommand(config, command, DEFAULT_TIMEOUT, false); + } + return executeCommand(config, command, DEFAULT_TIMEOUT, true); + } + + /** + * execute single command + * + * @param config session config + * @param command command + * @param commandTimeout command timeout + * @return exec result + * @throws JSchException exec exception + */ + public static ExecResult executeCommand(SessionConfig config, String command, int commandTimeout) + throws JSchException { + if (command.contains("nohup")) { + executeCommand(config, command, commandTimeout, false); + } + return executeCommand(config, command, commandTimeout, true); + } + + /** + * execute multiple commands + * + * @param config session config + * @param commands commands + * @return exec result list + * @throws JSchException exec exception + */ + public static List executeCommands(SessionConfig config, String commands) throws JSchException { + return executeCommands(config, commands.split(";"), DEFAULT_TIMEOUT); + } + + /** + * execute multiple commands + * + * @param config session config + * @param commands commands + * @param timeout timeout + * @return exec result list + * @throws JSchException exec exception + */ + public static List executeCommands(SessionConfig config, String[] commands, int timeout) + throws JSchException { + List results = new ArrayList<>(); + for (String command : commands) { + if (!command.trim().isEmpty()) { + if (command.contains("nohup")) { + results.add(executeCommand(config, command, DEFAULT_TIMEOUT, false)); + } else { + results.add(executeCommand(config, command, DEFAULT_TIMEOUT, true)); + } + } + } + return results; + } + + /** + * execute single command + * + * @param config session config + * @param command command + * @param timeout timeout + * @param isPty isPty + * @return exec result + * @throws JSchException exec exception + */ + public static ExecResult executeCommand(SessionConfig config, String command, int timeout, boolean isPty) + throws JSchException { + Session session = null; + ChannelExec channel = null; + ExecResult result = new ExecResult(); + boolean isAcquired = false; + try { + if (!SEMAPHORE.tryAcquire(timeout, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Failed to acquire semaphore within timeout: " + timeout + "ms"); + } + isAcquired = true; + session = JschSessionPool.borrowSession(config); + channel = ChannelProvider.getExecChannel(session); + channel.setPty(isPty); + channel.setCommand(command); + channel.connect(); + InputStream in = channel.getInputStream(); + InputStream err = channel.getExtInputStream(); + Thread outThread = readStream(in, result.getOutput()); + Thread errThread = readStream(err, result.getError()); + long startTime = System.currentTimeMillis(); + while (!channel.isClosed()) { + if (System.currentTimeMillis() - startTime > timeout) { + throw new TimeoutException("Command execution timeout"); + } + ThreadUtil.safeSleep(100); + } + outThread.join(1000); + errThread.join(1000); + result.setExitCode(channel.getExitStatus()); + return result; + } catch (TimeoutException e) { + throw new JSchException("Command execution timeout"); + } catch (IOException | InterruptedException e) { + throw new JSchException("Command execution failed", e); + } finally { + if (isAcquired) { + SEMAPHORE.release(); + } + ChannelProvider.closeChannel(channel); + if (session != null) { + JschSessionPool.returnSession(config, session); + } + } + } + + /** + * read stream asynchronously + * + * @param in input stream + * @param output output stream + * @return thread read stream thread + */ + private static Thread readStream(InputStream in, StringBuilder output) { + Thread thread = ThreadUtil.newThread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, Charset.defaultCharset()))) { + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + } + } catch (IOException ex) { + log.warn("exec operations read stream asynchronously has some error : {}", ex.getMessage()); + } + }, "read-stream-thread"); + thread.start(); + return thread; + } + + /** + * exec result + */ + @Data + public static class ExecResult { + private final StringBuilder output = new StringBuilder(); + private final StringBuilder error = new StringBuilder(); + private int exitCode = -1; + + /** + * check exec result is success + * + * @return true if success, false otherwise + */ + public boolean isSuccess() { + return exitCode == 0; + } + + /** + * get exec result + * + * @return exec result + */ + public String getResult() { + String result = output.toString().trim(); + if (result.endsWith("\n")) { + return result.substring(0, result.length() - 1); + } else { + return result; + } + } + + @Override + public String toString() { + return String.format(Locale.getDefault(), "ExecResult[exitCode=%d, output='%s', error='%s']", exitCode, + output.toString().trim(), error.toString().trim()); + } + } +} diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/JschSessionPool.java b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/JschSessionPool.java new file mode 100644 index 000000000..45e267768 --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/JschSessionPool.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + */ + +package org.opengauss.jsch.pool; + +import com.jcraft.jsch.Session; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.JSch; + +import org.apache.commons.pool2.BasePooledObjectFactory; +import org.apache.commons.pool2.DestroyMode; +import org.apache.commons.pool2.PooledObject; +import org.apache.commons.pool2.impl.DefaultPooledObject; +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.opengauss.jsch.pool.config.SessionConfig; +import org.opengauss.jsch.pool.exception.JschPoolException; +import org.opengauss.jsch.pool.records.HostUserKey; +import org.opengauss.jsch.pool.utils.JschUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +/** + * JschSessionPool session pool ,support multi-host ,multi-user multi session + * + * @author: wangchao + * @Date: 2025/8/4 12:21 + * @since 7.0.0-RC2 + **/ +public class JschSessionPool { + private static final Logger logger = LoggerFactory.getLogger(JschSessionPool.class); + private static final Map> POOL_MAP = new ConcurrentHashMap<>(); + private static final GenericObjectPoolConfig DEFAULT_POOL_CONFIG = JschUtils.createDefaultPoolConfig(); + + /** + * create session pool + * + * @param config session config + * @return session pool + */ + public static synchronized GenericObjectPool getOrCreatePool(SessionConfig config) { + HostUserKey key = config.getKey(); + GenericObjectPool pool = POOL_MAP.get(key); + if (pool == null) { + GenericObjectPoolConfig poolConfig = config.getPoolConfig() != null + ? config.getPoolConfig() + : DEFAULT_POOL_CONFIG; + pool = new GenericObjectPool<>(new SessionFactory(config), poolConfig); + POOL_MAP.put(key, pool); + preloadPool(pool, poolConfig.getMinIdle()); + } + return pool; + } + + /** + * preload pool + * + * @param pool session pool + * @param minIdle min idle + */ + private static void preloadPool(GenericObjectPool pool, int minIdle) { + Session[] sessions = new Session[minIdle]; + try { + for (int i = 0; i < minIdle; i++) { + sessions[i] = pool.borrowObject(); + } + } catch (Exception e) { + logger.warn("Preloading connection pool failed", e); + } finally { + for (Session session : sessions) { + if (session != null) { + try { + pool.returnObject(session); + } catch (Exception e) { + logger.warn("Preloading connection pool,when returnObject failed {}", e.getMessage()); + } + } + } + } + } + + /** + * close all pools + */ + public static synchronized void closeAllPools() { + for (Map.Entry> entry : POOL_MAP.entrySet()) { + entry.getValue().close(); + } + POOL_MAP.clear(); + } + + /** + * close pool + * + * @param key host user key + */ + public static synchronized void closePool(HostUserKey key) { + GenericObjectPool pool = POOL_MAP.remove(key); + if (pool != null) { + pool.close(); + } + } + + /** + * borrow session + * + * @param config session config + * @return session + * @throws JschPoolException borrow exception + */ + public static Session borrowSession(SessionConfig config) throws JschPoolException { + GenericObjectPool pool = getOrCreatePool(config); + try { + return pool.borrowObject(); + } catch (Exception e) { + pool.clear(); + throw new JschPoolException( + "Failed to borrow session from pool [" + config.getHost() + ":" + config.getUsername() + "] caused by " + + e.getMessage(), e); + } + } + + /** + * return session + * + * @param config session config + * @param session session + */ + public static void returnSession(SessionConfig config, Session session) { + if (session == null) { + return; + } + try { + GenericObjectPool pool = POOL_MAP.get(config.getKey()); + if (pool != null) { + pool.returnObject(session); + } else { + destroySession(session); + } + } catch (Exception e) { + logger.error("Failed to return session to pool", e); + destroySession(session); + } + } + + /** + * destroy session + * + * @param session session + */ + private static void destroySession(Session session) { + try { + if (session != null && session.isConnected()) { + session.disconnect(); + } + } catch (Exception e) { + logger.debug("Error destroying SFTP channel", e); + } + } + + /** + * get pool stats + * + * @return pool stats + */ + public static String getPoolStats() { + StringBuilder sb = new StringBuilder("SSH Session Pool Statistics:\n"); + sb.append("========================================\n"); + for (Map.Entry> entry : POOL_MAP.entrySet()) { + HostUserKey key = entry.getKey(); + GenericObjectPool pool = entry.getValue(); + sb.append( + String.format(Locale.getDefault(), "Host: %s:%d, User: %s, AuthType: %s, Channel: %s\n", key.host(), + key.port(), key.username(), key.authType(), key.channelType())); + sb.append(String.format(Locale.getDefault(), " Active: %d, Idle: %d, Waiters: %d\n", pool.getNumActive(), + pool.getNumIdle(), pool.getNumWaiters())); + sb.append(String.format(Locale.getDefault(), " Created: %d, Destroyed: %d\n", pool.getCreatedCount(), + pool.getDestroyedCount())); + sb.append("----------------------------------------\n"); + } + return sb.toString(); + } + + /** + * session factory + */ + private static class SessionFactory extends BasePooledObjectFactory { + private final SessionConfig config; + + /** + * session factory + * + * @param config session config + */ + private SessionFactory(SessionConfig config) { + this.config = config; + } + + @Override + public Session create() throws JSchException { + JSch jsch = new JSch(); + if (config.getPrivateKey() != null) { + jsch.addIdentity(config.getPrivateKey()); + } + if (config.getKnownHosts() != null) { + jsch.setKnownHosts(config.getKnownHosts()); + } + Session session = jsch.getSession(config.getUsername(), config.getHost(), config.getPort()); + session.setPassword(config.getPassword()); + Properties sessionConfig = new Properties(); + sessionConfig.put("StrictHostKeyChecking", config.getKnownHosts() != null ? "yes" : "no"); + session.setConfig(sessionConfig); + session.setTimeout(config.getTimeout()); + session.connect(); + return session; + } + + @Override + public void destroyObject(PooledObject p, DestroyMode destroyMode) throws Exception { + super.destroyObject(p, destroyMode); + } + + @Override + public boolean validateObject(PooledObject p) { + Session session = p.getObject(); + return session.isConnected(); + } + + @Override + public PooledObject wrap(Session session) { + return new DefaultPooledObject<>(session); + } + } +} diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/ProgressCallback.java b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/ProgressCallback.java new file mode 100644 index 000000000..8b6849275 --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/ProgressCallback.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +package org.opengauss.jsch.pool; + +/** + * progress callback + * + * @author: wangchao + * @Date: 2025/8/4 14:05 + * @Description: ProgressCallback + * @since 7.0.0 + **/ +public interface ProgressCallback { + /** + * progress + * + * @param current current + * @param total total + * @return percent + */ + String progress(long current, long total); + + /** + * completed + */ + void completed(); + + /** + * failed + * + * @param t throwable + */ + void failed(Throwable t); +} diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/SftpOperations.java b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/SftpOperations.java new file mode 100644 index 000000000..ed0b55320 --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/SftpOperations.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + */ + +package org.opengauss.jsch.pool; + +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpATTRS; +import com.jcraft.jsch.SftpException; +import com.jcraft.jsch.SftpProgressMonitor; + +import org.opengauss.jsch.pool.config.SessionConfig; +import org.opengauss.jsch.pool.exception.JschPoolException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.atomic.AtomicLong; + +/** + * SftpOperations + * + * @author: wangchao + * @Date: 2025/8/4 14:08 + * @since 7.0.0-RC2 + **/ +public class SftpOperations { + private static final Logger logger = LoggerFactory.getLogger(SftpOperations.class); + private static final int DEFAULT_PORT = 22; + + /** + * upload file with progress monitor + * + * @param config session config + * @param localPath local file path + * @param remotePath remote file path + * @param callback progress callback + * @throws JschPoolException upload exception + */ + public static void upload(SessionConfig config, String localPath, String remotePath, ProgressCallback callback) + throws JschPoolException { + upload(config, new File(localPath), remotePath, callback); + } + + /** + * download file with progress monitor + * + * @param config session config + * @param remotePath remote file path + * @param localPath local file path + * @param callback progress callback + * @throws JschPoolException download exception + */ + public static void download(SessionConfig config, String remotePath, String localPath, ProgressCallback callback) + throws JschPoolException { + download(config, remotePath, new File(localPath), callback); + } + + /** + * download file with progress monitor + * + * @param config session config + * @param remotePath remote file path + * @param localFile local file object + * @param callback progress callback + * @throws JschPoolException download exception + */ + public static void download(SessionConfig config, String remotePath, File localFile, ProgressCallback callback) + throws JschPoolException { + Session session = null; + ChannelSftp channel = null; + try { + session = JschSessionPool.borrowSession(config); + channel = ChannelProvider.getSftpChannel(session); + Path normalizedLocalPath; + try { + normalizedLocalPath = Paths.get(localFile.getAbsolutePath()).normalize().toAbsolutePath(); + } catch (InvalidPathException e) { + throw new JschPoolException("Invalid local file path", e); + } + File safeLocalFile = normalizedLocalPath.toFile(); + File parentDir = safeLocalFile.getParentFile(); + if (parentDir != null && !parentDir.exists()) { + parentDir.mkdirs(); + } + String normalizedRemotePath = remotePath.replace("\\", "/"); + if (normalizedRemotePath.contains("../") || normalizedRemotePath.contains("/..")) { + throw new JschPoolException("Remote path contains directory traversal attempts"); + } + SftpATTRS attrs = channel.stat(normalizedRemotePath); + long fileSize = attrs.getSize(); + SftpProgressMonitor monitor = new CallbackProgressMonitor(callback, fileSize); + channel.get(normalizedRemotePath, safeLocalFile.getAbsolutePath(), monitor, ChannelSftp.RESUME); + if (callback != null) { + callback.completed(); + } + } catch (JSchException | SftpException e) { + if (callback != null) { + callback.failed(e); + } + throw new JschPoolException("download file failed", e); + } finally { + ChannelProvider.closeChannel(channel); + if (session != null) { + JschSessionPool.returnSession(config, session); + } + } + } + + /** + * upload file with progress monitor + * + * @param config session config + * @param localFile local file object + * @param remotePath remote file path + * @param callback progress callback + * @throws JschPoolException upload exception + */ + public static void upload(SessionConfig config, File localFile, String remotePath, ProgressCallback callback) + throws JschPoolException { + Session session = null; + ChannelSftp channel = null; + FileInputStream fis = null; + try { + session = JschSessionPool.borrowSession(config); + channel = ChannelProvider.getSftpChannel(session); + fis = new FileInputStream(localFile); + SftpProgressMonitor monitor = new CallbackProgressMonitor(callback, localFile.length()); + createParentDirs(channel, remotePath); + channel.put(fis, remotePath, monitor, ChannelSftp.OVERWRITE); + if (callback != null) { + callback.completed(); + } + } catch (JSchException | SftpException | FileNotFoundException e) { + if (callback != null) { + callback.failed(e); + } + throw new JschPoolException("upload file failed", e); + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + logger.error("close file input stream failed", e); + } + } + ChannelProvider.closeChannel(channel); + if (session != null) { + JschSessionPool.returnSession(config, session); + } + } + } + + /** + * create remote directory (recursively create parent directories) + * + * @param channel sftp channel + * @param remotePath remote file path + * @throws SftpException create directory exception + */ + private static void createParentDirs(ChannelSftp channel, String remotePath) throws SftpException { + String parent = new File(remotePath).getParent().replace("\\", "/"); + if (parent != null && !parent.equals("/")) { + try { + channel.stat(parent); + } catch (SftpException e) { + if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { + createParentDirs(channel, parent); + } + } + channel.mkdir(parent); + } + } + + /** + * progress monitor adapter + */ + private static class CallbackProgressMonitor implements SftpProgressMonitor { + private final ProgressCallback callback; + private final long totalSize; + private final AtomicLong transferred = new AtomicLong(0); + + /** + * constructor + * + * @param callback progress callback + * @param totalSize total size + */ + public CallbackProgressMonitor(ProgressCallback callback, long totalSize) { + this.callback = callback; + this.totalSize = totalSize; + } + + @Override + public void init(int op, String src, String dest, long max) { + } + + @Override + public boolean count(long count) { + long current = transferred.addAndGet(count); + callback.progress(current, totalSize); + return true; + } + + @Override + public void end() { + } + } + + /** + * delete file + * + * @param config session config + * @param remotePath remote file path + * @throws JschPoolException delete file exception + */ + public static void deleteFile(SessionConfig config, String remotePath) throws JschPoolException { + Session session = null; + ChannelSftp channel = null; + try { + session = JschSessionPool.borrowSession(config); + channel = ChannelProvider.getSftpChannel(session); + channel.rm(remotePath); + } catch (JSchException | SftpException e) { + throw new JschPoolException("delete file failed", e); + } finally { + ChannelProvider.closeChannel(channel); + if (session != null) { + JschSessionPool.returnSession(config, session); + } + } + } + + /** + * create directory + * + * @param config session config + * @param remotePath remote directory path + * @throws JschPoolException create directory exception + */ + public static void createDirectory(SessionConfig config, String remotePath) throws JschPoolException { + Session session = null; + ChannelSftp channel = null; + try { + session = JschSessionPool.borrowSession(config); + channel = ChannelProvider.getSftpChannel(session); + createParentDirs(channel, remotePath); + channel.mkdir(remotePath); + } catch (JSchException | SftpException e) { + throw new JschPoolException("create directory failed", e); + } finally { + ChannelProvider.closeChannel(channel); + if (session != null) { + JschSessionPool.returnSession(config, session); + } + } + } + + /** + * check file exists + * + * @param config session config + * @param remotePath remote file path + * @return true:exists false:not exists + * @throws JschPoolException check file exists exception + */ + public static boolean exists(SessionConfig config, String remotePath) throws JschPoolException { + Session session = null; + ChannelSftp channel = null; + try { + session = JschSessionPool.borrowSession(config); + channel = ChannelProvider.getSftpChannel(session); + channel.stat(remotePath); + return true; + } catch (SftpException e) { + if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { + return false; + } + throw new JschPoolException("check file exists failed", e); + } catch (JSchException ex) { + throw new JschPoolException("check file exists failed", ex); + } finally { + ChannelProvider.closeChannel(channel); + if (session != null) { + JschSessionPool.returnSession(config, session); + } + } + } +} diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/config/SessionConfig.java b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/config/SessionConfig.java new file mode 100644 index 000000000..21e2f2f25 --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/config/SessionConfig.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.jsch.pool.config; + +import com.jcraft.jsch.Session; + +import lombok.Getter; + +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.opengauss.jsch.pool.enums.AuthType; +import org.opengauss.jsch.pool.enums.ChannelType; +import org.opengauss.jsch.pool.records.HostUserKey; + +import java.util.Objects; + +/** + * SessionConfig + * + * @author: wangchao + * @Date: 2025/8/4 14:04 + * @since 7.0.0-RC2 + **/ +public class SessionConfig { + @Getter + private final String host; + @Getter + private final int port; + @Getter + private final String username; + @Getter + private final String password; + @Getter + private String privateKey; + @Getter + private String knownHosts; + + /** + * default timeout 10s + */ + @Getter + private int timeout = 10000; + @Getter + private GenericObjectPoolConfig poolConfig; + + /** + * default channel type exec + */ + private ChannelType channelType = ChannelType.EXEC; + private boolean isReuseSessionForMultipleChannels = false; + + /** + * build session config , default port 22 + * + * @param host host + * @param username username + * @param password password + */ + public SessionConfig(String host, String username, String password) { + this(host, 22, username, password); + } + + /** + * build session config + * + * @param host host + * @param port port + * @param username username + * @param password password + */ + public SessionConfig(String host, int port, String username, String password) { + this.host = host; + this.port = port; + this.username = username; + this.password = password; + } + + /** + * set private key + * + * @param privateKey private key + * @return this + */ + public SessionConfig withPrivateKey(String privateKey) { + this.privateKey = privateKey; + return this; + } + + /** + * set known hosts + * + * @param knownHosts known hosts + * @return this + */ + public SessionConfig withKnownHosts(String knownHosts) { + this.knownHosts = knownHosts; + return this; + } + + /** + * set timeout + * + * @param timeout timeout + * @return this + */ + public SessionConfig withTimeout(int timeout) { + this.timeout = timeout; + return this; + } + + /** + * set channel type + * + * @param channelType channel type + * @return this + */ + public SessionConfig withChannelType(ChannelType channelType) { + if (!Objects.equals(channelType, ChannelType.EXEC) && !Objects.equals(channelType, ChannelType.SFTP) + && !Objects.equals(channelType, ChannelType.SHELL)) { + throw new IllegalArgumentException("Invalid channel type: " + channelType); + } + this.channelType = channelType; + return this; + } + + /** + * get key + * + * @return key + */ + public HostUserKey getKey() { + AuthType authType = privateKey != null ? AuthType.KEY : AuthType.PASSWORD; + return new HostUserKey(host, port, username, authType, channelType); + } + + /** + * set isAllow session reuse + * + * @param isAllow if true, session can be reused for multiple channels + * @return this + */ + public SessionConfig allowSessionReuse(boolean isAllow) { + this.isReuseSessionForMultipleChannels = isAllow; + return this; + } + + /** + * get allow session reuse + * + * @return if true, session can be reused for multiple channels + */ + public boolean isSessionReuseAllowed() { + return isReuseSessionForMultipleChannels; + } +} diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/enums/AuthType.java b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/enums/AuthType.java new file mode 100644 index 000000000..a5989c6d8 --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/enums/AuthType.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.jsch.pool.enums; + +/** + * AuthType + * + * @author: wangchao + * @Date: 2025/8/4 14:04 + * @since 7.0.0-RC2 + **/ +public enum AuthType { + PASSWORD, + KEY, + UNKNOWN; +} diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/enums/ChannelType.java b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/enums/ChannelType.java new file mode 100644 index 000000000..139ee7d94 --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/enums/ChannelType.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.jsch.pool.enums; + +/** + * ChannelType + * + * @author: wangchao + * @Date: 2025/8/4 14:04 + * @since 7.0.0-RC2 + **/ +public enum ChannelType { + EXEC, + SHELL, + SFTP, + UNKNOWN; +} diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/exception/JschPoolException.java b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/exception/JschPoolException.java new file mode 100644 index 000000000..f0d0d4522 --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/exception/JschPoolException.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +package org.opengauss.jsch.pool.exception; + +import com.jcraft.jsch.JSchException; + +/** + * JschPoolException + * + * @author: wangchao + * @Date: 2025/8/11 16:14 + * @since 7.0.0-RC2 + **/ +public class JschPoolException extends JSchException { + /** + * jsch pool exception + * + * @param message exception message + * @param e throwable + */ + public JschPoolException(String message, Throwable e) { + super(message, e); + } + + /** + * jsch pool exception + * + * @param message exception message + */ + public JschPoolException(String message) { + super(message); + } +} diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/records/HostUserKey.java b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/records/HostUserKey.java new file mode 100644 index 000000000..801abfc8c --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/records/HostUserKey.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.jsch.pool.records; + +import org.opengauss.jsch.pool.enums.AuthType; +import org.opengauss.jsch.pool.enums.ChannelType; + +import java.util.Objects; + +/** + * host user key + * + * @param host host + * @param port port + * @param username username + * @param authType auth type + * @param channelType channel type + * @author: wangchao + * @Date: 2025/8/4 14:04 + * @since 7.0.0-RC2 + */ +public record HostUserKey(String host, int port, String username, AuthType authType, ChannelType channelType) { + private static final int DEFAULT_SSH_PORT = 22; + + /** + * constructor + * + * @param host host + * @param port port + * @param username username + * @param authType auth type + * @param channelType channel type + */ + public HostUserKey { + Objects.requireNonNull(host, "Host cannot be null"); + Objects.requireNonNull(username, "Username cannot be null"); + if (port < 1 || port > 65535) { + throw new IllegalArgumentException("Port must be 1-65535"); + } + if (authType == null) { + authType = AuthType.UNKNOWN; + } + if (channelType == null) { + channelType = ChannelType.UNKNOWN; + } + } + + /** + * constructor + * + * @param host host + * @param username username + * @param authType auth type + * @param channelType channel type + */ + public HostUserKey(String host, String username, AuthType authType, ChannelType channelType) { + this(host, DEFAULT_SSH_PORT, username, authType, channelType); + } +} diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/utils/JschUtils.java b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/utils/JschUtils.java new file mode 100644 index 000000000..670508208 --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/src/main/java/org/opengauss/jsch/pool/utils/JschUtils.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + */ + +package org.opengauss.jsch.pool.utils; + +import com.jcraft.jsch.Session; + +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.opengauss.jsch.pool.ProgressCallback; +import org.opengauss.jsch.pool.config.SessionConfig; + +import java.text.DecimalFormat; + +/** + * JschUtils + * + * @author: wangchao + * @Date: 2025/8/4 14:10 + * @since 7.0.0-RC2 + **/ +@Slf4j +public class JschUtils { + /** + * create default session pool config + * + * @return session pool config + */ + public static GenericObjectPoolConfig createDefaultPoolConfig() { + GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); + config.setMaxTotal(8); + config.setMaxIdle(4); + config.setMinIdle(2); + config.setTestOnBorrow(true); + config.setTestOnReturn(true); + config.setBlockWhenExhausted(true); + config.setMaxWaitMillis(5000); + return config; + } + + /** + * create high performance session pool config + * + * @return session pool config + */ + public static GenericObjectPoolConfig createHighPerfPoolConfig() { + GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); + config.setMaxTotal(20); + config.setMaxIdle(10); + config.setMinIdle(5); + config.setTestOnBorrow(true); + config.setTestWhileIdle(true); + config.setTimeBetweenEvictionRunsMillis(30000); + config.setMinEvictableIdleTimeMillis(600000); + return config; + } + + /** + * create console progress callback + * + * @param operationName operation name + * @return progress callback + */ + public static ProgressCallback createConsoleProgressCallback(String operationName) { + return new ProgressCallback() { + private static final ThreadLocal DF = ThreadLocal.withInitial( + () -> new DecimalFormat("#.00")); + private long lastLogTime = 0L; + private String percent = "0.00"; + + @Override + public String progress(long current, long total) { + long now = System.currentTimeMillis(); + if (now - lastLogTime > 1000) { + percent = DF.get().format(((double) current) / ((double) total) * 100.0); + log.info("{} progress: {}%", operationName, percent); + lastLogTime = now; + } + return percent; + } + + @Override + public void completed() { + log.info("{} completed", operationName); + } + + @Override + public void failed(Throwable t) { + log.error("{} failed: {}", operationName, t.getMessage()); + } + }; + } + + /** + * create basic session config + * + * @param host host + * @param username username + * @param password password + * @return session config + */ + public static SessionConfig createBasicConfig(String host, String username, String password) { + return new SessionConfig(host, username, password); + } + + /** + * create key based session config + * + * @param host host + * @param username username + * @param privateKey private key + * @return session config + */ + public static SessionConfig createKeyBasedConfig(String host, String username, String privateKey) { + return new SessionConfig(host, username, null).withPrivateKey(privateKey); + } +} diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/src/test/java/org/opengauss/jsch/pool/utils/JschUtilsTest.java b/ER-Model/openGauss-datakit/jsch-session-pool/src/test/java/org/opengauss/jsch/pool/utils/JschUtilsTest.java new file mode 100644 index 000000000..b187f0888 --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/src/test/java/org/opengauss/jsch/pool/utils/JschUtilsTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.jsch.pool.utils; + +import lombok.extern.slf4j.Slf4j; + +import org.junit.jupiter.api.Test; +import org.opengauss.jsch.pool.ExecOperations; +import org.opengauss.jsch.pool.JschSessionPool; +import org.opengauss.jsch.pool.config.SessionConfig; +import org.opengauss.jsch.pool.enums.ChannelType; + +import java.util.List; + +/** + * JschUtilsTest + * + * @author: wangchao + * @Date: 2025/8/4 15:53 + * @since 7.0.0-RC2 + **/ +@Slf4j +public class JschUtilsTest { + @Test + void test() throws Exception { + SessionConfig execConfig = new SessionConfig("192.168.0.xxx", "root", "xxx@123").withChannelType( + ChannelType.EXEC).withTimeout(15000); + // 执行单个命令 + try { + ExecOperations.ExecResult result = ExecOperations.executeCommand(execConfig, "ls -l /var/log"); + log.info("command exec result :{}", result); + log.info("exit code: {}", result.getExitCode()); + log.info("output: \n{}", result.getOutput()); + log.info("error: \n{}", result.getError()); + } catch (Exception e) { + log.error("execute single command failed", e); + } + // 执行多个命令 + try { + List results = ExecOperations.executeCommands(execConfig, + "date; free -m; df -h"); + for (int i = 0; i < results.size(); i++) { + log.info("command #{} result: {}", (i + 1), results.get(i)); + } + } catch (Exception e) { + log.error("execute multiple commands failed", e); + } + // 关闭所有连接池(应用退出时调用) + JschSessionPool.closeAllPools(); + } +} diff --git a/ER-Model/openGauss-datakit/jsch-session-pool/src/test/java/org/opengauss/jsch/pool/utils/MoreHostTaskTest.java b/ER-Model/openGauss-datakit/jsch-session-pool/src/test/java/org/opengauss/jsch/pool/utils/MoreHostTaskTest.java new file mode 100644 index 000000000..e4b1275e6 --- /dev/null +++ b/ER-Model/openGauss-datakit/jsch-session-pool/src/test/java/org/opengauss/jsch/pool/utils/MoreHostTaskTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.jsch.pool.utils; + +import lombok.extern.slf4j.Slf4j; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.opengauss.jsch.pool.ExecOperations; +import org.opengauss.jsch.pool.JschSessionPool; +import org.opengauss.jsch.pool.config.SessionConfig; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * MoreHostTaskTest + * + * @author: wangchao + * @Date: 2025/8/4 17:36 + * @since 7.0.0-RC2 + **/ +@Slf4j +public class MoreHostTaskTest { + private static final String DISK_TOTAL_MONITOR = "df -T --total | tail -n +2| tr -s \" \" | grep total"; + private static final String HOST_BASE_INFO = + "cat /proc/cpuinfo |grep \"processor\"|wc -l && grep MemFree /proc/meminfo " + + "| awk '{val=$2/1024}END{print val}' && df -Th | egrep -v \"(tmpfs|sr0)\" | tail -n +2| " + + "tr -s \" \" | cut -d \" \" -f5 | tr -d \"G\" | awk '{sum+=$1}END{print sum}'"; + private static final String CPU = "lscpu | grep \"CPU(s):\\|Architecture\\|MHz\""; + private static final String OS + = "cat /etc/os-release | grep ID= | head -n 1 | awk -F '=' '{print $2}' | sed 's/\\\"//g'"; + private static final String OS_VERSION + = "cat /etc/os-release | grep VERSION_ID= | head -n 1|awk -F '=' '{print $2}' | sed 's/\\\"//g'"; + + private static final String MEMORY = "free -g |head -n 2| tail -n 1 | tr -s \" \""; + private static final String JAVA_VERSION = "java -version"; + private static final String HOSTNAME = "hostname"; + private static final String CPU_USING = + "awk 'NR==1 {u=$2; n=$3; s=$4; i=$5; w=$6; x=$7; y=$8; z=$9} END {total=u+n+s+i+w+x+y+z; idle=i; printf \"%" + + ".1f\\n\", 100 - (idle*100/total)}' /proc/stat"; + private static final String NET_CARD_NAME + = "export PATH=$PATH:/sbin:/usr/sbin && ip -o addr show | grep %s | awk '{print $2}'"; + + private static Map configs = new ConcurrentHashMap<>(); + private static List commands = new LinkedList<>(); + + @BeforeAll + static void preEnv() { + configs.put("192.168.0.xxx", new SessionConfig("192.168.0.xxx", "root", "xxx@123")); + configs.put("192.168.0.xxx", new SessionConfig("192.168.0.xxx", "root", "xxx@123")); + configs.put("192.168.0.xxx", new SessionConfig("192.168.0.xxx", "root", "xxx@123")); + configs.put("192.168.0.xxx", new SessionConfig("192.168.0.xxx", "root", "xxx@123")); + configs.put("192.168.0.xxx", new SessionConfig("192.168.0.xxx", "root", "xxx@123")); + configs.put("192.168.0.xxx", new SessionConfig("192.168.0.xxx", "root", "xxx@123")); + commands.add(CPU); + commands.add(DISK_TOTAL_MONITOR); + commands.add(HOST_BASE_INFO); + commands.add(OS_VERSION); + commands.add(OS); + commands.add(JAVA_VERSION); + commands.add(HOSTNAME); + commands.add(MEMORY); + commands.add(NET_CARD_NAME); + commands.add(CPU_USING); + } + + @Test + void test() { + configs.forEach((host, config) -> { + commands.forEach(cmd -> { + try { + ExecOperations.ExecResult result = ExecOperations.executeCommand(config, cmd); + log.info("command exec result :{} {} {}", config.getHost(), cmd, result); + } catch (Exception e) { + log.error("command exec error :{} {} {}", config.getHost(), cmd, e); + } + }); + }); + // 关闭所有连接池(应用退出时调用) + JschSessionPool.closeAllPools(); + } + + @Test + void testAysnc() { + ExecutorService executor = Executors.newFixedThreadPool(8); + List tasks = new LinkedList<>(); + configs.forEach((host, config) -> { + commands.forEach(cmd -> { + tasks.add(() -> { + try { + ExecOperations.ExecResult result = ExecOperations.executeCommand(config, cmd); + log.info("command exec result :{} {} {}", config.getHost(), cmd, result); + } catch (Exception e) { + log.error("command exec error :{} {} ", config.getHost(), cmd, e); + } + }); + }); + }); + // 2. 打乱任务顺序 + Collections.shuffle(tasks); + // 3. 提交打乱后的任务 + tasks.forEach(executor::submit); + // 关闭线程池(同之前逻辑) + executor.shutdown(); + try { + if (!executor.awaitTermination(5, TimeUnit.MINUTES)) { + executor.shutdownNow(); + } + } catch (InterruptedException e) { + executor.shutdownNow(); + } + log.info("thread pool stats:{}", JschSessionPool.getPoolStats()); + // 最后关闭所有连接池 + JschSessionPool.closeAllPools(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/pom.xml b/ER-Model/openGauss-datakit/visualtool-api/pom.xml new file mode 100644 index 000000000..d3311acf0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/pom.xml @@ -0,0 +1,193 @@ + + + + org.opengauss + visualtool-parent + ${admin.version} + ../../pom.xml + + 4.0.0 + jar + visualtool-api + + + visualtool-api + + + false + ${web.build.skip} + ../visualtool-ui + ../visualtool-ui/dist + static + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-websocket + + + org.springframework.boot + spring-boot-starter-test + test + + + org.opengauss + opengauss-jdbc + + + org.opengauss + visualtool-framework + + + org.springframework.boot + spring-boot-starter-actuator + + + org.intarkDB + intarkDB-jdbc + 1.0 + system + ${project.basedir}/../../lib/intarkdb-jdbc-1.0.0.1.jar + + + io.opentelemetry + opentelemetry-sdk + + + junit + junit + test + + + + + + + com.gitee.starblues + spring-brick-maven-packager + ${spring-brick.version} + + main + + org.opengauss.admin.AdminApplication + jar + openGauss-datakit-${parent.version} + + + + + + repackage + + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.2.0 + + + + ${web.build.path} + + **/* + + false + + + ${basedir}/src/main/resources/${web.resources.path} + + **/* + + false + + + ${web.clean.skip} + true + + + + org.codehaus.mojo + exec-maven-plugin + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.0 + + + copy-web + generate-resources + + copy-resources + + + ${basedir}/src/main/resources + + + ${web.build.path} + ${web.resources.path} + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + default-prepare-agent + + prepare-agent + + + + report + prepare-package + + report + + + target/jacoco.exec + + + + junit-test + test + + report + + + target/jacoco.exec + + + + + + + ${project.artifactId} + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/AdminApplication.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/AdminApplication.java new file mode 100644 index 000000000..dcff792ee --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/AdminApplication.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AdminApplication.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/AdminApplication.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin; + +import com.gitee.starblues.loader.launcher.SpringBootstrap; +import com.gitee.starblues.loader.launcher.SpringMainBootstrap; + +import cn.hutool.core.thread.ThreadUtil; + +import org.opengauss.admin.common.utils.AesGcmUtils; +import org.opengauss.admin.common.utils.RsaUtils; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.context.request.RequestContextListener; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +/** + * Application Main + * + * @author xielibo + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +@ComponentScan({"org.opengauss.admin", "org.opengauss.agent"}) +public class AdminApplication implements SpringBootstrap { + public static void main(String[] args) { + SpringMainBootstrap.launch(AdminApplication.class, args); + } + + @Override + public void run(String[] args) throws Exception { + ThreadUtil.execute(RsaUtils::publicKey); + ThreadUtil.execute(() -> { + ThreadUtil.sleep(1000); + AesGcmUtils.initializeKey(); + }); + SecurityContextHolder.setStrategyName("MODE_INHERITABLETHREADLOCAL"); + SpringApplication.run(AdminApplication.class, args); + } + + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } + @Bean + public RequestContextListener requestContextListener() { + return new RequestContextListener(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/AdminServletInitializer.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/AdminServletInitializer.java new file mode 100644 index 000000000..c00e1e75d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/AdminServletInitializer.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AdminServletInitializer.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/AdminServletInitializer.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * AdminServletInitializer + * + * @author xielibo + */ +public class AdminServletInitializer extends SpringBootServletInitializer { + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(AdminApplication.class); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysLogController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysLogController.java new file mode 100644 index 000000000..63a92b009 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysLogController.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysLogController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysLogController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.controller; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.dto.SysLogConfigDto; +import org.opengauss.admin.common.core.vo.SysLogConfigVo; +import org.opengauss.admin.system.service.ISysLogService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.io.File; +import java.nio.file.Files; +import java.util.List; +import java.util.Map; + +/** + * System log controller + * + * @author snow + */ +@Slf4j +@RestController +@RequestMapping("/system/log") +public class SysLogController { + + @Autowired + ISysLogService sysLogService; + + /** + * List all configurable system log config + * @return Config + */ + @RequestMapping + public AjaxResult getAllLogConfig() { + SysLogConfigVo sysLogConfigVo = sysLogService.getAllLogConfig(); + return AjaxResult.success(sysLogConfigVo); + } + + /** + * Save system log config + * @param sysLogConfigDto + * @return + */ + @PostMapping + public AjaxResult saveAllLogConfig(@RequestBody SysLogConfigDto sysLogConfigDto) { + sysLogService.saveAllLogConfig(sysLogConfigDto); + return AjaxResult.success(); + } + + /** + * List all saved log file + * @return + */ + @GetMapping("files") + public AjaxResult files() { + List> files = sysLogService.listAllLogFile(); + return AjaxResult.success(files); + } + + /** + * Download single log file by filename + * @param filename the log filename + * @return + */ + @GetMapping("download") + public ResponseEntity download(String filename) throws Exception { + File logfile = sysLogService.getLogFileByName(filename); + byte[] bytes = Files.readAllBytes(logfile.toPath()); + ByteArrayResource bar = new ByteArrayResource(bytes); + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .header("Content-disposition", "attachment; filename=" + filename) + .body(bar); + } + + @GetMapping("print") + public AjaxResult testPrint() { + log.trace("Trace log"); + log.debug("Debug log"); + log.info("Info log"); + log.warn("Warn log"); + log.error("Error Log"); + return AjaxResult.success(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysLoginController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysLoginController.java new file mode 100644 index 000000000..66c16d57e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysLoginController.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysLoginController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysLoginController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.web.controller; + +import com.google.common.collect.Maps; +import org.opengauss.admin.common.constant.Constants; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.entity.SysMenu; +import org.opengauss.admin.common.core.domain.entity.SysUser; +import org.opengauss.admin.common.core.domain.model.LoginBody; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.enums.SysMenuRouteOpenPosition; +import org.opengauss.admin.common.utils.RsaUtils; +import org.opengauss.admin.common.utils.ServletUtils; +import org.opengauss.admin.framework.web.service.SysLoginService; +import org.opengauss.admin.framework.web.service.SysPermissionService; +import org.opengauss.admin.framework.web.service.TokenService; +import org.opengauss.admin.system.service.ISysMenuService; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * common + * + * @author xielibo + */ +@RestController +public class SysLoginController { + @Autowired + private SysLoginService loginService; + + @Autowired + private TokenService tokenService; + + @Autowired + private ISysMenuService menuService; + + @Autowired + private SysPermissionService permissionService; + + @PostMapping("/login") + public AjaxResult login(@RequestBody LoginBody loginBody) { + AjaxResult ajax = AjaxResult.success(); + String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode()); + ajax.put(Constants.TOKEN, token); + return ajax; + } + + /** + * get login public key + * + * @return public key + */ + @GetMapping("/pubKey") + public AjaxResult publicKey() { + return AjaxResult.success(RsaUtils.publicKey()); + } + + /** + * get user info + */ + @GetMapping("getInfo") + public AjaxResult getInfo() { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + SysUser user = loginUser.getUser(); + Set roles = permissionService.getRolePermission(user); + Set permissions = permissionService.getMenuPermission(user); + AjaxResult ajax = AjaxResult.success(); + ajax.put("user", user); + ajax.put("roles", roles); + ajax.put("permissions", permissions); + return ajax; + } + + /** + * get routers + */ + @GetMapping("getRouters") + public AjaxResult getRouters() { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + SysUser user = loginUser.getUser(); + List menus = menuService.selectMenuTreeByUserId(user.getUserId()); + String language = ServletUtils.getRequest().getHeader("language"); + return AjaxResult.success(menuService.buildMenus(menus, language)); + } + + /** + * get index instance router + */ + @GetMapping("getIndexInstanceRouters") + public AjaxResult getIndexStanceRoute(){ + List sysMenus = menuService.selectSpecialRouteList(SysMenuRouteOpenPosition.INDEX_INSTANCE_DATA.getCode()); + if (sysMenus.size() == 0) { + return AjaxResult.success(); + } + SysMenu m = sysMenus.get(0); + if (StringUtils.isNotBlank(m.getPluginTheme())) { + Map routeQuery = Maps.newHashMap(); + routeQuery.put("theme", m.getPluginTheme()); + m.setQuery(routeQuery); + } + return AjaxResult.success(m); + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysMenuController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysMenuController.java new file mode 100644 index 000000000..1760689e5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysMenuController.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysMenuController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysMenuController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.controller; + +import org.opengauss.admin.common.annotation.Log; +import org.opengauss.admin.common.constant.UserConstants; +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.TreeSelect; +import org.opengauss.admin.common.core.domain.entity.SysMenu; +import org.opengauss.admin.common.core.domain.entity.SysRole; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.enums.BusinessType; +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.common.enums.SysMenuVisible; +import org.opengauss.admin.common.utils.SecurityUtils; +import org.opengauss.admin.common.utils.ServletUtils; +import org.opengauss.admin.framework.web.service.TokenService; +import org.opengauss.admin.system.service.ISysMenuService; +import org.opengauss.admin.system.service.ISysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Menu Controller + * + * @author xielibo + */ +@RestController +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController { + + @Autowired + private ISysMenuService menuService; + + @Autowired + private TokenService tokenService; + + @Autowired + private ISysRoleService roleService; + + /** + * get Menu list + */ + @PreAuthorize("@ss.hasPermi('system:menu:list')") + @GetMapping("/list") + public AjaxResult list(SysMenu menu) { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + Integer userId = loginUser.getUser().getUserId(); + List menus = menuService.selectMenuList(menu, userId); + return AjaxResult.success(menus); + } + + /** + * get info by ID + */ + @PreAuthorize("@ss.hasPermi('system:menu:query')") + @GetMapping(value = "/{menuId}") + public AjaxResult getInfo(@PathVariable Integer menuId) { + return AjaxResult.success(menuService.selectMenuById(menuId)); + } + + /** + * Get treeselect menu list + */ + @GetMapping("/treeselect") + public AjaxResult treeselect(SysMenu menu) { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + Integer userId = loginUser.getUser().getUserId(); + List menus = menuService.selectMenuList(menu, userId); + return AjaxResult.success(menuService.buildMenuTreeSelect(menus)); + } + + /** + * Get treeselect menu list by role + */ + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Integer roleId) { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + List menus = menuService.selectMenuList(loginUser.getUser().getUserId()); + menus = menus.stream().filter(m -> m.getVisible().equals(SysMenuVisible.SHOW.getCode())).collect(Collectors.toList()); + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId)); + List trees = menuService.buildMenuTreeSelect(menus); + SysRole role = roleService.selectRoleById(roleId); + if (!role.isAdmin()) { + trees = trees.stream().filter(t -> !t.getId().equals(6)).collect(Collectors.toList()); + } + ajax.put("menus", trees); + return ajax; + } + + /** + * save menu + */ + @Log(title = "menus", businessType = BusinessType.INSERT) + @PreAuthorize("@ss.hasPermi('system:menu:add')") + @PostMapping + public AjaxResult add(@Validated @RequestBody SysMenu menu) { + if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) { + return AjaxResult.error(ResponseCode.MENU_NAME_IS_EXISTS_ERROR.code()); + } + menu.setCreateBy(SecurityUtils.getUsername()); + return toAjax(menuService.insertMenu(menu)); + } + + /** + * update menu + */ + @Log(title = "menus", businessType = BusinessType.UPDATE) + @PreAuthorize("@ss.hasPermi('system:menu:edit')") + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysMenu menu) { + if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) { + return AjaxResult.error(ResponseCode.MENU_NAME_IS_EXISTS_ERROR.code()); + } else if (menu.getMenuId().equals(menu.getParentId())) { + return AjaxResult.error(ResponseCode.MENU_NOT_ADD_SELF_AS_SUBMENU_ERROR.code()); + } + return toAjax(menuService.updateMenu(menu)); + } + + /** + * delete menu + */ + @PreAuthorize("@ss.hasPermi('system:menu:remove')") + @Log(title = "menus", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public AjaxResult remove(@PathVariable("menuId") Integer menuId) { + if (menuService.hasChildByMenuId(menuId)) { + return AjaxResult.error(ResponseCode.MENU_HAS_SUBMENU_NOT_DELETE.code()); + } + if (menuService.checkMenuExistRole(menuId)) { + return AjaxResult.error(ResponseCode.MENU_ASSIGNED_NOT_DELETE.code()); + } + return toAjax(menuService.deleteMenuById(menuId)); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysOperlogController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysOperlogController.java new file mode 100644 index 000000000..0296db7dc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysOperlogController.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysOperlogController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysOperlogController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.system.domain.SysOperLog; +import org.opengauss.admin.system.service.ISysOperLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * OperLog Controller + * + * @author xielibo + */ +@RestController +@RequestMapping("/system/operlog") +public class SysOperlogController extends BaseController { + @Autowired + private ISysOperLogService operLogService; + + @GetMapping("/list") + public TableDataInfo list(SysOperLog operLog) { + IPage list = operLogService.selectOperLogList(startPage(), operLog); + return getDataTable(list); + } + + @DeleteMapping("/{operIds}") + public AjaxResult remove(@PathVariable String[] operIds) { + return toAjax(operLogService.deleteOperLogByIds(operIds)); + } + + @DeleteMapping("/clean") + public AjaxResult clean() { + operLogService.cleanOperLog(); + return AjaxResult.success(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysProfileController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysProfileController.java new file mode 100644 index 000000000..447ff1625 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysProfileController.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysProfileController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysProfileController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.web.controller; + +import org.opengauss.admin.common.annotation.Log; +import org.opengauss.admin.common.config.SystemConfig; +import org.opengauss.admin.common.constant.UserConstants; +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.entity.SysUser; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.core.dto.ModifyPasswordDto; +import org.opengauss.admin.common.enums.BusinessType; +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.common.utils.RsaUtils; +import org.opengauss.admin.common.utils.SecurityUtils; +import org.opengauss.admin.common.utils.ServletUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.common.utils.file.FileUploadUtils; +import org.opengauss.admin.framework.web.service.TokenService; +import org.opengauss.admin.system.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +/** + * user profile controller + * + * @author xielibo + */ +@RestController +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController { + @Autowired + private ISysUserService userService; + + @Autowired + private TokenService tokenService; + + /** + * Get the currently logged in user information + */ + @GetMapping + public AjaxResult profile() { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + SysUser user = loginUser.getUser(); + AjaxResult ajax = AjaxResult.success(user); + ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername())); + return ajax; + } + + /** + * update info + */ + @Log(title = "user profile", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult updateProfile(@RequestBody SysUser user) { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + if (!loginUser.getUser().getUserId().equals(user.getUserId())) { + return AjaxResult.error(ResponseCode.UNAUTHORIZED.code()); + } + if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) { + return AjaxResult.error(ResponseCode.USER_PHONE_EXISTS_ERROR.code()); + } + if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) { + return AjaxResult.error(ResponseCode.USER_EMAIL_EXISTS_ERROR.code()); + } + if (userService.updateUserProfile(user) > 0) { + loginUser.getUser().setNickName(user.getNickName()); + loginUser.getUser().setPhonenumber(user.getPhonenumber()); + loginUser.getUser().setEmail(user.getEmail()); + loginUser.getUser().setSex(user.getSex()); + tokenService.setLoginUser(loginUser); + return AjaxResult.success(); + } + return AjaxResult.error(); + } + + /** + * reset password + */ + @Log(title = "user profile", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public AjaxResult updatePwd(@RequestBody ModifyPasswordDto modifyPasswordDto) { + String oldPassword = RsaUtils.decrypt(modifyPasswordDto.getOldPassword()); + String newPassword = RsaUtils.decrypt(modifyPasswordDto.getNewPassword()); + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + String userName = loginUser.getUsername(); + String password = loginUser.getPassword(); + if (!SecurityUtils.matchesPassword(oldPassword, password)) { + return AjaxResult.error(ResponseCode.USER_RESET_PASS_ORIGIN_PASS_ERROR.code()); + } + if (SecurityUtils.matchesPassword(newPassword, password)) { + return AjaxResult.error(ResponseCode.USER_PASS_SAME_ERROR.code()); + } + if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0) { + loginUser.getUser().setUpdatePwd("1"); + loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword)); + tokenService.setLoginUser(loginUser); + return AjaxResult.success(); + } + return AjaxResult.error(); + } + + /** + * upload avatar + */ + @Log(title = "user profile", businessType = BusinessType.UPDATE) + @PostMapping("/avatar") + public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws IOException { + if (!file.isEmpty()) { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + String avatar = FileUploadUtils.upload(SystemConfig.getAvatarPath(), file); + if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) { + AjaxResult ajax = AjaxResult.success(); + ajax.put("imgUrl", avatar); + loginUser.getUser().setAvatar(avatar); + tokenService.setLoginUser(loginUser); + return ajax; + } + } + return AjaxResult.error(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysRoleController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysRoleController.java new file mode 100644 index 000000000..528e59339 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysRoleController.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysRoleController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysRoleController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.opengauss.admin.common.constant.UserConstants; +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.entity.SysRole; +import org.opengauss.admin.common.core.domain.entity.SysUser; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.common.utils.SecurityUtils; +import org.opengauss.admin.common.utils.ServletUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.framework.web.service.SysPermissionService; +import org.opengauss.admin.framework.web.service.TokenService; +import org.opengauss.admin.system.service.ISysRoleService; +import org.opengauss.admin.system.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * roles + * + * @author xielibo + */ +@RestController +@RequestMapping("/system/role") +public class SysRoleController extends BaseController { + @Autowired + private ISysRoleService roleService; + + @Autowired + private TokenService tokenService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private ISysUserService userService; + + @GetMapping("/list") + public TableDataInfo list(SysRole role) { + IPage list = roleService.selectRoleList(role,startPage()); + return getDataTable(list); + } + + + /** + * get role by roleI + */ + @GetMapping(value = "/{roleId}") + public AjaxResult getInfo(@PathVariable Integer roleId) { + return AjaxResult.success(roleService.selectRoleById(roleId)); + } + + /** + * save + */ + @PostMapping + public AjaxResult add(@Validated @RequestBody SysRole role) { + if (StringUtils.isBlank(role.getRoleName())) { + return AjaxResult.error(ResponseCode.ROLE_NAME_IS_NOT_EMPTY_ERROR.code()); + } + if (role.getRoleName().length() > 25) { + return AjaxResult.error(ResponseCode.ROLE_NAME_MAX_LENGTH_ERROR.code()); + } + if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) { + return AjaxResult.error(ResponseCode.ROLE_EXISTS_ERROR.code()); + } + if (StringUtils.isNotEmpty(role.getRemark()) && role.getRemark().length() > 200) { + return AjaxResult.error(ResponseCode.ROLE_REMARK_MAX_LENGTH_ERROR.code()); + } + role.setCreateBy(SecurityUtils.getUsername()); + return toAjax(roleService.insertRole(role)); + + } + + /** + * update + */ + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + if (StringUtils.isBlank(role.getRoleName())) { + return AjaxResult.error(ResponseCode.ROLE_NAME_IS_NOT_EMPTY_ERROR.code()); + } + if (role.getRoleName().length() > 25) { + return AjaxResult.error(ResponseCode.ROLE_NAME_MAX_LENGTH_ERROR.code()); + } + if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) { + return AjaxResult.error(ResponseCode.ROLE_EXISTS_ERROR.code()); + } + if (StringUtils.isNotEmpty(role.getRemark()) && role.getRemark().length() > 200) { + return AjaxResult.error(ResponseCode.ROLE_REMARK_MAX_LENGTH_ERROR.code()); + } + if (roleService.updateRole(role) > 0) { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) { + loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser())); + loginUser.setUser(userService.getOne(new QueryWrapper().lambda().eq(SysUser::getUserName, loginUser.getUser().getUserName()))); + tokenService.setLoginUser(loginUser); + } + return AjaxResult.success(); + } + return AjaxResult.error(); + } + + /** + * update data scope + */ + @PutMapping("/dataScope") + public AjaxResult dataScope(@RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + return toAjax(roleService.authDataScope(role)); + } + + /** + * update status + */ + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + return toAjax(roleService.updateRoleStatus(role)); + } + + /** + * delete + */ + @PreAuthorize("@ss.hasPermi('system:role:remove')") + @DeleteMapping("/{roleIds}") + public AjaxResult remove(@PathVariable Integer[] roleIds) { + return toAjax(roleService.deleteRoleByIds(roleIds)); + } + + /** + * optionselect + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping("/optionselect") + public AjaxResult optionselect() { + return AjaxResult.success(roleService.selectRoleAll()); + } +} + diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysSettingController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysSettingController.java new file mode 100644 index 000000000..e84f1dacc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysSettingController.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysSettingController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysSettingController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.web.controller; + +import org.opengauss.admin.common.annotation.Log; +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.entity.SysSettingEntity; +import org.opengauss.admin.common.enums.BusinessType; +import org.opengauss.admin.system.service.ISysSettingService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * System Setting Controller + * + * @author wangyl + */ +@RestController +@RequestMapping("/system/setting") +public class SysSettingController extends BaseController { + @Autowired + private ISysSettingService sysSettingService; + + /** + * update system setting + */ + @Log(title = "setting", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult update(@RequestBody @Validated SysSettingEntity setting) { + setting.setUserId(getUserId()); + boolean res = sysSettingService.updateSetting(setting); + return res ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * list system setting + */ + @GetMapping + public AjaxResult list() { + SysSettingEntity setting = sysSettingService.getSetting(getUserId()); + return AjaxResult.success(setting); + } + + @GetMapping("/checkUploadPath") + public AjaxResult checkSysUploadPath(@RequestParam String path) { + boolean result = sysSettingService.hasUploadPath(path, getUserId()); + // return false is not ok + return AjaxResult.success("ok", !result); + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysTaskController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysTaskController.java new file mode 100644 index 000000000..176267ed8 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysTaskController.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysTaskController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysTaskController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.opengauss.admin.common.annotation.Log; +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.dto.SysTaskDto; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.enums.BusinessType; +import org.opengauss.admin.system.domain.SysTask; +import org.opengauss.admin.system.service.ISysTaskService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * Task Controller + * + * @author xielibo + * @date 2023-01-13 + */ +@RestController +@RequestMapping("/sys/task") +public class SysTaskController extends BaseController { + @Autowired + private ISysTaskService sysTaskService; + + /** + * page list + */ + @PreAuthorize("@ss.hasPermi('system:task:list')") + @GetMapping("/list") + public TableDataInfo list(SysTaskDto task) { + IPage list = sysTaskService.selectList(startPage(), task); + return getDataTable(list); + } + + /** + * list + */ + @PreAuthorize("@ss.hasPermi('system:task:list')") + @GetMapping("/list/all") + public AjaxResult listAll(SysTaskDto task) { + List list = sysTaskService.selectListAll(task); + return AjaxResult.success(list); + } + + /** + * getById + */ + @PreAuthorize("@ss.hasPermi('system:task:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Integer id) { + return AjaxResult.success(sysTaskService.getDetailById(id)); + } + + /** + * delete + */ + @Log(title = "task", businessType = BusinessType.DELETE) + @PreAuthorize("@ss.hasPermi('system:task:remove')") + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + sysTaskService.deleteTask(ids); + return AjaxResult.success(); + } + + /** + * start + */ + @Log(title = "task", businessType = BusinessType.START) + @PreAuthorize("@ss.hasPermi('system:task:update')") + @PostMapping("/start/{id}") + public AjaxResult start(@PathVariable Integer id ) { + sysTaskService.startTask(id); + return AjaxResult.success(); + } + + /** + * stop + */ + @Log(title = "task", businessType = BusinessType.STOP) + @PreAuthorize("@ss.hasPermi('system:task:update')") + @PostMapping("/stop/{id}") + public AjaxResult stop(@PathVariable Integer id ) { + sysTaskService.stopTask(id); + return AjaxResult.success(); + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysUserController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysUserController.java new file mode 100644 index 000000000..f0b1d1e7d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysUserController.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysUserController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysUserController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.web.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; + +import org.opengauss.admin.common.annotation.Log; +import org.opengauss.admin.common.constant.UserConstants; +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.entity.SysRole; +import org.opengauss.admin.common.core.domain.entity.SysUser; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.enums.BusinessType; +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.common.utils.RsaUtils; +import org.opengauss.admin.common.utils.SecurityUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.system.service.ISysRoleService; +import org.opengauss.admin.system.service.ISysUserService; + +import cn.hutool.core.util.StrUtil; + +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * User Controller + * + * @author xielibo + */ +@RestController +@RequestMapping("/system/user") +public class SysUserController extends BaseController { + @Autowired + private ISysUserService userService; + + @Autowired + private ISysRoleService roleService; + + /** + * Query User Page List + * + * @param user user + * @return TableDataInfo + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/list") + public TableDataInfo list(SysUser user) { + IPage result = userService.selectUserList(user, startPage()); + return getDataTable(result); + } + + /** + * Get User Info + * + * @param userId user id + * @return AjaxResult + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping(value = {"/", "/{userId}"}) + public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Integer userId) { + AjaxResult ajax = AjaxResult.success(); + List roles = roleService.selectRoleAll(); + ajax.put("roles", + SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + if (StringUtils.isNotNull(userId)) { + ajax.put(AjaxResult.DATA_TAG, userService.selectUserById(userId)); + ajax.put("roleIds", roleService.selectRoleListByUserId(userId)); + } + return ajax; + } + + /** + * Save User + * + * @param user user + * @return AjaxResult + */ + @Log(title = "users", businessType = BusinessType.INSERT) + @PreAuthorize("@ss.hasPermi('system:user:add')") + @PostMapping + public AjaxResult add(@RequestBody SysUser user) { + if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user.getUserName()))) { + return AjaxResult.error(ResponseCode.USER_ACCOUNT_EXISTS_ERROR.code()); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && UserConstants.NOT_UNIQUE.equals( + userService.checkPhoneUnique(user))) { + return AjaxResult.error(ResponseCode.USER_PHONE_EXISTS_ERROR.code()); + } else if (StringUtils.isNotEmpty(user.getEmail()) && UserConstants.NOT_UNIQUE.equals( + userService.checkEmailUnique(user))) { + return AjaxResult.error(ResponseCode.USER_EMAIL_EXISTS_ERROR.code()); + } + if (user.getPhonenumber().length() > 11) { + return AjaxResult.error(ResponseCode.USER_TELEPHONE_MAX_LENGTH_ERROR.code()); + } + if (user.getUserName().length() > 30) { + return AjaxResult.error(ResponseCode.USER_NAME_MAX_LENGTH_ERROR.code()); + } + if (user.getNickName().length() > 30) { + return AjaxResult.error(ResponseCode.USER_NICKNAME_MAX_LENGTH_ERROR.code()); + } + if (StrUtil.isEmpty(user.getPassword())) { + return AjaxResult.error(ResponseCode.USER_PASSWORD_CANNOT_BE_EMPTY_ERROR.code()); + } + if (StringUtils.isNotEmpty(user.getRemark()) && user.getRemark().length() > 200) { + return AjaxResult.error(ResponseCode.USER_REMARK_MAX_LENGTH_ERROR.code()); + } + user.setCreateBy(getUsername()); + user.setPassword(SecurityUtils.encryptPassword(RsaUtils.decrypt(user.getPassword()))); + return toAjax(userService.insertUser(user)); + } + + /** + * Update User + * + * @param user user + * @return AjaxResult + */ + @Log(title = "users", businessType = BusinessType.UPDATE) + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysUser user) { + user.setPassword(null); + userService.checkUserAllowed(user); + if (StringUtils.isNotEmpty(user.getPhonenumber()) && UserConstants.NOT_UNIQUE.equals( + userService.checkPhoneUnique(user))) { + return AjaxResult.error(ResponseCode.USER_PHONE_EXISTS_ERROR.code()); + } else if (StringUtils.isNotEmpty(user.getEmail()) && UserConstants.NOT_UNIQUE.equals( + userService.checkEmailUnique(user))) { + return AjaxResult.error(ResponseCode.USER_EMAIL_EXISTS_ERROR.code()); + } + if (user.getPhonenumber().length() > 11) { + return AjaxResult.error(ResponseCode.USER_TELEPHONE_MAX_LENGTH_ERROR.code()); + } + if (user.getUserName().length() > 30) { + return AjaxResult.error(ResponseCode.USER_NAME_MAX_LENGTH_ERROR.code()); + } + if (user.getNickName().length() > 30) { + return AjaxResult.error(ResponseCode.USER_NICKNAME_MAX_LENGTH_ERROR.code()); + } + if (StringUtils.isNotEmpty(user.getRemark()) && user.getRemark().length() > 200) { + return AjaxResult.error(ResponseCode.USER_REMARK_MAX_LENGTH_ERROR.code()); + } + user.setUpdateBy(getUsername()); + return toAjax(userService.updateUser(user)); + } + + /** + * Delete User + * + * @param userIds user ids + * @return AjaxResult + */ + @Log(title = "users", businessType = BusinessType.DELETE) + @PreAuthorize("@ss.hasPermi('system:user:remove')") + @DeleteMapping("/{userIds}") + public AjaxResult remove(@PathVariable Integer[] userIds) { + if (ArrayUtils.contains(userIds, getUserId())) { + return error(); + } + return toAjax(userService.deleteUserByIds(userIds)); + } + + /** + * Reset Password + * + * @param user user + * @return AjaxResult + */ + @Log(title = "users", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public AjaxResult resetPwd(@RequestBody SysUser user) { + userService.checkUserAllowed(user); + user.setPassword(SecurityUtils.encryptPassword(RsaUtils.decrypt(user.getPassword()))); + user.setUpdateBy(getUsername()); + return toAjax(userService.resetPwd(user)); + } + + /** + * Update Status + * + * @param user user + * @return AjaxResult + */ + @Log(title = "users", businessType = BusinessType.UPDATE) + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysUser user) { + userService.checkUserAllowed(user); + user.setUpdateBy(getUsername()); + return toAjax(userService.updateUserStatus(user)); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysWhiteListController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysWhiteListController.java new file mode 100644 index 000000000..b94d40ada --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysWhiteListController.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysWhiteListController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SysWhiteListController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.github.pagehelper.util.StringUtil; +import org.opengauss.admin.common.annotation.Log; +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.enums.BusinessType; +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.system.domain.SysWhiteList; +import org.opengauss.admin.system.service.ISysWhiteListService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +/** + * whiteList Controller + * + * @author xielibo + * @date 2022-08-10 + */ +@RestController +@RequestMapping("/sys/whiteList") +public class SysWhiteListController extends BaseController { + @Autowired + private ISysWhiteListService iSysWhiteListService; + + /** + * page list + */ + @PreAuthorize("@ss.hasPermi('system:whiteList:list')") + @GetMapping("/list") + public TableDataInfo list(SysWhiteList whiteList) { + IPage list = iSysWhiteListService.selectList(startPage(), whiteList); + return getDataTable(list); + } + + /** + * list + */ + @PreAuthorize("@ss.hasPermi('system:whiteList:list')") + @GetMapping("/list/all") + public AjaxResult listAll(SysWhiteList whiteList) { + List list = iSysWhiteListService.selectListAll(whiteList); + return AjaxResult.success(list); + } + + /** + * getById + */ + @PreAuthorize("@ss.hasPermi('system:whiteList:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Integer id) { + return AjaxResult.success(iSysWhiteListService.getById(id)); + } + + /** + * save + */ + @Log(title = "whitelist", businessType = BusinessType.INSERT) + @PreAuthorize("@ss.hasPermi('system:whiteList:add')") + @PostMapping + public AjaxResult add(@RequestBody SysWhiteList whiteList) { + whiteList.setCreateTime(new Date()); + return processWhiteList(whiteList, true); + } + + /** + * update + */ + @Log(title = "whitelist", businessType = BusinessType.UPDATE) + @PreAuthorize("@ss.hasPermi('system:whiteList:edit')") + @PutMapping + public AjaxResult edit(@RequestBody SysWhiteList whiteList) { + return processWhiteList(whiteList, false); + } + + private AjaxResult processWhiteList(SysWhiteList whiteList, boolean isAdd) { + if (whiteList.getTitle().length() > 100) { + return AjaxResult.error(ResponseCode.WHITELIST_TITLE_MAX_LENGTH_ERROR.code()); + } + if (iSysWhiteListService.checkTitleExists(whiteList)) { + return AjaxResult.error(ResponseCode.WHITELIST_TITLE_EXISTS_ERROR.code()); + } + if (StringUtil.isNotEmpty(whiteList.getIpList()) && whiteList.getIpList().length() > 200) { + return AjaxResult.error(ResponseCode.WHITELIST_IPS_MAX_LENGTH_ERROR.code()); + } + if (whiteList.hasDuplicateIp()) { + return AjaxResult.error(ResponseCode.WHITELIST_HAS_DUPLICATE_IP_ERROR.code()); + } + List existsIps = iSysWhiteListService.checkIpsExistsInWhiteList(whiteList); + if (!existsIps.isEmpty()) { + return AjaxResult.error(ResponseCode.WHITELIST_IPS_EXISTS_ERROR.code(), + ResponseCode.WHITELIST_IPS_EXISTS_ERROR.msg() + ". Ip addresses: " + String.join(",", existsIps)); + } + + return isAdd ? toAjax(iSysWhiteListService.save(whiteList)) + : toAjax(iSysWhiteListService.updateById(whiteList)); + } + + /** + * delete + */ + @Log(title = "whitelist", businessType = BusinessType.DELETE) + @PreAuthorize("@ss.hasPermi('system:whiteList:remove')") + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Integer[] ids) { + return toAjax(iSysWhiteListService.removeBatchByIds(Arrays.asList(ids))); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SystemPluginController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SystemPluginController.java new file mode 100644 index 000000000..6629234ac --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SystemPluginController.java @@ -0,0 +1,552 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SystemPluginController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/SystemPluginController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.web.controller; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.map.MapUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.gitee.starblues.core.DefaultPluginManager; +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.core.PluginState; +import com.gitee.starblues.core.RealizeProvider; +import com.gitee.starblues.core.exception.PluginException; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.integration.operator.PluginOperator; +import com.gitee.starblues.integration.operator.upload.UploadParam; +import com.gitee.starblues.spring.extract.ExtractCoordinate; +import com.gitee.starblues.spring.extract.ExtractFactory; +import com.google.common.collect.Maps; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.annotation.Log; +import org.opengauss.admin.common.config.SystemConfig; +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.dto.PluginConfigDataDto; +import org.opengauss.admin.common.core.dto.PluginDownloadDTO; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.enums.BusinessType; +import org.opengauss.admin.common.enums.PluginLicenseType; +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.common.enums.SysPluginStatus; +import org.opengauss.admin.common.enums.SysPluginDownloadStatus; +import org.opengauss.admin.common.enums.SysPluginTheme; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.common.utils.file.FileUploadUtils; +import org.opengauss.admin.system.domain.SysPlugin; +import org.opengauss.admin.system.domain.SysPluginLogo; +import org.opengauss.admin.system.plugin.beans.PluginExtensionInfoDto; +import org.opengauss.admin.system.plugin.extract.PluginExtensionInfoExtract; +import org.opengauss.admin.system.service.ISysPluginConfigDataService; +import org.opengauss.admin.system.service.ISysPluginConfigService; +import org.opengauss.admin.system.service.ISysPluginRepositoryService; +import org.opengauss.admin.system.service.ISysPluginService; +import org.opengauss.admin.system.service.ISysMenuService; +import org.opengauss.admin.system.service.ISysPluginLogoService; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.multipart.MultipartFile; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Map; + +/** + * user profile controller + * + * @author: wangchao + * @Date: 2025/9/11 21:21 + * @since 7.0.0-RC2 + **/ +@RestController +@RequestMapping("/system/plugins") +@Slf4j +public class SystemPluginController extends BaseController { + @Autowired + private ISysPluginService sysPluginService; + @Autowired + private ISysPluginRepositoryService sysPluginRepositoryService; + @Autowired + private PluginOperator pluginOperator; + @Autowired + private ISysPluginConfigService sysPluginConfigService; + @Autowired + private ISysPluginConfigDataService sysPluginConfigDataService; + @Autowired + private ISysMenuService sysMenuService; + @Autowired + private ISysPluginLogoService iSysPluginLogoService; + @Autowired + private ExtractFactory extractFactory; + @Autowired + private RealizeProvider realizeProvider; + @Autowired + private IntegrationConfiguration configuration; + + private final String baseOpsId = "base-ops"; + + + /** + * get plugin list + */ + @GetMapping("/list") + public TableDataInfo list(SysPlugin sysPlugin) { + IPage list = sysPluginService.selectList(startPage(), sysPlugin); + list.getRecords().forEach(d -> { + PluginInfo pluginInfo = pluginOperator.getPluginInfo(d.getPluginId()); + if (pluginInfo != null && pluginInfo.getPluginState().toString().equals(PluginState.STARTED.toString())) { + d.setPluginStatus(SysPluginStatus.START.getCode()); + } else { + d.setPluginStatus(SysPluginStatus.DISABLE.getCode()); + } + SysPluginLogo plugin = iSysPluginLogoService.getByPluginId(d.getPluginId()); + if (plugin != null) { + d.setLogoPath(plugin.getLogoPath()); + } + }); + return getDataTable(list); + } + + /** + * get plugin extension list + */ + @GetMapping("/extensions/list") + public TableDataInfo listExtensionInfo(SysPlugin sysPlugin) { + IPage page = sysPluginService.selectList(startPage(), sysPlugin); + List list = new ArrayList<>(); + page.getRecords().forEach(d -> { + PluginExtensionInfoDto dto = new PluginExtensionInfoDto(); + dto.setPluginId(d.getPluginId()); + dto.setPluginLicenseType(PluginLicenseType.FREE); + dto.setPluginName(d.getPluginId()); + try { + ExtractCoordinate coordinate = ExtractCoordinate.build(d.getPluginId()); + PluginExtensionInfoExtract noticeExtract = extractFactory.getExtractByCoordinate(coordinate); + PluginExtensionInfoDto extensionInfo = noticeExtract.getPluginExtensionInfo(); + BeanUtils.copyProperties(extensionInfo, dto); + } catch (RuntimeException exception) { + log.warn("Query plugin:" + d.getPluginId() + " extension info error"); + } + list.add(dto); + }); + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(ResponseCode.SUCCESS.code()); + rspData.setMsg(ResponseCode.SUCCESS.msg()); + rspData.setRows(list); + rspData.setTotal(page.getTotal()); + return rspData; + } + + /** + * get plugin count + */ + @GetMapping("/count") + public AjaxResult installCount() { + return AjaxResult.success(sysPluginService.count()); + } + + @GetMapping("/listContent") + public AjaxResult listContent() { + return AjaxResult.success(pluginOperator.getPluginInfo()); + } + + /** + * start plugin + * + * @param id plugin id + */ + @Log(title = "plugins", businessType = BusinessType.START) + @PostMapping("/start/{id}") + public AjaxResult start(@PathVariable("id") String id) { + try { + if (pluginOperator.start(id)) { + sysPluginService.startByPluginId(id); + return AjaxResult.success(); + } else { + return AjaxResult.error(ResponseCode.INTEGRATION_PLUGIN_START_ERROR.code()); + } + } catch (Exception e) { + log.error("start plugins [{}] failed ", id, e); + return AjaxResult.error(ResponseCode.INTEGRATION_PLUGIN_START_ERROR.code(), e.getMessage()); + } + } + + /** + * stop plugin by id + * + * @param id plugin id + */ + @Log(title = "plugins", businessType = BusinessType.STOP) + @PostMapping("/stop/{id}") + public AjaxResult stop(@PathVariable("id") String id) { + try { + Integer count = sysMenuService.countMenuHasOtherEnablePluginSubmenuByPluginId(id); + if (count > 0) { + return AjaxResult.error(ResponseCode.PLUGIN_MENU_HAS_OTHER_PLUGIN_SUBMENU_UNINSTALL_ERROR.code()); + } + if (pluginOperator.stop(id)) { + sysPluginService.stopByPluginId(id); + return AjaxResult.success(); + } else { + return AjaxResult.error(ResponseCode.INTEGRATION_PLUGIN_STOP_ERROR.code()); + } + } catch (Exception e) { + log.error("stop plugins [{}] failed ", id, e); + return AjaxResult.error(ResponseCode.INTEGRATION_PLUGIN_STOP_ERROR.code(), e.getMessage()); + } + } + + /** + * upload and instal plugin offline + * + * @param jarFile jarFile + */ + @Log(title = "plugins", businessType = BusinessType.INSTALL) + @PostMapping("/offline_install") + public AjaxResult install(@RequestParam("file") MultipartFile jarFile) { + String pluginId = ""; + try { + preInstall(jarFile); + UploadParam uploadParam = UploadParam.byMultipartFile(jarFile) + .setBackOldPlugin(true) + .setStartPlugin(true) + .setUnpackPlugin(false); + PluginInfo pluginInfo = pluginOperator.uploadPlugin(uploadParam); + pluginId = pluginInfo.getPluginId(); + if (!pluginInfo.getPluginDescriptor().getPluginVersion() + .equals(sysPluginRepositoryService.getCurrentVersion())) { + pluginOperator.uninstall(pluginInfo.getPluginId(), true, true); + return AjaxResult.error(ResponseCode.INTEGRATION_PLUGIN_VERSION_ERROR.msg() + + sysPluginRepositoryService.getCurrentVersion()); + } + Map result; + if ((result = updateSystemByPluginInfo(pluginInfo)) != null) { + sysPluginRepositoryService.updatePluginDownloadStatus( + List.of(pluginInfo.getPluginId()), SysPluginDownloadStatus.DOWNLOADED_STATUS.getCode()); + return AjaxResult.success(result); + } else { + return AjaxResult.error(ResponseCode.INTEGRATION_PLUGIN_INSTALL_ERROR.code()); + } + } catch (Exception e) { + log.error("offline_install plugins [{}] failed ", pluginId, e); + return AjaxResult.error(ResponseCode.INTEGRATION_PLUGIN_INSTALL_ERROR.msg() + ":" + e.getMessage()); + } + } + + /** + * getFileNameFromUrl + * + * @param url pluginDownloadUrl + * @return jarFile name + */ + private String getFileNameFromUrl(String url) { + try { + URL u = new URL(url); + String path = u.getPath(); + return path.substring(path.lastIndexOf("/") + 1); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Invalid URL format", e); + } + } + + /** + * upload and instal plugin online + * + * @param dto dto + */ + @Log(title = "plugins", businessType = BusinessType.INSTALL) + @PostMapping("/online_install") + public AjaxResult onlineInstall(@RequestBody PluginDownloadDTO dto) { + String pluginUrl = dto.getPluginUrl(); + String wsBusinessId = dto.getWsBusinessId(); + String fileName = getFileNameFromUrl(pluginUrl); + log.info("wsBusinessId is {} in onlineInstall", wsBusinessId); + sysPluginRepositoryService.saveOnlinePackage(pluginUrl, fileName, wsBusinessId); + String pluginPathString = SystemConfig.getPluginPath() + File.separator + getFileNameFromUrl(pluginUrl); + Path pluginPath = Paths.get(pluginPathString); + PluginInfo pluginInfo = pluginOperator.install(pluginPath, false); + try { + Map result; + if ((result = updateSystemByPluginInfo(pluginInfo)) != null) { + sysPluginRepositoryService.updatePluginDownloadStatus( + List.of(pluginInfo.getPluginId()), SysPluginDownloadStatus.DOWNLOADED_STATUS.getCode()); + return AjaxResult.success(result); + } else { + return AjaxResult.error(ResponseCode.INTEGRATION_PLUGIN_INSTALL_ERROR.code()); + } + } catch (Exception e) { + log.error("online_install plugins [{}] failed ", fileName, e); + return AjaxResult.error(ResponseCode.INTEGRATION_PLUGIN_INSTALL_ERROR.msg() + ":" + e.getMessage()); + } + } + + /** + * Update plugin service info. + * @param pluginInfo the plugin info. + * @return addition map attr. + */ + public Map updateSystemByPluginInfo(PluginInfo pluginInfo) { + if (pluginInfo == null) { + return null; + } + String logo = null; + Integer pluginType = null; + Integer isNeedConfigured = null; + String theme = null; + String configAttrs = null; + String descriptionEn = null; + if (pluginInfo.getExtensionInfo() != null) { + logo = MapUtil.getStr(pluginInfo.getExtensionInfo(), "logo"); + descriptionEn = MapUtil.getStr(pluginInfo.getExtensionInfo(), "descriptionEn"); + pluginType = MapUtil.getInt(pluginInfo.getExtensionInfo(), "pluginType"); + isNeedConfigured = MapUtil.getInt(pluginInfo.getExtensionInfo(), "isNeedConfigured"); + theme = MapUtil.getStr(pluginInfo.getExtensionInfo(), "theme"); + configAttrs = MapUtil.getStr(pluginInfo.getExtensionInfo(), "configAttrs"); + + if (!SysPluginTheme.DARK.getCode().equals(theme) && !SysPluginTheme.LIGHT.getCode().equals(theme)) { + theme = null; + } + if (StringUtils.isNotBlank(logo)) { + logo = Base64.decodeStr(logo); + logo = FileUploadUtils.writeMenuSvgIcon(logo); + } + } + SysPlugin plugin = sysPluginService.getByPluginId(pluginInfo.getPluginId()); + if (plugin == null) { + plugin = SysPlugin.builder().pluginId(pluginInfo.getPluginId()) + .pluginDesc(pluginInfo.getPluginDescriptor().getDescription()).pluginDescEn(descriptionEn) + .bootstrapClass(pluginInfo.getPluginDescriptor().getPluginBootstrapClass()) + .pluginProvider(pluginInfo.getPluginDescriptor().getProvider()).isNeedConfigured(isNeedConfigured) + .pluginType(pluginType).pluginVersion(pluginInfo.getPluginDescriptor().getPluginVersion()) + .theme(theme).build(); + setPluginStatus(plugin, pluginInfo); + sysPluginService.save(plugin); + } else { + // update plugin version,desc,provider + plugin.setPluginVersion(pluginInfo.getPluginDescriptor().getPluginVersion()); + plugin.setPluginDesc(pluginInfo.getPluginDescriptor().getDescription()); + plugin.setPluginDescEn(descriptionEn); + plugin.setPluginProvider(pluginInfo.getPluginDescriptor().getProvider()); + setPluginStatus(plugin, pluginInfo); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysPlugin::getPluginId, pluginInfo.getPluginId()); + sysPluginService.update(plugin, queryWrapper); + } + Map result = Maps.newHashMap(); + result.put("isNeedConfigured", isNeedConfigured); + result.put("pluginId", pluginInfo.getPluginId()); + if (isNeedConfigured != null && isNeedConfigured == 1) { + sysPluginConfigService.savePluginConfig(pluginInfo.getPluginId(), configAttrs); + result.put("configAttrs", configAttrs); + String alreadyData = sysPluginConfigDataService.getDataByPluginId(pluginInfo.getPluginId()); + if (StringUtils.isNotBlank(alreadyData)) { + result.put("configData", alreadyData); + } + } + if (StringUtils.isNotBlank(theme)) { + sysMenuService.updatePluginMenuTheme(pluginInfo.getPluginId(), theme); + } + if (StringUtils.isNotBlank(logo)) { + sysMenuService.updatePluginMenuIcon(pluginInfo.getPluginId(), logo); + iSysPluginLogoService.savePluginConfig(pluginInfo.getPluginId(), logo); + // refresh father logo + sysMenuService.updatePluginFatherMenuIcon(pluginInfo.getPluginId(), logo); + } + return result; + } + + private void setPluginStatus(SysPlugin plugin, PluginInfo pluginInfo) { + if (PluginState.STARTED.equals(pluginInfo.getPluginState())) { + plugin.setPluginStatus(SysPluginStatus.START.getCode()); + } else { + plugin.setPluginStatus(SysPluginStatus.DISABLE.getCode()); + } + } + + /** + * uninstall plugin If there are submenus added by other plugins under your own menu, you cannot uninstall them + * + * @param id plugin id + */ + @Log(title = "plugins", businessType = BusinessType.UNINSTALL) + @PostMapping("/uninstall/{id}") + public AjaxResult uninstall(@PathVariable("id") String id) { + try { + Integer count = sysMenuService.countMenuHasOtherPluginSubmenuByPluginId(id); + if (count > 0) { + return AjaxResult.error(ResponseCode.PLUGIN_MENU_HAS_OTHER_PLUGIN_SUBMENU_UNINSTALL_ERROR.code()); + } + pluginOperator.uninstall(id, true, true); + sysPluginRepositoryService.updatePluginDownloadStatus(List.of(id), + SysPluginDownloadStatus.UN_DOWNLOAD_STATUS.getCode()); + return AjaxResult.success(); + } catch (Exception e) { + log.error("uninstall plugin failed {} ", id, e); + return AjaxResult.error(ResponseCode.INTEGRATION_PLUGIN_UNINSTALL_ERROR.code(), e.getMessage()); + } catch (Error e) { + log.error("uninstall plugin error {} ", id, e); + return AjaxResult.error(ResponseCode.INTEGRATION_PLUGIN_UNINSTALL_ERROR.code(), e.getMessage()); + } + } + + /** + * get plugin info + * + * @param id plugin id + */ + @PostMapping("/get/{id}") + public AjaxResult get(@PathVariable("id") String id) { + try { + return AjaxResult.success(pluginOperator.getPluginInfo(id)); + } catch (Exception e) { + log.error(e.getMessage()); + return AjaxResult.error(); + } + } + + + /** + * save plugin config content + */ + @PostMapping("/pluginConfigData") + public AjaxResult savePluginConfigData(@RequestBody PluginConfigDataDto configDataDto) { + sysPluginConfigDataService.savePluginConfigData(configDataDto.getPluginId(), configDataDto.getConfigData()); + return AjaxResult.success(); + } + + private void preInstall(MultipartFile jarFile) { + Path duplicateFilePath = getPluginPathWithDuplicateFileName(getPluginPaths(), jarFile); + if (duplicateFilePath == null) { + return; + } + if (isPluginLoaded(duplicateFilePath)) { + return; + } + if (!removePluginInstallationPackage(duplicateFilePath)) { + throw new PluginException( + "A file with the same name exists in the system plugin directory and failed to be deleted."); + } + } + + private List getPluginPaths() { + return realizeProvider.getPluginScanner().scan( + new DefaultPluginManager(realizeProvider, configuration).getPluginsRoots()); + } + + private Path getPluginPathWithDuplicateFileName(List pluginPaths, MultipartFile jarFile) { + String jarFileName = jarFile.getOriginalFilename(); + Path result = null; + + for (Path path : pluginPaths) { + String fileName = path.getFileName().toString(); + if (fileName.equals(jarFileName)) { + result = path; + } + } + + return result; + } + + private boolean isPluginLoaded(Path pluginPath) { + return pluginOperator.getPluginInfo(pluginOperator.parse(pluginPath).getPluginId()) != null; + } + + /** + * get unload plugins info list + */ + @GetMapping("/isBaseOpsStart") + public AjaxResult getBaseOpsStatus() { + if (!sysPluginService.getPluginList().contains(baseOpsId)) { + return AjaxResult.success(false); + } + if (pluginOperator.getPluginInfo(baseOpsId).getPluginState().equals(PluginState.STOPPED)) { + return AjaxResult.success(false); + } + return AjaxResult.success(true); + } + + private boolean removePluginInstallationPackage(Path pluginPath) { + try { + Files.delete(pluginPath); + log.info("Plugin installation package deleted successfully: {}", pluginPath); + } catch (IOException e) { + log.error("Failed to delete plugin installation package: {}", pluginPath, e); + return false; + } + return true; + } + + /** + * get unload plugins info list + */ + @GetMapping("/unloadPluginsInfo") + public AjaxResult getUnloadPluginInfosList() { + try { + // 获取未下载插件的plugin_id列表 + List unloadedPluginIds = sysPluginRepositoryService.getUnloadPluginIdsList(); + + return AjaxResult.success(unloadedPluginIds); + } catch (Exception e) { + StringWriter errorsWriter = new StringWriter(); + e.printStackTrace(new PrintWriter(errorsWriter)); + log.error(errorsWriter.toString()); + return AjaxResult.error(ResponseCode.INTEGRATION_UNLOAD_PLUGIN_INFO_QUERY_ERROR.msg() + + ":" + e.getMessage()); + } + } + + /** + * get plugin info + * + * @param pluginId pluginVersion + */ + @PostMapping("/getUnloadPluginUrl") + public AjaxResult getUnloadPluginUrl(@RequestParam("pluginId") String pluginId) { + String pluginVersion = sysPluginRepositoryService.getCurrentVersion(); + String unloadPluginUrl = sysPluginRepositoryService + .getPluginUrlByIdAndVersion(pluginId, pluginVersion); + if (unloadPluginUrl == null) { + return AjaxResult.error(ResponseCode.INTEGRATION_PLUGIN_URL_QUERY_ERROR.msg()); + } + return AjaxResult.success(unloadPluginUrl); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/agent/AgentInstallController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/agent/AgentInstallController.java new file mode 100644 index 000000000..68560441b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/agent/AgentInstallController.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.web.controller.agent; + +import org.opengauss.admin.common.constant.AgentConstants; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.entity.agent.AgentInstallEntity; +import org.opengauss.admin.common.core.domain.model.agent.AgentStartResult; +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskParam; +import org.opengauss.agent.event.AgentStatusEvent; +import org.opengauss.agent.service.AgentTaskManager; +import org.opengauss.agent.service.IAgentInstallService; +import org.opengauss.agent.utils.AgentTaskUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; + +/** + * AgentInstallController + * + * @author: wangchao + * @Date: 2025/4/10 09:27 + * @Description: AgentInstallController + * @since 7.0.0-RC2 + **/ +@RestController +@RequestMapping("/agent") +public class AgentInstallController { + @Resource + private IAgentInstallService agentInstallService; + @Resource + private AgentTaskManager agentTaskManager; + + /** + * Install agent + *
+     *     After installation, create a Host task, but do not start it directly.
+     *     Wait until the Agent goes online, and then trigger the task to start through the online notification event.
+     *     {@link org.opengauss.agent.impl.AgentServerServiceImpl#handleStatusChange(AgentStatusEvent)}
+     * 
+ * + * @param install install information + * @return result + */ + @PostMapping("/install") + public AjaxResult install(@RequestBody AgentInstallEntity install) { + agentInstallService.installAgent(install); + AgentTaskParam hostTask = AgentTaskParam.builder() + .agentId(install.getAgentId()) + .taskTemplateId(AgentConstants.TaskTemplate.HOST_DYNAMIC_METRICS) + .taskName(AgentTaskUtils.generateTaskName(AgentConstants.TaskTemplate.HOST_DYNAMIC_METRICS)) + .build(); + agentTaskManager.createTask(hostTask); + return AjaxResult.success(); + } + + /** + * Uninstall agent + * + * @param agentId agent id + * @return result + */ + @PostMapping("/uninstall") + public AjaxResult unInstall(@RequestParam("agentId") String agentId) { + agentInstallService.uninstallAgent(agentId); + agentTaskManager.cleanAgentTaskInstance(agentId); + return AjaxResult.success(); + } + + /** + * List agent + * + * @return agent list + */ + @GetMapping("/list") + public AjaxResult list() { + return AjaxResult.success(agentInstallService.list()); + } + + /** + * Start agent + * + * @param agentId agent id + * @return result + */ + @PostMapping("/start") + public AjaxResult start(@RequestParam("agentId") String agentId) { + AgentStartResult agentStartStatus = agentInstallService.startAgent(agentId); + if (agentStartStatus.isSuccess()) { + return AjaxResult.success(agentStartStatus.message()); + } else { + return AjaxResult.error(agentStartStatus.message()); + } + } + + /** + * Stop agent + * + * @param agentId agent id + * @return result + */ + @PostMapping("/stop") + public AjaxResult stop(@RequestParam("agentId") String agentId) { + agentInstallService.stopAgent(agentId); + return AjaxResult.success(); + } + + /** + * Upgrade agent + * + * @param agentId agent id + * @return result + */ + @PostMapping("/upgrade") + public AjaxResult upgrade(@RequestParam("agentId") String agentId) { + agentInstallService.updateAgent(agentId); + return AjaxResult.success(); + } + + /** + * Update agent port + * + * @param agentId agent id + * @param agentPort agent port + * @return result + */ + @PostMapping("/updateAgentPort") + public AjaxResult updateAgentPort(@RequestParam("agentId") String agentId, + @RequestParam("agentPort") String agentPort) { + agentInstallService.updateAgentPort(agentId, Integer.valueOf(agentPort)); + return AjaxResult.success(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/agent/AgentServerController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/agent/AgentServerController.java new file mode 100644 index 000000000..6f707765b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/agent/AgentServerController.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.web.controller.agent; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.model.agent.HeartbeatReport; +import org.opengauss.agent.service.IAgentHeartbeatService; +import org.opengauss.agent.service.IAgentServerService; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; + +/** + * AgentServerController + * + * @author: wangchao + * @Date: 2025/4/10 09:27 + * @Description: AgentInstallController + * @since 7.0.0-RC2 + **/ +@RestController +@RequestMapping("/agent") +public class AgentServerController { + @Resource + private IAgentHeartbeatService agentHeartbeatService; + @Resource + private IAgentServerService agentServerService; + + /** + * receive heartbeat + * + * @param heart heart + * @return result + */ + @PostMapping("/heartbeat") + public AjaxResult heartbeat(@RequestBody HeartbeatReport heart) { + agentHeartbeatService.receiveHeartbeat(heart); + return AjaxResult.success(); + } + + /** + * deregister + * + * @param requestDown requestDown + * @return result + */ + @PostMapping("/deregister") + public AjaxResult deregister(@RequestBody HeartbeatReport requestDown) { + agentHeartbeatService.deregister(requestDown); + return AjaxResult.success(); + } + + /** + * anomaly checking + * + * @param agentId agentId + * @return result + */ + @PostMapping("/server/anomaly/checking") + public AjaxResult anomalyChecking(@RequestParam("agentId") String agentId) { + return AjaxResult.success(agentServerService.anomalyChecking(agentId)); + } + + /** + * start task callback + * + * @param agentId agentId + * @return result + */ + @PostMapping("/task/callback/start") + public AjaxResult taskCallbackStarted(@RequestParam("agentId") String agentId) { + agentServerService.taskCallbackStarted(agentId); + return AjaxResult.success(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/agent/AgentServerReceiveController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/agent/AgentServerReceiveController.java new file mode 100644 index 000000000..404a35dfb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/agent/AgentServerReceiveController.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.web.controller.agent; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.model.agent.HostBaseInfo; +import org.opengauss.agent.data.CustomMetricData; +import org.opengauss.agent.event.producer.BackpressureStatus; +import org.opengauss.agent.event.producer.BackpressureStrategy; +import org.opengauss.agent.service.IAgentServerReceiveService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; + +import jakarta.annotation.Resource; + +/** + * AgentServerReceiveController + * + * @author: wangchao + * @Date: 2025/4/10 09:27 + * @Description: AgentInstallController + * @since 7.0.0-RC2 + **/ +@RestController +@RequestMapping("/receive") +public class AgentServerReceiveController { + @Resource + private IAgentServerReceiveService agentServerReceiveService; + @Resource + private BackpressureStrategy strategy; + + /** + * Get the custom metric data of the host + * + * @param hostBaseInfo hostBaseInfo + * @return result + */ + @PostMapping("/fixed/host/info") + public AjaxResult fixedHostBaseInfo(HostBaseInfo hostBaseInfo) { + agentServerReceiveService.refreshBaseHostFixedInfo(hostBaseInfo); + return AjaxResult.success(); + } + + /** + * get custom metric data of the host + * + * @param taskIds taskIds + * @param agentId agentId + * @param metricDataList metricDataList + * @return result + */ + @PostMapping("/metrics") + public AjaxResult metrics(@RequestParam(value = "taskIds") List taskIds, + @RequestParam(value = "agentId") Long agentId, @RequestBody List metricDataList) { + agentServerReceiveService.receiveMetricData(taskIds, agentId, metricDataList); + return AjaxResult.success(); + } + + /** + * get custom metric data of the host + * + * @param taskId taskId + * @param agentId agentId + * @param dataList dataList + * @return result + */ + @PostMapping("/pipeline") + public AjaxResult pipeline(@RequestParam(value = "taskId") Long taskId, + @RequestParam(value = "agentId") Long agentId, @RequestBody List> dataList) { + agentServerReceiveService.receivePipelineData(taskId, agentId, dataList); + return AjaxResult.success(); + } + + /** + * Adjust the threshold + * + * @param percent percent + */ + @PostMapping("/backpressure/threshold") + public void adjustThreshold(@RequestParam int percent) { + strategy.setThresholdPercent(percent); + } + + /** + * Get the current status of the backpressure strategy + * + * @return status + */ + @GetMapping("/backpressure/status") + public BackpressureStatus getStatus() { + return strategy.getBackpressureStatus(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/agent/AgentTaskDefinitionController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/agent/AgentTaskDefinitionController.java new file mode 100644 index 000000000..4e55eb332 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/agent/AgentTaskDefinitionController.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.web.controller.agent; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.entity.agent.AgentInstallEntity; +import org.opengauss.admin.common.core.domain.entity.agent.TaskMetricsDefinition; +import org.opengauss.admin.common.core.domain.entity.agent.TaskSchemaDefinition; +import org.opengauss.admin.common.core.domain.entity.agent.TaskTemplateDefinition; +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskParam; +import org.opengauss.admin.common.utils.ChainAssert; +import org.opengauss.agent.repository.TaskMetricsDefinitionService; +import org.opengauss.agent.repository.TaskSchemaDefinitionService; +import org.opengauss.agent.repository.TaskTemplateDefinitionService; +import org.opengauss.agent.service.AgentHttpProxy; +import org.opengauss.agent.service.AgentTaskManager; +import org.opengauss.agent.service.IAgentInstallService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import jakarta.annotation.Resource; + +/** + * AgentTaskDefinitionController + * + * @author: wangchao + * @Date: 2025/4/10 09:27 + * @Description: AgentInstallController + * @since 7.0.0-RC2 + **/ +@RestController +@RequestMapping("/agent") +public class AgentTaskDefinitionController { + @Resource + private TaskTemplateDefinitionService taskTemplateDefinitionService; + @Resource + private TaskSchemaDefinitionService taskSchemaDefinitionService; + @Resource + private TaskMetricsDefinitionService taskMetricsDefinitionService; + @Resource + private AgentTaskManager agentTaskManager; + @Resource + private IAgentInstallService agentInstallService; + @Resource + private AgentHttpProxy agentHttpProxy; + + /** + * save task template definition + * + * @param taskTemplateDefinitions taskTemplateDefinitions + * @return result + */ + @PostMapping("/taskTemplateDefinition/save") + public AjaxResult taskTemplateDefinition(@RequestBody List taskTemplateDefinitions) { + taskTemplateDefinitionService.saveBatch(taskTemplateDefinitions); + return AjaxResult.success(); + } + + /** + * save task schema definition + * + * @param taskSchemaDefinitions taskSchemaDefinitions + * @return result + */ + @PostMapping("/taskSchemaDefinition/save") + public AjaxResult taskSchemaDefinition(@RequestBody List taskSchemaDefinitions) { + taskSchemaDefinitionService.saveBatch(taskSchemaDefinitions); + return AjaxResult.success(); + } + + /** + * save task metrics definition + * + * @param taskMetricsDefinitions taskMetricsDefinitions + * @return result + */ + @PostMapping("/taskMetricsDefinition/save") + public AjaxResult taskMetricsDefinition(@RequestBody List taskMetricsDefinitions) { + taskMetricsDefinitionService.saveBatch(taskMetricsDefinitions); + return AjaxResult.success(); + } + + /** + * save task instance + * + * @param agentTaskParam agentTaskParam + * @return result + */ + @PostMapping("/taskInstance/save") + public AjaxResult taskInstance(@RequestBody AgentTaskParam agentTaskParam) { + ChainAssert.of(agentInstallService.getByAgentId(agentTaskParam.getAgentId())).nonNull("Agent not found"); + return AjaxResult.success(agentTaskManager.createTask(agentTaskParam)); + } + + /** + * get task list + * + * @return result + */ + @GetMapping("/task/list") + public AjaxResult taskList() { + List list = agentInstallService.list(); + return AjaxResult.success(agentTaskManager.queryAllOfAgentTask(list)); + } + + /** + * get task metrics definition list + * + * @return result + */ + @GetMapping("/taskMetricsDefinition/list") + public AjaxResult taskMetricsDefinitionList() { + return AjaxResult.success(taskMetricsDefinitionService.list()); + } + + /** + * start task + * + * @param agentId agent id + * @param id task id + * @return result + */ + @GetMapping("/start/task") + public AjaxResult startTask(@RequestParam("agentId") String agentId, @RequestParam("id") Long id) { + AgentInstallEntity agent = agentInstallService.getByAgentId(agentId); + agentHttpProxy.fetchAgentPubKey(agent); + agentTaskManager.startAgentTaskByTaskId(agent, id); + return AjaxResult.success(); + } + + /** + * get task schema definition list + * + * @return result + */ + @GetMapping("/taskSchemaDefinition/list") + public AjaxResult taskSchemaDefinitionList() { + return AjaxResult.success(taskSchemaDefinitionService.list()); + } + + /** + * get task template definition list + * + * @return result + */ + @GetMapping("/taskTemplateDefinition/list") + public AjaxResult list() { + return AjaxResult.success(taskTemplateDefinitionService.list()); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/AzController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/AzController.java new file mode 100644 index 000000000..6dc10fe13 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/AzController.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AzController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/AzController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.controller.ops; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.entity.ops.OpsAzEntity; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.system.service.ops.IOpsAzService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * AZ + * + * @author lhf + * @date 2022/8/6 20:45 + **/ +@RestController +@RequestMapping("/az") +public class AzController extends BaseController { + + @Autowired + private IOpsAzService opsAzService; + + @PostMapping + public AjaxResult add(@Validated @RequestBody OpsAzEntity az) { + return toAjax(opsAzService.add(az)); + } + + @GetMapping("/hasName") + public AjaxResult hasName(@RequestParam("name") String name) { + return AjaxResult.success(opsAzService.hasName(name)); + } + + @GetMapping("/page") + public TableDataInfo page(@RequestParam(required = false, value = "name") String name) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsAzEntity.class) + .like(StrUtil.isNotEmpty(name), OpsAzEntity::getName, name); + IPage page = opsAzService.page(startPage(), queryWrapper); + return getDataTable(page); + } + + @GetMapping("/{azId}") + public AjaxResult get(@PathVariable String azId){ + return AjaxResult.success(opsAzService.getById(azId)); + } + + @PutMapping("/{azId}") + public AjaxResult edit(@PathVariable String azId, + @Validated @RequestBody OpsAzEntity az) { + az.setAzId(azId); + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsAzEntity.class) + .eq(OpsAzEntity::getName, az.getName()) + .ne(OpsAzEntity::getAzId, azId); + + if (opsAzService.count(queryWrapper) > 0) { + throw new OpsException("name already exists"); + } + + return toAjax(opsAzService.updateById(az)); + } + + @DeleteMapping("/{azId}") + public AjaxResult del(@PathVariable String azId) { + return toAjax(opsAzService.removeById(azId)); + } + + @GetMapping("/listAll") + public AjaxResult listAll() { + List azList = opsAzService.list(); + return AjaxResult.success(azList); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/EncryptionController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/EncryptionController.java new file mode 100644 index 000000000..430da7287 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/EncryptionController.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * EncryptionController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/EncryptionController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.controller.ops; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author lhf + * @date 2022/11/29 22:38 + **/ +@RestController +@RequestMapping("/encryption") +public class EncryptionController { + + @Autowired + private EncryptionUtils encryptionUtils; + + @GetMapping("/getKey") + public AjaxResult getKey() { + AjaxResult ajax = AjaxResult.success(); + ajax.put("key", encryptionUtils.getKey()); + return ajax; + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/HostController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/HostController.java new file mode 100644 index 000000000..a5880d81b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/HostController.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/HostController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.controller.ops; + +import com.baomidou.mybatisplus.core.metadata.IPage; + +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.model.ops.HostBody; +import org.opengauss.admin.common.core.domain.model.ops.ImportAsynInfo; +import org.opengauss.admin.common.core.domain.model.ops.host.OpsHostVO; +import org.opengauss.admin.common.core.domain.model.ops.host.SSHBody; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.enums.OsSupportMap; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.excel.ImportAsynInfoUtils; +import org.opengauss.admin.system.service.ops.IHostService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Set; + +/** + * Host + * + * @author lhf + * @date 2022/8/7 22:24 + **/ +@RestController +@RequestMapping("/host") +public class HostController extends BaseController { + + @Autowired + private IHostService hostService; + + private final HashMap fileStreamMap = new HashMap(); + + @PostMapping + public AjaxResult add(@RequestBody @Validated HostBody hostBody) { + hostService.add(hostBody); + return AjaxResult.success(); + } + + @PostMapping("/upload") + public AjaxResult upload(@RequestPart @RequestParam("file") MultipartFile file) { + ImportAsynInfo asynInfo = new ImportAsynInfo(); + String uuid = ImportAsynInfoUtils.createUUID(asynInfo); + if (!file.isEmpty()) { + try { + fileStreamMap.put(uuid, file.getInputStream()); + } catch (IOException e) { + throw new OpsException(e.getMessage()); + } + } else { + throw new OpsException("The file is empty."); + } + return AjaxResult.success(uuid); + } + + /** + * Invoke related file handling operations. + * + * @param uuid The unique identifier of the file. + * @param isInvoke A flag that indicates whether to perform file processing; + * 0 means to process, and non-zero means not to process. + * @param currentLocale The current user's locale, used for internationalization support. + * @return AjaxResult The result of the method execution, + * which includes whether the operation was successful, along with related messages and data. + */ + @PostMapping("/invokeFile") + public AjaxResult invokeFile(@RequestParam String uuid, @RequestParam Integer isInvoke, + @RequestParam String currentLocale) { + if (isInvoke == 0) { + hostService.invokeFile(uuid, fileStreamMap, currentLocale); + } + fileStreamMap.remove(uuid); + return AjaxResult.success(); + } + + /** + * Download the template file, + * and download the corresponding template file based on the currently selected language. + * + * @param response HTTP response object, used to send files to the client. + * @param currentLocale The current user's locale, + * used to determine which version of the template to download. + */ + @GetMapping("/downloadTemplate/{currentLocale}") + public void downloadTemplate(HttpServletResponse response, @PathVariable String currentLocale) { + hostService.downloadTemplate(response, currentLocale); + } + + @GetMapping("/downloadErrorExcel/{uuid}") + public void downloadErrorExcel(HttpServletResponse response, @PathVariable String uuid) { + hostService.downloadErrorExcel(response, uuid); + ImportAsynInfoUtils.deleteAsynInfo(uuid); + } + + @GetMapping("get_import_plan") + public AjaxResult getImportPlanByUuid(@RequestParam String uuid) { + ImportAsynInfo asynInfo = ImportAsynInfoUtils.getAsynInfo(uuid); + return asynInfo == null + ? AjaxResult.error("The progress information has been fully obtained.") + : asynInfo.getMsg() == null ? AjaxResult.success(asynInfo) : AjaxResult.error(asynInfo.getMsg()); + } + + @GetMapping("/listAll") + public AjaxResult listAll(@RequestParam(value = "azId", required = false) String azId) { + List hostEntityList = hostService.listAll(azId); + return AjaxResult.success(hostEntityList); + } + + /** + * Query the list of hosts based on the AZ ID. + * + * @param name name + * @param tagIds tagIds + * @param os osName + * @return page list + */ + @GetMapping("/page") + public TableDataInfo page(@RequestParam(required = false) String name, + @RequestParam(value = "tagIds", required = false) Set tagIds, + @RequestParam(value = "os", required = false) String os) { + IPage page = hostService.pageHost(startPage(), name, tagIds, os); + return getDataTable(page); + } + + @PostMapping("/ping") + public AjaxResult ping(@RequestBody @Validated HostBody hostBody) { + boolean ping = hostService.ping(hostBody); + return ping ? AjaxResult.success() : AjaxResult.error("Connectivity test failed"); + } + + /** + * ping host. + * + * @param hostId host id + * @param rootPassword pwd + * @return ping status + * @deprecated Use {@link #ping(String, String)} instead. + */ + @Deprecated + @GetMapping("/ping/{hostId}") + public AjaxResult ping(@PathVariable String hostId, + @RequestParam(value = "rootPassword", required = false) String rootPassword) { + boolean ping = hostService.ping(hostId, rootPassword); + return ping ? AjaxResult.success() : AjaxResult.error("Connectivity test failed"); + } + + @DeleteMapping("/{hostId}") + public AjaxResult del(@PathVariable String hostId) { + boolean del = hostService.del(hostId); + return del ? AjaxResult.success() : AjaxResult.error("Failed to delete host"); + } + + /** + * edit host. + * + * @param hostId host id + * @param hostBody host body + * @return edit status + */ + @PutMapping("/{hostId}") + public AjaxResult edit(@PathVariable String hostId, @RequestBody @Validated HostBody hostBody) { + boolean edit = hostService.edit(hostId, hostBody); + return edit ? AjaxResult.success() : AjaxResult.error("Failed to edit host"); + } + + @PostMapping("/ssh") + public AjaxResult ssh(@RequestBody SSHBody sshBody) { + hostService.ssh(sshBody); + return AjaxResult.success(); + } + + /** + * ssh host + * + * @param hostId host id + * @param sshBody ss body + * @return ssh status + */ + @PostMapping("/ssh/{hostId}") + public AjaxResult ssh(@PathVariable("hostId") String hostId, @RequestBody SSHBody sshBody) { + hostService.ssh(hostId, sshBody); + return AjaxResult.success(); + } + + /** + * page monitor host lost + * + * @param hostIds host ids + * @param businessId business id + * @return monitor status + */ + @PostMapping("/monitor") + public AjaxResult pageMonitor(@RequestBody List hostIds, @RequestParam String businessId) { + return AjaxResult.success(hostService.pageMonitor(hostIds, businessId)); + } + + @GetMapping("/listSupportOsName") + public AjaxResult listSupportOsName() { + return AjaxResult.success(OsSupportMap.getSupportOsNameList()); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/HostUserController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/HostUserController.java new file mode 100644 index 000000000..9853f82cd --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/HostUserController.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostUserController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/HostUserController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.controller.ops; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.core.domain.model.ops.HostUserBody; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.utils.OpsAssert; +import org.opengauss.admin.system.service.ops.IHostService; +import org.opengauss.admin.system.service.ops.IHostUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author lhf + * @date 2022/8/11 23:00 + **/ +@RestController +@RequestMapping("/hostUser") +public class HostUserController extends BaseController { + @Autowired + private IHostUserService hostUserService; + @Autowired + private IHostService hostService; + + @GetMapping("/page/{hostId}") + public TableDataInfo page(@PathVariable("hostId") String hostId) { + IPage page = hostUserService.page(startPage(), Wrappers.lambdaQuery(OpsHostUserEntity.class).eq(OpsHostUserEntity::getHostId, hostId)); + return getDataTable(page); + } + + @GetMapping("/listAll/{hostId}") + public AjaxResult listAll(@PathVariable("hostId") String hostId) { + List opsHostUserEntityList = hostUserService.list(Wrappers.lambdaQuery(OpsHostUserEntity.class).eq(OpsHostUserEntity::getHostId, hostId)); + return AjaxResult.success(opsHostUserEntityList); + } + + @GetMapping("/listAllWithoutRoot/{hostId}") + public AjaxResult listAllWithoutRoot(@PathVariable("hostId") String hostId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostUserEntity.class) + .eq(OpsHostUserEntity::getHostId, hostId) + .ne(OpsHostUserEntity::getUsername, "root"); + List opsHostUserEntityList = hostUserService.list(queryWrapper); + return AjaxResult.success(opsHostUserEntityList); + } + + @PostMapping + public AjaxResult add(@RequestBody @Validated HostUserBody hostUserBody) { + OpsHostEntity host = hostService.getById(hostUserBody.getHostId()); + OpsAssert.nonNull(host, "host id " + hostUserBody.getHostId() + " is invalid"); + hostUserService.add(host, hostUserBody); + return AjaxResult.success(); + } + + @PutMapping("/{hostUserId}") + public AjaxResult edit(@PathVariable String hostUserId, + @RequestBody HostUserBody hostUserBody) { + OpsHostEntity host = hostService.getById(hostUserBody.getHostId()); + OpsAssert.nonNull(host, "host id " + hostUserBody.getHostId() + " is invalid"); + hostUserService.edit(host, hostUserId, hostUserBody); + return AjaxResult.success(); + } + + @DeleteMapping("/{hostUserId}") + public AjaxResult del(@PathVariable String hostUserId) { + hostUserService.del(hostUserId); + return AjaxResult.success(); + } + + /** + * check if the user has root permission + * + * @param userId user ID + * @return has root permission or not + */ + @GetMapping("/hasRootPermission/{userId}") + public AjaxResult hasRootPermission(@PathVariable("userId") String userId) { + OpsHostUserEntity hostUser = hostUserService.getById(userId); + OpsAssert.nonNull(hostUser, "host user id " + userId + " is invalid"); + + OpsHostEntity host = hostService.getById(hostUser.getHostId()); + OpsAssert.nonNull(host, "user hostId " + hostUser.getHostId() + " is invalid"); + boolean hasPermission = hostUserService.hasRootPermission(host, userId); + return AjaxResult.success(hasPermission); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/JdbcDbClusterController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/JdbcDbClusterController.java new file mode 100644 index 000000000..e09d7ab8d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/JdbcDbClusterController.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JdbcDbClusterController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/JdbcDbClusterController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.controller.ops; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterImportAnalysisVO; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterInputDto; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterVO; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.system.service.ops.IOpsJdbcDbClusterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; + +/** + * @author lhf + * @date 2023/1/13 11:10 + **/ +@RestController +@RequestMapping("/jdbcDbCluster") +public class JdbcDbClusterController extends BaseController { + @Autowired + private IOpsJdbcDbClusterService opsJdbcDbClusterService; + + @PostMapping("/add") + public AjaxResult add(@RequestBody JdbcDbClusterInputDto clusterInput) { + opsJdbcDbClusterService.add(clusterInput); + return AjaxResult.success(); + } + + @GetMapping("/page") + public TableDataInfo page(@RequestParam(required = false, value = "name") String name,@RequestParam(required = false,value = "ip") String ip, @RequestParam(required = false,value = "type") String type) { + Page page = opsJdbcDbClusterService.page(name,ip,type,startPage()); + return getDataTable(page); + } + + @DeleteMapping("/{clusterId}") + public AjaxResult del(@PathVariable("clusterId") String clusterId) { + opsJdbcDbClusterService.del(clusterId); + return AjaxResult.success(); + } + + @PutMapping("/{clusterId}") + public AjaxResult update(@PathVariable("clusterId") String clusterId, @RequestBody JdbcDbClusterInputDto clusterInput) { + opsJdbcDbClusterService.update(clusterId, clusterInput); + return AjaxResult.success(); + } + + @PostMapping("/importAnalysis") + public AjaxResult importAnalysis(@RequestParam("file") MultipartFile file) { + JdbcDbClusterImportAnalysisVO analysisVO = opsJdbcDbClusterService.importAnalysis(file); + return AjaxResult.success(analysisVO); + } + + @PostMapping("/importCluster") + public AjaxResult importCluster(@RequestParam("file") MultipartFile file) { + opsJdbcDbClusterService.importCluster(file); + return AjaxResult.success(); + } + + @GetMapping("/downloadTemplate") + public ResponseEntity downloadTemplate(){ + String fileName = "JDBC_IMPORT_TEMPLATE.csv"; + try { + fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()); + } catch (UnsupportedEncodingException e) { + + } + HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Type", "application/octet-stream"); + headers.add("Access-Control-Expose-Headers", "Content-Disposition"); + headers.add("Content-Disposition", "attachment;filename=" + fileName); + + String content = "\"集群名称(自定义一个集群名,集群名相同则认为是同一个集群)\",\"连接URL(JDBC的URL信息,例如 jdbc:opengauss://IP:PORT/databasename)\",\"用户名(数据库连接用户名)\",\"密码(数据库用户名对应的密码)\"\n" + + "\"\",\"\",\"\",\"\""; + return ResponseEntity.ok().headers(headers).body(content.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/JdbcDbClusterNodeController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/JdbcDbClusterNodeController.java new file mode 100644 index 000000000..76f231f5f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/JdbcDbClusterNodeController.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JdbcDbClusterNodeController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/JdbcDbClusterNodeController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.controller.ops; + +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterNodeInputDto; +import org.opengauss.admin.system.service.ops.IOpsJdbcDbClusterNodeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * @author lhf + * @date 2023/1/13 15:34 + **/ +@RestController +@RequestMapping("/jdbcDbClusterNode") +public class JdbcDbClusterNodeController extends BaseController { + + @Autowired + private IOpsJdbcDbClusterNodeService opsJdbcDbClusterNodeService; + + @DeleteMapping("/{clusterNodeId}") + public AjaxResult del(@PathVariable("clusterNodeId") String clusterNodeId) { + opsJdbcDbClusterNodeService.del(clusterNodeId); + return AjaxResult.success(); + } + + @PutMapping("/{clusterNodeId}") + public AjaxResult update(@PathVariable("clusterNodeId") String clusterNodeId, @RequestBody JdbcDbClusterNodeInputDto clusterNodeInput) { + opsJdbcDbClusterNodeService.update(clusterNodeId, clusterNodeInput); + return AjaxResult.success(); + } + + @PostMapping("/add/{clusterId}") + public AjaxResult add(@PathVariable("clusterId") String clusterId, @RequestBody JdbcDbClusterNodeInputDto clusterNodeInput) { + opsJdbcDbClusterNodeService.add(clusterId, clusterNodeInput); + return AjaxResult.success(); + } + + @PostMapping("/ping") + public AjaxResult ping(@RequestBody JdbcDbClusterNodeInputDto clusterNodeInput) { + return AjaxResult.success(opsJdbcDbClusterNodeService.ping(clusterNodeInput)); + } + + @GetMapping("/monitor/{clusterNodeId}") + public AjaxResult monitor(@PathVariable("clusterNodeId") String clusterNodeId,@RequestParam("businessId") String businessId){ + return AjaxResult.success(opsJdbcDbClusterNodeService.monitor(clusterNodeId,businessId)); + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/OpsClusterController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/OpsClusterController.java new file mode 100644 index 000000000..aa01a52d6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/OpsClusterController.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/OpsClusterController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.web.controller.ops; + +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.model.ops.ClusterSummaryVO; +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO; +import org.opengauss.admin.common.core.domain.model.ops.check.CheckSummaryVO; +import org.opengauss.admin.system.service.ops.IOpsClusterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; + +/** + * Operation and maintenance cluster operations + * + * @author lhf + * @date 2022/8/6 17:38 + **/ +@RestController +@RequestMapping("/opsCluster") +public class OpsClusterController extends BaseController { + @Autowired + private IOpsClusterService opsClusterService; + + @GetMapping("/listCluster") + public AjaxResult listCluster() { + List clusterVOList = opsClusterService.listCluster(); + return AjaxResult.success(clusterVOList); + } + + @GetMapping("/summary") + public AjaxResult summary() { + ClusterSummaryVO clusterSummaryVO = opsClusterService.summary(); + return AjaxResult.success(clusterSummaryVO); + } + + @GetMapping("/monitor") + public AjaxResult monitor(@RequestParam String clusterId, @RequestParam String hostId, @RequestParam String businessId) { + opsClusterService.monitor(clusterId, hostId, businessId); + return AjaxResult.success(); + } + + @GetMapping("/check") + public AjaxResult check(@RequestParam String clusterId,@RequestParam(value = "rootPassword",required = false) String rootPassword) { + CheckSummaryVO checkSummaryVO = opsClusterService.check(clusterId,rootPassword); + return AjaxResult.success(checkSummaryVO); + } + + @GetMapping + public AjaxResult threadPoolMonitor() { + Map res = opsClusterService.threadPoolMonitor(); + return AjaxResult.success(res); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/OpsHostTagController.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/OpsHostTagController.java new file mode 100644 index 000000000..8d9bb668d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/OpsHostTagController.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsHostTagController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/OpsHostTagController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.controller.ops; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.opengauss.admin.common.core.controller.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostTagEntity; +import org.opengauss.admin.common.core.domain.model.ops.host.tag.HostTagAddInputDto; +import org.opengauss.admin.common.core.domain.model.ops.host.tag.HostTagInputDto; +import org.opengauss.admin.common.core.domain.model.ops.host.tag.HostTagPageVO; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.system.service.ops.IOpsHostTagRelService; +import org.opengauss.admin.system.service.ops.IOpsHostTagService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @author lhf + * @date 2023/3/14 23:42 + **/ +@RestController +@RequestMapping("/hostTag") +public class OpsHostTagController extends BaseController { + @Autowired + private IOpsHostTagService opsHostTagService; + @Autowired + private IOpsHostTagRelService opsHostTagRelService; + + @PutMapping("/addTag") + public AjaxResult addTag(@RequestBody HostTagInputDto hostTagInputDto) { + opsHostTagService.addTag(hostTagInputDto); + return AjaxResult.success(); + } + + @PutMapping("/delTag") + public AjaxResult delTagRelation(@RequestBody HostTagInputDto hostTagInputDto) { + opsHostTagRelService.delTagRelation(hostTagInputDto); + return AjaxResult.success(); + } + + @GetMapping("/listAll") + public AjaxResult listAll() { + List list = opsHostTagService.list(); + return AjaxResult.success(list); + } + + @GetMapping("/page") + public TableDataInfo page(@RequestParam(value = "name", required = false) String name) { + IPage page = opsHostTagService.page(startPage(), name); + return getDataTable(page); + } + + @PostMapping("/add") + public AjaxResult add(@RequestBody HostTagAddInputDto addInputDto) { + opsHostTagService.add(addInputDto.getName()); + return AjaxResult.success(); + } + + @PutMapping("/update/{tagId}") + public AjaxResult update(@PathVariable("tagId") String tagId, @RequestBody HostTagAddInputDto addInputDto) { + opsHostTagService.update(tagId, addInputDto.getName()); + return AjaxResult.success(); + } + + @DeleteMapping("/del/{tagId}") + @Transactional(rollbackFor = Exception.class) + public AjaxResult del(@PathVariable("tagId") String tagId) { + opsHostTagRelService.delByTagId(tagId); + opsHostTagService.removeById(tagId); + return AjaxResult.success(); + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/WsEndpoint.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/WsEndpoint.java new file mode 100644 index 000000000..6e15549b3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/WsEndpoint.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WsEndpoint.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/WsEndpoint.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.web.controller.ops; + +import org.opengauss.admin.system.service.ops.IWsService; + +import jakarta.websocket.OnClose; +import jakarta.websocket.OnMessage; +import jakarta.websocket.Session; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.config.annotation.EnableWebSocket; + +import jakarta.websocket.OnOpen; +import jakarta.websocket.OnError; +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; + +/** + * websocket + * @author lhf + * @date 2022/8/7 14:00 + **/ +@Slf4j +@Component +@EnableWebSocket +@ServerEndpoint("/websocket/{type}/{businessId}") +public class WsEndpoint { + + private static IWsService wsService; + + @Autowired + public void setWsService(IWsService wsService){ + WsEndpoint.wsService = wsService; + } + + @OnOpen + public void onOpen(Session session, + @PathParam("type") String type, + @PathParam("businessId") String businessId) { + log.info("onOpen, type:{},businessId:{}", type, businessId); + wsService.onOpen(businessId, session); + } + + @OnClose + public void onClose(@PathParam("type") String type, + @PathParam("businessId") String businessId) { + log.info("onClose, type:{},businessId:{}", type, businessId); + wsService.onClose(businessId); + } + + /** + * ws endpoint on error handler + * + * @param type type + * @param businessId bussiness id + * @param error error msg + */ + @OnError + public void onError(@PathParam("type") String type, @PathParam("businessId") String businessId, Throwable error) { + log.error("onError,type:{},businessId:{},err ", type, businessId, error); + } + + @OnMessage(maxMessageSize = 10 * 1024 * 1024) + public void onMessage(@PathParam("type") String type, + @PathParam("businessId") String businessId, + String message, Session session) { + log.info("onMessage, type:{},businessId:{},message:{}", type, businessId, message); + wsService.onMessage(businessId, message); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/core/config/AdminApplicationRunner.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/core/config/AdminApplicationRunner.java new file mode 100644 index 000000000..c81a252fd --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/core/config/AdminApplicationRunner.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AdminApplicationRunner.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/core/config/AdminApplicationRunner.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.web.core.config; + +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.integration.operator.PluginOperator; + +import cn.hutool.core.collection.CollUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.system.service.ISysPluginService; +import org.opengauss.admin.system.service.ISysSettingService; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.opengauss.agent.service.IAgentInstallService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; + +import java.util.List; + +/** + * @className: AdminApplicationRunner + * @description: AdminApplicationRunner + * @author: xielibo + * @date: 2022-12-17 15:43 + **/ +@Slf4j +@Component +public class AdminApplicationRunner implements ApplicationRunner { + @Resource + private EncryptionUtils encryptionUtils; + @Resource + private IAgentInstallService agentInstallService; + @Resource + private ISysSettingService sysSettingService; + @Autowired + private PluginOperator pluginOperator; + @Autowired + private ISysPluginService sysPluginService; + + @Override + public void run(ApplicationArguments args) throws Exception { + clearDirtyDataOfPlugins(); + this.encryptionUtils.updateKeyPairSecret(); + this.encryptionUtils.refreshKeyPair(false); + sysSettingService.initHttpProxy(); + agentInstallService.startAllOfAgent(); + } + + private void clearDirtyDataOfPlugins() { + List pluginList = sysPluginService.getPluginList(); + if (CollUtil.isEmpty(pluginList)) { + return; + } + for (String pluginId : pluginList) { + PluginInfo pluginInfo = pluginOperator.getPluginInfo(pluginId); + if (pluginInfo == null) { + sysPluginService.uninstallPluginByPluginId(pluginId); + } + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/core/config/DataSourceConfig.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/core/config/DataSourceConfig.java new file mode 100644 index 000000000..32fc7077f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/core/config/DataSourceConfig.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DataSourceConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/config/DataSourceConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.web.core.config; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.enums.DbDataLocationEnum; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.system.base.DbDataLocation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer; +import org.springframework.boot.sql.init.DatabaseInitializationMode; +import org.springframework.boot.sql.init.DatabaseInitializationSettings; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +import javax.sql.DataSource; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * DataSourceConfig + * + * @since 2024/1/16 + */ +@Slf4j +@Configuration +public class DataSourceConfig { + @Autowired + private ApplicationContext applicationContext; + + @Bean("customDataSourceInitializer") + @Profile("!dev") + DataSourceScriptDatabaseInitializer dataSourceScriptDatabaseInitializer(DataSourceProperties properties, + DataSource dataSource) { + String driverClassName = properties.getDriverClassName(); + if (DbDataLocationEnum.OPENGAUSS.getDriverClass().equals(driverClassName)) { + checkDatabaseAvailability(properties); + } + Optional dbDataLocationEnum = DbDataLocationEnum.of(driverClassName); + DatabaseInitializationSettings settings = new DatabaseInitializationSettings(); + settings.setContinueOnError(true); + settings.setSeparator(";"); + settings.setMode(DatabaseInitializationMode.ALWAYS); + if (dbDataLocationEnum.isEmpty()) { + return new DataSourceScriptDatabaseInitializer(dataSource, new DatabaseInitializationSettings()); + } + settings.setDataLocations(dbDataLocationEnum.get().getLocations()); + return new DataSourceScriptDatabaseInitializer(dataSource, settings); + } + + private void checkDatabaseAvailability(DataSourceProperties properties) { + try (Connection connection = createConnection(properties)) { + checkSqlCompatibility(connection); + checkPermission(connection, properties.getUsername()); + } catch (SQLException e) { + log.error("Failed to check the database availability.", e); + System.exit(SpringApplication.exit(applicationContext, () -> 1)); + } + } + + private Connection createConnection(DataSourceProperties properties) throws SQLException { + Connection connection = DriverManager.getConnection(properties.getUrl(), properties.getUsername(), + properties.getPassword()); + if (connection == null) { + log.error("Failed to get connection to the database: connection is null."); + System.exit(SpringApplication.exit(applicationContext, () -> 1)); + } + return connection; + } + + /** + * release the database location according to the database type + * + * @param properties properties + * @return DbDataLocation + */ + @Bean + public DbDataLocation getDbDataLocation(DataSourceProperties properties) { + String driverClassName = properties.getDriverClassName(); + return DbDataLocationEnum.of(driverClassName).map(DbDataLocation::new).orElseThrow(() -> { + String supportDrivers = Arrays.stream(DbDataLocationEnum.values()) + .map(DbDataLocationEnum::getDriverClass) + .collect(Collectors.joining()); + return new OpsException( + "Invalid driver class name: " + driverClassName + ", support drivers: " + supportDrivers); + }); + } + + private void checkSqlCompatibility(Connection connection) throws SQLException { + String showCompatibilitySql = "show sql_compatibility;"; + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(showCompatibilitySql)) { + if (resultSet.next()) { + String sqlCompatibility = resultSet.getString(1); + if (sqlCompatibility == null || !sqlCompatibility.equals("A")) { + log.error("The sql_compatibility type of the database does not meet requirements. " + + "The required type is 'A', and the current type is '{}'.", sqlCompatibility); + System.exit(SpringApplication.exit(applicationContext, () -> 1)); + } + } + } + } + + private void checkPermission(Connection connection, String username) throws SQLException { + String permissionSql = String.format("select rolsystemadmin from pg_roles where rolname= '%s';", username); + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(permissionSql)) { + if (resultSet.next()) { + String permissionResult = resultSet.getString(1); + if (permissionResult == null || !permissionResult.equals("t")) { + log.error("The database user permission does not meet requirements. " + + "Please set the user as the sysadmin."); + System.exit(SpringApplication.exit(applicationContext, () -> 1)); + } + } + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/core/config/HostMonitorCacheInitialize.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/core/config/HostMonitorCacheInitialize.java new file mode 100644 index 000000000..c83a2122a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/core/config/HostMonitorCacheInitialize.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.web.core.config; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.agent.TaskMetricsDefinition; +import org.opengauss.admin.system.service.HostMonitorCacheService; +import org.opengauss.agent.service.AgentTaskManager; +import org.springframework.stereotype.Component; + +import java.util.List; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; + +/** + * HostMonitorCacheInitialize + * + * @author: wangchao + * @Date: 2025/8/1 16:40 + * @since 7.0.0-RC2 + **/ +@Slf4j +@Component +public class HostMonitorCacheInitialize { + @Resource + private HostMonitorCacheService hostMonitorCacheService; + @Resource + private AgentTaskManager agentTaskManager; + + /** + * initialize host monitor cache + */ + @PostConstruct + public void initialize() { + List metricsDefinitionList = agentTaskManager.queryHostMetricsDefinition(); + hostMonitorCacheService.initHostMonitorCacheEnvironment(metricsDefinitionList); + hostMonitorCacheService.startHostMonitorScheduled(); + log.info("host monitor cache initialize success"); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/core/dbinit/SystemUploadPathInit.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/core/dbinit/SystemUploadPathInit.java new file mode 100644 index 000000000..ce35ce720 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/core/dbinit/SystemUploadPathInit.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SystemUploadPathInit.java + * + * IDENTIFICATION + * openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/core/dbinit/SystemUploadPathInit.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.web.core.dbinit; + +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.config.SystemConfig; +import org.opengauss.admin.common.core.domain.entity.SysSettingEntity; +import org.opengauss.admin.system.service.ISysSettingService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.io.File; +import java.io.IOException; + +/** + * The class is used to initialize the system upload path. + * + * @since 2024/3/22 10:10 + */ +@Slf4j +@Component +public class SystemUploadPathInit implements CommandLineRunner { + @Autowired + private ISysSettingService sysSettingService; + + /** + * run + * + * @param args args + */ + @Override + public void run(String... args) { + Integer adminId = 1; + SysSettingEntity setting = sysSettingService.getSetting(adminId); + if (ObjectUtils.isEmpty(setting)) { + log.error("Unable to obtain the administrator user. " + + "Failed to initialize the system upload path."); + return; + } + + String defaultUploadPath = "/ops/files/"; + if (!setting.getUploadPath().equals(defaultUploadPath)) { + mkdirFile(setting.getUploadPath()); + return; + } + + mkdirFile(SystemConfig.getUploadPath()); + + setting.setUploadPath(SystemConfig.getUploadPath()); + sysSettingService.saveOrUpdate(setting); + } + + /** + * mkdirs file by file path + * + * @param filePath file path + */ + private void mkdirFile(String filePath) { + try { + File pathFile = new File(filePath); + if (!pathFile.exists() && !pathFile.mkdirs()) { + throw new IOException(String.format("Folder %s create failed.", filePath)); + } + } catch (IOException e) { + log.error("Default system upload path create failed: ", e); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/listener/MyPluginInitializerListener.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/listener/MyPluginInitializerListener.java new file mode 100644 index 000000000..86dac96af --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/listener/MyPluginInitializerListener.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MyPluginInitializerListener.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/listener/MyPluginInitializerListener.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.listener; + +import com.gitee.starblues.integration.listener.PluginInitializerListener; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author xielibo + * @version 1.0 + */ +@Component +@Slf4j +public class MyPluginInitializerListener implements PluginInitializerListener { + @Autowired + private EncryptionUtils encryptionUtils; + + @Override + public void before() { + log.info("My plugin init before."); + this.encryptionUtils.updateKeyPairSecret(); + this.encryptionUtils.refreshKeyPair(); + } + + @Override + public void complete() { + log.info("My plugin init complete."); + } + + @Override + public void failure(Throwable throwable) { + log.error("My plugin init error: {}", throwable.getMessage()); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/listener/MypluginListener.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/listener/MypluginListener.java new file mode 100644 index 000000000..fe19522f6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/listener/MypluginListener.java @@ -0,0 +1,95 @@ +/** + Copyright starBlues. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.web.listener; + +import com.gitee.starblues.core.PluginInfo; +import com.gitee.starblues.integration.listener.PluginListener; +import com.gitee.starblues.integration.operator.PluginOperator; +import org.opengauss.admin.system.service.ISysPluginService; +import org.opengauss.admin.web.controller.SystemPluginController; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.nio.file.Path; +import java.util.Map; + + +/** + * Plugin Listener + */ +@Component +public class MypluginListener implements PluginListener { + private static final Logger log = LoggerFactory.getLogger(MypluginListener.class); + + @Autowired + private ISysPluginService sysPluginService; + + @Autowired + private SystemPluginController systemPluginController; + + @Autowired + private PluginOperator pluginOperator; + + @Override + public void loadSuccess(PluginInfo pluginInfo) { + log.info("plugin [{}] load success.", pluginInfo.getPluginId()); + } + + @Override + public void loadFailure(Path path, Throwable throwable) { + log.error("plugin[{}] load fail. {}", path, throwable); + } + + @Override + public void unLoadSuccess(PluginInfo pluginInfo) { + log.info("plugin[{}] uninstall success.", pluginInfo.getPluginId()); + sysPluginService.uninstallPluginByPluginId(pluginInfo.getPluginId()); + } + + @Override + public void unLoadFailure(PluginInfo pluginInfo, Throwable throwable) { + log.error("plugin[{}] uninstall fail. {}", pluginInfo.getPluginId(), throwable); + } + + @Override + public void startSuccess(PluginInfo pluginInfo) { + log.info("plugin[{}] start success", pluginInfo.getPluginId()); + if (pluginInfo.getExtensionInfo() == null || pluginInfo.getExtensionInfo().isEmpty()) { + pluginInfo = pluginOperator.getPluginInfo(pluginInfo.getPluginId()); + } + Map result = systemPluginController.updateSystemByPluginInfo(pluginInfo); + log.info("plugin[{}] update logo {}.", pluginInfo.getPluginId(), result == null ? "failed" : "success"); + } + + @Override + public void startFailure(PluginInfo pluginInfo, Throwable throwable) { + log.error("plugin[{}] start fail. {}", pluginInfo.getPluginId(), throwable); + + pluginOperator.unload(pluginInfo.getPluginId()); + } + + @Override + public void stopSuccess(PluginInfo pluginInfo) { + log.info("plugin[{}] stop success.", pluginInfo.getPluginId()); + } + + @Override + public void stopFailure(PluginInfo pluginInfo, Throwable throwable) { + log.error("plugin[{}] stop fail. {}", pluginInfo.getPluginId(), throwable); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/ws/WebSocketEndpoint.java b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/ws/WebSocketEndpoint.java new file mode 100644 index 000000000..01d7a2485 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/java/org/opengauss/admin/web/ws/WebSocketEndpoint.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WebSocketEndpoint.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/ws/WebSocketEndpoint.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.web.ws; + +import org.opengauss.admin.system.service.IWebSocketService; + +import jakarta.websocket.OnClose; +import jakarta.websocket.OnError; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.config.annotation.EnableWebSocket; + +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; + +/** + * @className: WsSocketController + * @description: WebSocketEndpoint + * @author: xielibo + * @date: 2022-11-14 22:31 + **/ +@Slf4j +@Component +@EnableWebSocket +@ServerEndpoint("/ws/{pluginId}/{sessionId}") +public class WebSocketEndpoint { + + private static IWebSocketService wsService; + @Autowired + public void setWsService(IWebSocketService wsService){ + WebSocketEndpoint.wsService = wsService; + } + + @OnOpen + public void onOpen(Session session, + @PathParam("pluginId") String pluginId, + @PathParam("sessionId") String sessionId) { + log.info("WebSocket onOpen, pluginId: {}", pluginId); + wsService.onOpen(pluginId,sessionId,session); + } + + @OnClose + public void onClose(@PathParam("pluginId") String pluginId, + @PathParam("sessionId") String sessionId) { + log.info("WebSocket onClose, pluginId: {}", pluginId); + wsService.onClose(pluginId,sessionId); + } + + @OnError + public void onError(@PathParam("pluginId") String pluginId, + @PathParam("sessionId") String sessionId, + Throwable error) { + log.error("WebSocket onError, pluginId: {}, errorMessage: {}", pluginId, error.getMessage()); + } + + @OnMessage(maxMessageSize = 10 * 1024 * 1024) + public void onMessage(@PathParam("pluginId") String pluginId, + @PathParam("sessionId") String sessionId, + String message, Session session) { + log.info("WebSocket onMessage, pluginId: {}", pluginId); + wsService.onMessage(pluginId,sessionId,message); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/META-INF/spring-devtools.properties b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/META-INF/spring-devtools.properties new file mode 100644 index 000000000..2b23f85a3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/META-INF/spring-devtools.properties @@ -0,0 +1 @@ +restart.include.json=/com.alibaba.fastjson.*.jar \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/application-dev.yml b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/application-dev.yml new file mode 100644 index 000000000..962ca4185 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/application-dev.yml @@ -0,0 +1,47 @@ +system: + defaultStoragePath: files/default_path + whitelist: + enabled: false +server: + port: 9494 + servlet: + context-path: / +logging: + file: + path: logs +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + # For openGauss + #driver-class-name: org.opengauss.Driver + #url: jdbc:opengauss://127.0.0.1:5432/databasename?currentSchema=public&batchMode=off + #username: username + #password: password + druid: + test-while-idle: true + test-on-borrow: true + validation-query: "select 1" + validation-query-timeout: 10000 + connection-error-retry-attempts: 0 + break-after-acquire-failure: true + max-wait: 300000 + keep-alive: true + max-active: 30 + min-evictable-idle-time-millis: 600000 + +plugin: + sortInitPluginIds: + decrypt: + enable: false + version: 0.0.0 +plugins: + oauth-login: + # A unique identifier obtained by registering with DevKit + client-id: your_client_id + # A secret key paired with the client_id + client-secret: your_client_secret + datakit-url: https://ip:port + devkit-url: https://ip:port + # SSL certificate + ssl-key: + diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/application.yml b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/application.yml new file mode 100644 index 000000000..d2c0ddacb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/application.yml @@ -0,0 +1,81 @@ +version: 7.0.0-RC3 +server: + proxy: + hostname: localhost + port: 8080 + tomcat: + uri-encoding: UTF-8 + max-threads: 800 + min-spare-threads: 30 + ssl: + enabled: false +spring: + profiles: + active: dev + messages: + basename: i18n/messages + servlet: + multipart: + max-file-size: 20GB + max-request-size: 20GB + mvc: + async: + request-timeout: 60000 + sql: + init: + mode: always + main: + allow-bean-definition-overriding: true + +mybatis-plus: + typeAliasesPackage: org.opengauss.admin.**.domain + mapperLocations: classpath*:mapper/**/*Mapper.xml + configLocation: classpath:mybatis/mybatis-config.xml + +pagehelper: + helperDialect: postgresql + supportMethodsArguments: true + params: count=countSql + +plugin: + mainPackage: org.opengauss.admin + runMode: prod + # runMode: deployment + pluginPath: + - ~/visualtool-plugin + +# pluginRestPathPrefix: /static-plugin +agent: + localPath: ./agent + agentName: datakit-agent-${version}-runner.jar +disruptor: + bufferSize: 1024 + backpressure: + threshold: 10 + initDelay: 1 + maxDelay: 100 + factor: 2 +management: + server: + port: 9494 + endpoints: + enabled-by-default: false + web: + exposure: + include: 'prometheus' + base-path: "/" + endpoint: + prometheus: + enabled: true + health: + show-details: always + metrics: + enable: + jvm: false + logback: false + files: false + tomcat: false + executor: false + disk: false + uptime: false + integration: false diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/banner.txt b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/banner.txt new file mode 100644 index 000000000..329280f02 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Application Version: ${system.version} +Spring Boot Version: ${spring-boot.version} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/db/intarkDB-init.sql b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/db/intarkDB-init.sql new file mode 100644 index 000000000..413da6d2b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/db/intarkDB-init.sql @@ -0,0 +1,2065 @@ +CREATE TABLE IF NOT EXISTS "ops_az" ( + "az_id" int8 NOT NULL PRIMARY KEY, + "name" varchar(255) NOT NULL, + "priority" int2, + "address" varchar(255) , + "remark" varchar(255) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ) +; +COMMENT ON COLUMN "ops_az"."az_id" IS 'az主键'; +COMMENT ON COLUMN "ops_az"."name" IS 'az名称'; +COMMENT ON COLUMN "ops_az"."priority" IS '优先级'; +COMMENT ON COLUMN "ops_az"."address" IS '实际地理位置'; +COMMENT ON COLUMN "ops_az"."remark" IS '描述'; +COMMENT ON COLUMN "ops_az"."create_by" IS '创建者'; +COMMENT ON COLUMN "ops_az"."create_time" IS '创建时间'; +COMMENT ON COLUMN "ops_az"."update_by" IS '更新者'; +COMMENT ON COLUMN "ops_az"."update_time" IS '更新时间'; + +INSERT INTO "ops_az"("az_id", "name", "priority", "address", "remark", "create_by", "create_time", "update_by", "update_time") VALUES (1639451067550920705, 'az1', NULL, '中国', NULL, 'admin', '2023-03-25 10:16:08.69', 'admin', '2023-03-25 10:16:08.69'); + +-- ---------------------------- +-- Table structure for ops_check +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "ops_check" ( + "check_id" varchar(255) NOT NULL PRIMARY KEY, + "cluster_id" varchar(255) , + "check_res" text , + "remark" varchar(255) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ) +; +COMMENT ON COLUMN "ops_check"."remark" IS '描述'; +COMMENT ON COLUMN "ops_check"."create_by" IS '创建者'; +COMMENT ON COLUMN "ops_check"."create_time" IS '创建时间'; +COMMENT ON COLUMN "ops_check"."update_by" IS '更新者'; +COMMENT ON COLUMN "ops_check"."update_time" IS '更新时间'; + +-- ---------------------------- +-- Table structure for ops_cluster +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "ops_cluster" ( + "cluster_id" varchar(255) NOT NULL PRIMARY KEY, + "version" varchar(255) , + "version_num" varchar(255) , + "install_mode" varchar(64) , + "deploy_type" varchar(64) , + "cluster_name" varchar(255) , + "install_package_path" varchar(255) , + "database_password" text , + "database_username" varchar(255) , + "remark" varchar(255) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp, + "install_path" varchar(255) , + "log_path" varchar(255) , + "tmp_path" varchar(255) , + "om_tools_path" varchar(255) , + "core_path" varchar(255) , + "port" varchar(255) , + "enable_dcf" int2, + "dcf_port" varchar(255) + ) +; +COMMENT ON COLUMN "ops_cluster"."cluster_id" IS '集群标识'; +COMMENT ON COLUMN "ops_cluster"."version" IS '版本'; +COMMENT ON COLUMN "ops_cluster"."version_num" IS '版本号'; +COMMENT ON COLUMN "ops_cluster"."install_mode" IS '安装模式'; +COMMENT ON COLUMN "ops_cluster"."deploy_type" IS '部署方式'; +COMMENT ON COLUMN "ops_cluster"."cluster_name" IS '集群名称'; +COMMENT ON COLUMN "ops_cluster"."install_package_path" IS '安装包路径'; +COMMENT ON COLUMN "ops_cluster"."database_password" IS '数据库密码'; +COMMENT ON COLUMN "ops_cluster"."database_username" IS '数据库用户名'; +COMMENT ON COLUMN "ops_cluster"."remark" IS '描述'; +COMMENT ON COLUMN "ops_cluster"."create_by" IS '创建者'; +COMMENT ON COLUMN "ops_cluster"."create_time" IS '创建时间'; +COMMENT ON COLUMN "ops_cluster"."update_by" IS '更新者'; +COMMENT ON COLUMN "ops_cluster"."update_time" IS '更新时间'; +COMMENT ON COLUMN "ops_cluster"."install_path" IS '企业版安装路径'; +COMMENT ON COLUMN "ops_cluster"."log_path" IS '企业版日志路径'; +COMMENT ON COLUMN "ops_cluster"."tmp_path" IS '企业版临时文件路径'; +COMMENT ON COLUMN "ops_cluster"."om_tools_path" IS '企业版om路径'; +COMMENT ON COLUMN "ops_cluster"."core_path" IS '企业版核心路径'; +COMMENT ON COLUMN "ops_cluster"."port" IS '企业版端口'; +COMMENT ON COLUMN "ops_cluster"."enable_dcf" IS '是否开启DCF 0否1是'; +COMMENT ON COLUMN "ops_cluster"."dcf_port" IS 'DCF端口'; + +-- ---------------------------- +-- Table structure for ops_cluster_node +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "ops_cluster_node" ( + "cluster_node_id" int8 NOT NULL PRIMARY KEY, + "cluster_role" varchar(255) , + "host_id" int8, + "install_user_id" int8, + "install_path" varchar(255) , + "data_path" varchar(255) , + "pkg_path" varchar(255) , + "install_demo_database" int2, + "cluster_id" varchar(255) , + "is_install_cm" int2, + "is_cm_master" int2, + "cm_data_path" varchar(255) , + "cm_port" varchar(255) , + "xlog_path" varchar(255) , + "is_enable_dss" int2, + "dss_data_lun_link_path" varchar(255) , + "xlog_lun_link_path" varchar(255) , + "cm_voting_lun_link_path" varchar(255) , + "cm_sharing_lun_link_path" varchar(255) , + "remark" varchar(255) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ) +; + +COMMENT ON COLUMN "ops_cluster_node"."cluster_role" IS '集群角色'; +COMMENT ON COLUMN "ops_cluster_node"."host_id" IS '主机ID'; +COMMENT ON COLUMN "ops_cluster_node"."install_user_id" IS '安装用户ID'; +COMMENT ON COLUMN "ops_cluster_node"."install_path" IS '安装路径'; +COMMENT ON COLUMN "ops_cluster_node"."data_path" IS '数据目录'; +COMMENT ON COLUMN "ops_cluster_node"."pkg_path" IS '安装包目录'; +COMMENT ON COLUMN "ops_cluster_node"."install_demo_database" IS '是否安装示例数据库'; +COMMENT ON COLUMN "ops_cluster_node"."cluster_id" IS '集群ID'; +COMMENT ON COLUMN "ops_cluster_node"."is_install_cm" IS '是否安装 CM'; +COMMENT ON COLUMN "ops_cluster_node"."is_cm_master" IS '是否CM master'; +COMMENT ON COLUMN "ops_cluster_node"."cm_data_path" IS 'cm数据路径'; +COMMENT ON COLUMN "ops_cluster_node"."cm_port" IS 'cm端口'; +COMMENT ON COLUMN "ops_cluster_node"."xlog_path" IS 'xlog路径'; +COMMENT ON COLUMN "ops_cluster_node"."is_enable_dss" IS '是否开启Dss'; +COMMENT ON COLUMN "ops_cluster_node"."dss_data_lun_link_path" IS 'data卷软链接'; +COMMENT ON COLUMN "ops_cluster_node"."xlog_lun_link_path" IS 'xlog卷软链接'; +COMMENT ON COLUMN "ops_cluster_node"."cm_voting_lun_link_path" IS 'cm投票卷软链接'; +COMMENT ON COLUMN "ops_cluster_node"."cm_sharing_lun_link_path" IS 'cm共享卷软链接'; +COMMENT ON COLUMN "ops_cluster_node"."remark" IS '描述'; +COMMENT ON COLUMN "ops_cluster_node"."create_by" IS '创建者'; +COMMENT ON COLUMN "ops_cluster_node"."create_time" IS '创建时间'; +COMMENT ON COLUMN "ops_cluster_node"."update_by" IS '更新者'; +COMMENT ON COLUMN "ops_cluster_node"."update_time" IS '更新时间'; + +ALTER TABLE "ops_cluster_node" ALTER COLUMN "cluster_node_id" TYPE varchar(255); + + + +-- ---------------------------- +-- Table structure for ops_cluster_task +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "ops_cluster_task" ( + "cluster_id" varchar(255) NOT NULL PRIMARY KEY, + "host_id" varchar(255) , + "host_user_id" varchar(255) , + "os" varchar(255) , + "cpu_arch" varchar(255) , + "version" varchar(255) , + "version_num" varchar(255) , + "package_name" varchar(255) , + "package_id" varchar(255) , + "cluster_name" varchar(255) , + "database_username" varchar(255) , + "database_password" text , + "database_port" int4 DEFAULT 5432, + "install_package_path" varchar(255) , + "install_path" varchar(255) , + "log_path" varchar(255) , + "tmp_path" varchar(255) , + "om_tools_path" varchar(255) , + "core_path" varchar(255) , + "env_path" varchar(255) , + "enable_cm_tool" int4 DEFAULT 0, + "enable_dcf" int4 DEFAULT 0, + "enable_generate_environment_variable_file" int4 DEFAULT 0, + "xml_config_path" varchar(255) , + "deploy_type" varchar(255) , + "cluster_node_num" int4 DEFAULT 0, + "status" varchar(255) , + "execute_time" timestamp, + "env_check_result" varchar(255) , + "remark" text , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ) +; +COMMENT ON COLUMN "ops_cluster_task"."cluster_id" IS '集群ID'; +COMMENT ON COLUMN "ops_cluster_task"."host_id" IS '服务器IP'; +COMMENT ON COLUMN "ops_cluster_task"."host_user_id" IS '安装服务器用户ID'; +COMMENT ON COLUMN "ops_cluster_task"."os" IS '操作系统名称'; +COMMENT ON COLUMN "ops_cluster_task"."cpu_arch" IS 'CPU架构'; +COMMENT ON COLUMN "ops_cluster_task"."version" IS '数据库版本'; +COMMENT ON COLUMN "ops_cluster_task"."version_num" IS '数据库版本号'; +COMMENT ON COLUMN "ops_cluster_task"."package_name" IS '安装包名称'; +COMMENT ON COLUMN "ops_cluster_task"."package_id" IS '安装包ID'; +COMMENT ON COLUMN "ops_cluster_task"."cluster_name" IS '集群名称'; +COMMENT ON COLUMN "ops_cluster_task"."database_username" IS '数据库用户名称'; +COMMENT ON COLUMN "ops_cluster_task"."database_password" IS '数据库密码'; +COMMENT ON COLUMN "ops_cluster_task"."database_port" IS '数据库端口'; +COMMENT ON COLUMN "ops_cluster_task"."install_package_path" IS '安装包路径'; +COMMENT ON COLUMN "ops_cluster_task"."install_path" IS '安装路径'; +COMMENT ON COLUMN "ops_cluster_task"."log_path" IS 'log路径'; +COMMENT ON COLUMN "ops_cluster_task"."tmp_path" IS 'tmp路径'; +COMMENT ON COLUMN "ops_cluster_task"."om_tools_path" IS 'om工具安装路径'; +COMMENT ON COLUMN "ops_cluster_task"."core_path" IS 'core 路径'; +COMMENT ON COLUMN "ops_cluster_task"."env_path" IS '环境变量路径'; +COMMENT ON COLUMN "ops_cluster_task"."enable_cm_tool" IS '是否安装CM工具'; +COMMENT ON COLUMN "ops_cluster_task"."enable_dcf" IS '是否dcf'; +COMMENT ON COLUMN "ops_cluster_task"."enable_generate_environment_variable_file" IS '是否分离环境变量文件'; +COMMENT ON COLUMN "ops_cluster_task"."xml_config_path" IS 'xml安装配置路径'; +COMMENT ON COLUMN "ops_cluster_task"."deploy_type" IS '集群部署类型'; +COMMENT ON COLUMN "ops_cluster_task"."cluster_node_num" IS '集群节点数量'; +COMMENT ON COLUMN "ops_cluster_task"."status" IS '安装任务执行状态'; +COMMENT ON COLUMN "ops_cluster_task"."execute_time" IS '任务执行时间'; +COMMENT ON COLUMN "ops_cluster_task"."env_check_result" IS '环境检查结果'; +COMMENT ON COLUMN "ops_cluster_task"."create_by" IS '创建者'; +COMMENT ON COLUMN "ops_cluster_task"."create_time" IS '创建时间'; +COMMENT ON COLUMN "ops_cluster_task"."update_by" IS '更新者'; +COMMENT ON COLUMN "ops_cluster_task"."update_time" IS '更新时间'; +COMMENT ON TABLE "ops_cluster_task" IS 'openGauss集群安装任务表'; + + + +-- ---------------------------- +-- Table structure for ops_cluster_task_node +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "ops_cluster_task_node" ( + "cluster_node_id" varchar(255) NOT NULL PRIMARY KEY, + "cluster_id" varchar(255) , + "host_id" varchar(255) , + "host_user_id" varchar(255) , + "node_type" varchar(255) , + "data_path" varchar(255) , + "az_owner" varchar(255) , + "az_priority" varchar(255) , + "env_check_result" varchar(255) , + "env_check_detail" text , + "is_cm_master" int4 DEFAULT 0, + "cm_data_path" varchar(255) , + "cm_port" int4 DEFAULT 0, + "remark" text , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ) +; + +COMMENT ON COLUMN "ops_cluster_task_node"."cluster_node_id" IS '集群节点ID'; +COMMENT ON COLUMN "ops_cluster_task_node"."cluster_id" IS '集群ID'; +COMMENT ON COLUMN "ops_cluster_task_node"."host_id" IS '节点服务器Id'; +COMMENT ON COLUMN "ops_cluster_task_node"."host_user_id" IS '节点用户ID'; +COMMENT ON COLUMN "ops_cluster_task_node"."node_type" IS '节点类型'; +COMMENT ON COLUMN "ops_cluster_task_node"."data_path" IS '数据路径'; +COMMENT ON COLUMN "ops_cluster_task_node"."az_owner" IS '所属AZ'; +COMMENT ON COLUMN "ops_cluster_task_node"."az_priority" IS 'AZ优先级'; +COMMENT ON COLUMN "ops_cluster_task_node"."env_check_result" IS '环境检查结果'; +COMMENT ON COLUMN "ops_cluster_task_node"."env_check_detail" IS '环境检查详情'; +COMMENT ON COLUMN "ops_cluster_task_node"."is_cm_master" IS '是否CM主节点'; +COMMENT ON COLUMN "ops_cluster_task_node"."cm_data_path" IS 'CM数据路径'; +COMMENT ON COLUMN "ops_cluster_task_node"."cm_port" IS 'CM端口'; +COMMENT ON COLUMN "ops_cluster_task_node"."create_by" IS '创建者'; +COMMENT ON COLUMN "ops_cluster_task_node"."create_time" IS '创建时间'; +COMMENT ON COLUMN "ops_cluster_task_node"."update_by" IS '更新者'; +COMMENT ON COLUMN "ops_cluster_task_node"."update_time" IS '更新时间'; +COMMENT ON TABLE "ops_cluster_task_node" IS '集群安装任务节点信息'; + +-- ---------------------------- +-- Table structure for ops_encryption +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "ops_encryption" +( + "encryption_id" varchar(255) NOT NULL PRIMARY KEY, + "remark" varchar(255), + "create_by" varchar(64), + "create_time" timestamp, + "update_by" varchar(64), + "update_time" timestamp, + "key_security" varchar(64), + "encryption_key" text, + "public_key" text, + "private_key" text +); +COMMENT ON COLUMN "ops_encryption"."remark" IS '描述'; +COMMENT ON COLUMN "ops_encryption"."create_by" IS '创建者'; +COMMENT ON COLUMN "ops_encryption"."create_time" IS '创建时间'; +COMMENT ON COLUMN "ops_encryption"."update_by" IS '更新者'; +COMMENT ON COLUMN "ops_encryption"."update_time" IS '更新时间'; + +-- ---------------------------- +-- Table structure for ops_host +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "ops_host" ( + "host_id" int8 NOT NULL PRIMARY KEY, + "hostname" varchar(255) , + "private_ip" varchar(64) NOT NULL, + "public_ip" varchar(64) NOT NULL, + "port" int8 NOT NULL, + "az_id" int8, + "os_build" varchar(255) , + "cpu_model" varchar(255) , + "cpu_arch" varchar(64) , + "cpu_freq" varchar(64) , + "physical_cores" int4 , + "logical_cores" int4 , + "remark" varchar(255) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ) +; +COMMENT ON COLUMN "ops_host"."host_id" IS '主机id'; +COMMENT ON COLUMN "ops_host"."hostname" IS '主机名'; +COMMENT ON COLUMN "ops_host"."private_ip" IS '内网ip地址'; +COMMENT ON COLUMN "ops_host"."public_ip" IS '公网ip地址'; +COMMENT ON COLUMN "ops_host"."port" IS '端口'; +COMMENT ON COLUMN "ops_host"."az_id" IS 'az_id'; +COMMENT ON COLUMN "ops_host"."remark" IS '描述'; +COMMENT ON COLUMN "ops_host"."create_by" IS '创建者'; +COMMENT ON COLUMN "ops_host"."create_time" IS '创建时间'; +COMMENT ON COLUMN "ops_host"."update_by" IS '更新者'; +COMMENT ON COLUMN "ops_host"."update_time" IS '更新时间'; + +-- ---------------------------- +-- Table structure for ops_host_user +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "ops_host_user" ( + "host_user_id" int8 NOT NULL PRIMARY KEY, + "username" varchar(255) NOT NULL, + "password" text , + "host_id" int8 NOT NULL, + "remark" varchar(255) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ) +; +COMMENT ON COLUMN "ops_host_user"."host_user_id" IS '主机用户主键'; +COMMENT ON COLUMN "ops_host_user"."username" IS '用户名'; +COMMENT ON COLUMN "ops_host_user"."password" IS '密码'; +COMMENT ON COLUMN "ops_host_user"."host_id" IS '所属主机id'; +COMMENT ON COLUMN "ops_host_user"."remark" IS '备注'; +COMMENT ON COLUMN "ops_host_user"."create_by" IS '创建者'; +COMMENT ON COLUMN "ops_host_user"."create_time" IS '创建时间'; +COMMENT ON COLUMN "ops_host_user"."update_by" IS '更新者'; +COMMENT ON COLUMN "ops_host_user"."update_time" IS '更新时间'; + +CREATE TABLE IF NOT EXISTS "ops_host_tag" ( + "host_tag_id" varchar(255) NOT NULL PRIMARY KEY, + "name" varchar(255) , + "remark" varchar(255) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ) +; +COMMENT ON COLUMN "ops_host_tag"."name" IS '标签名'; +COMMENT ON COLUMN "ops_host_tag"."remark" IS '备注'; +COMMENT ON COLUMN "ops_host_tag"."create_by" IS '创建者'; +COMMENT ON COLUMN "ops_host_tag"."create_time" IS '创建时间'; +COMMENT ON COLUMN "ops_host_tag"."update_by" IS '更新者'; +COMMENT ON COLUMN "ops_host_tag"."update_time" IS '更新时间'; + +-- ---------------------------- +-- Table structure for ops_host_tag_rel +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "ops_host_tag_rel" ( + "id" varchar(255) NOT NULL PRIMARY KEY, + "host_id" varchar(255) , + "tag_id" varchar(255) , + "remark" varchar(255) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ) +; +COMMENT ON COLUMN "ops_host_tag_rel"."host_id" IS '主机ID'; +COMMENT ON COLUMN "ops_host_tag_rel"."tag_id" IS 'tag ID'; +COMMENT ON COLUMN "ops_host_tag_rel"."remark" IS '描述'; +COMMENT ON COLUMN "ops_host_tag_rel"."create_by" IS '创建者'; +COMMENT ON COLUMN "ops_host_tag_rel"."create_time" IS '创建时间'; +COMMENT ON COLUMN "ops_host_tag_rel"."update_by" IS '更新者'; +COMMENT ON COLUMN "ops_host_tag_rel"."update_time" IS '更新时间'; + + +-- ---------------------------- +-- Table structure for ops_package_path_dict +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "ops_package_path_dict" ( + "id" varchar(255) NOT NULL PRIMARY KEY, + "os" varchar(255) , + "os_version" varchar(255) , + "cpu_arch" varchar(255) , + "version" varchar(255) , + "url_path" varchar(255) , + "package_name_tmp" varchar(255) , + "pkg_tmp_use_version" varchar(255) , + "remark" varchar(255) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ) +; +COMMENT ON COLUMN "ops_package_path_dict"."os" IS '操作系统名称'; +COMMENT ON COLUMN "ops_package_path_dict"."os_version" IS '操作系统版本'; +COMMENT ON COLUMN "ops_package_path_dict"."cpu_arch" IS 'CPU架构'; +COMMENT ON COLUMN "ops_package_path_dict"."version" IS 'openGauss版本'; +COMMENT ON COLUMN "ops_package_path_dict"."url_path" IS 'openGauss包URI路径'; +COMMENT ON COLUMN "ops_package_path_dict"."package_name_tmp" IS 'openGauss包名称模版'; +COMMENT ON COLUMN "ops_package_path_dict"."pkg_tmp_use_version" IS 'openGauss模版使用版本号'; +COMMENT ON COLUMN "ops_package_path_dict"."create_by" IS '创建者'; +COMMENT ON COLUMN "ops_package_path_dict"."create_time" IS '创建时间'; +COMMENT ON COLUMN "ops_package_path_dict"."update_by" IS '更新者'; +COMMENT ON COLUMN "ops_package_path_dict"."update_time" IS '更新时间'; +COMMENT ON TABLE "ops_package_path_dict" IS 'openGauss包名称字典表'; + + + +UPDATE ops_package_path_dict SET os_version = '7' WHERE (os_version IS NULL OR os_version = '') AND os='CentOS'; +UPDATE ops_package_path_dict SET os_version = '20.03' WHERE (os_version IS NULL OR os_version = '') AND os = 'openEuler' AND url_path NOT LIKE '%2203%'; +UPDATE ops_package_path_dict SET os_version = '22.03', os = 'openEuler' WHERE (os_version IS NULL OR os_version = '') AND os LIKE '%%openEuler%%' AND url_path LIKE '%2203%' ; + + +UPDATE ops_package_path_dict SET pkg_tmp_use_version = '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1' WHERE (pkg_tmp_use_version IS NULL OR pkg_tmp_use_version = '') ; + + +ALTER TABLE ops_host_user ADD COLUMN sudo int2 DEFAULT 0; +COMMENT ON COLUMN "ops_host_user"."sudo" IS '管理员'; + +-- ---------------------------- +-- Table structure for sys_log_config +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "sys_log_config" ( + "key" varchar(100) NOT NULL, + "value" varchar(500) NOT NULL, + "id" int4 NOT NULL PRIMARY KEY + ) +; + +-- ---------------------------- +-- Table structure for sys_menu +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "sys_menu" ( + "menu_id" int8 NOT NULL PRIMARY KEY AUTOINCREMENT, + "menu_name" varchar(50) NOT NULL, + "parent_id" int8, + "order_num" int4, + "path" varchar(200) , + "component" varchar(255) , + "query" varchar(255) , + "is_frame" int4 DEFAULT 1, + "is_cache" int4 DEFAULT 0, + "menu_type" char(1) , + "visible" char(1) DEFAULT 0, + "status" char(1) DEFAULT 0, + "perms" varchar(100) , + "icon" varchar(100) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp, + "remark" varchar(500) , + "open_way" int4 DEFAULT 1, + "plugin_id" varchar(100) , + "open_position" int2 DEFAULT 1, + "query_template" varchar(100) , + "plugin_theme" varchar(50) , + "menu_classify" int2 DEFAULT 1, + "menu_en_name" varchar(50) + ) +; +COMMENT ON COLUMN "sys_menu"."menu_id" IS '菜单ID'; +COMMENT ON COLUMN "sys_menu"."menu_name" IS '菜单名称'; +COMMENT ON COLUMN "sys_menu"."parent_id" IS '父菜单ID'; +COMMENT ON COLUMN "sys_menu"."order_num" IS '显示顺序'; +COMMENT ON COLUMN "sys_menu"."path" IS '路由地址'; +COMMENT ON COLUMN "sys_menu"."component" IS '组件路径'; +COMMENT ON COLUMN "sys_menu"."query" IS '路由参数'; +COMMENT ON COLUMN "sys_menu"."is_frame" IS '是否为外链(0是 1否)'; +COMMENT ON COLUMN "sys_menu"."is_cache" IS '是否缓存(0缓存 1不缓存)'; +COMMENT ON COLUMN "sys_menu"."menu_type" IS '菜单类型(M目录 C菜单 F按钮)'; +COMMENT ON COLUMN "sys_menu"."visible" IS '菜单状态(0显示 1隐藏)'; +COMMENT ON COLUMN "sys_menu"."status" IS '菜单状态(0正常 1停用)'; +COMMENT ON COLUMN "sys_menu"."perms" IS '权限标识'; +COMMENT ON COLUMN "sys_menu"."icon" IS '菜单图标'; +COMMENT ON COLUMN "sys_menu"."create_by" IS '创建者'; +COMMENT ON COLUMN "sys_menu"."create_time" IS '创建时间'; +COMMENT ON COLUMN "sys_menu"."update_by" IS '更新者'; +COMMENT ON COLUMN "sys_menu"."update_time" IS '更新时间'; +COMMENT ON COLUMN "sys_menu"."remark" IS '备注'; +COMMENT ON COLUMN "sys_menu"."open_way" IS '打开方式;1:页面打开;2:弹窗打开'; +COMMENT ON COLUMN "sys_menu"."plugin_id" IS '所属插件ID'; +COMMENT ON COLUMN "sys_menu"."open_position" IS '打开位置;1:左边菜单;2:首页实例'; +COMMENT ON COLUMN "sys_menu"."query_template" IS 'query参数模板'; +COMMENT ON COLUMN "sys_menu"."plugin_theme" IS '插件主题'; +COMMENT ON COLUMN "sys_menu"."menu_classify" IS '菜单分类;1:业务菜单;2:平台菜单'; +COMMENT ON COLUMN "sys_menu"."menu_en_name" IS '英文名称'; +COMMENT ON TABLE "sys_menu" IS '菜单权限表'; + +-- ---------------------------- +-- Table structure for sys_oper_log +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "sys_oper_log" ( + "oper_id" int8 NOT NULL PRIMARY KEY AUTOINCREMENT, + "title" varchar(50), + "business_type" int4, + "method" varchar(100) , + "request_method" varchar(10) , + "operator_type" int4, + "oper_name" varchar(50) , + "oper_url" varchar(255) , + "oper_ip" varchar(128) , + "oper_location" varchar(255) , + "oper_param" varchar(2000) , + "json_result" varchar(2000) , + "status" int4, + "error_msg" varchar(2000) , + "oper_time" timestamp + ) +; +COMMENT ON COLUMN "sys_oper_log"."oper_id" IS '日志主键'; +COMMENT ON COLUMN "sys_oper_log"."title" IS '模块标题'; +COMMENT ON COLUMN "sys_oper_log"."business_type" IS '业务类型(0其它 1新增 2修改 3删除)'; +COMMENT ON COLUMN "sys_oper_log"."method" IS '方法名称'; +COMMENT ON COLUMN "sys_oper_log"."request_method" IS '请求方式'; +COMMENT ON COLUMN "sys_oper_log"."operator_type" IS '操作类别(0其它 1后台用户 2手机端用户)'; +COMMENT ON COLUMN "sys_oper_log"."oper_name" IS '操作人员'; +COMMENT ON COLUMN "sys_oper_log"."oper_url" IS '请求URL'; +COMMENT ON COLUMN "sys_oper_log"."oper_ip" IS '主机地址'; +COMMENT ON COLUMN "sys_oper_log"."oper_location" IS '操作地点'; +COMMENT ON COLUMN "sys_oper_log"."oper_param" IS '请求参数'; +COMMENT ON COLUMN "sys_oper_log"."json_result" IS '返回参数'; +COMMENT ON COLUMN "sys_oper_log"."status" IS '操作状态(0正常 1异常)'; +COMMENT ON COLUMN "sys_oper_log"."error_msg" IS '错误消息'; +COMMENT ON COLUMN "sys_oper_log"."oper_time" IS '操作时间'; +COMMENT ON TABLE "sys_oper_log" IS '操作日志记录'; + +-- ---------------------------- +-- Table structure for sys_plugin_config +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "sys_plugin_config" ( + "id" int8 NOT NULL PRIMARY KEY AUTOINCREMENT, + "plugin_id" varchar(100) , + "config_json" varchar(1000) + ) +; +COMMENT ON COLUMN "sys_plugin_config"."id" IS '主键ID'; +COMMENT ON COLUMN "sys_plugin_config"."plugin_id" IS '插件ID'; +COMMENT ON COLUMN "sys_plugin_config"."config_json" IS '配置参数'; +COMMENT ON TABLE "sys_plugin_config" IS '插件配置结构表'; + +-- ---------------------------- +-- Table structure for sys_plugin_config_data +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "sys_plugin_config_data" ( + "id" int8 NOT NULL PRIMARY KEY AUTOINCREMENT, + "plugin_id" varchar(100) , + "config_data" varchar(1000) + ) +; +COMMENT ON COLUMN "sys_plugin_config_data"."id" IS '主键ID'; +COMMENT ON COLUMN "sys_plugin_config_data"."plugin_id" IS '插件ID'; +COMMENT ON COLUMN "sys_plugin_config_data"."config_data" IS '配置数据'; +COMMENT ON TABLE "sys_plugin_config_data" IS '插件配置数据'; + +-- ---------------------------- +-- Table structure for sys_plugins +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "sys_plugins" ( + "id" int4 NOT NULL PRIMARY KEY AUTOINCREMENT, + "plugin_id" varchar(100) , + "bootstrap_class" varchar(255) , + "plugin_desc" text , + "logo_path" varchar(255) , + "plugin_type" int4, + "plugin_version" varchar(50) , + "plugin_provider" varchar(50) , + "plugin_status" int2 DEFAULT 1, + "is_need_configured" int2, + "theme" varchar(50) + ) +; +COMMENT ON COLUMN "sys_plugins"."id" IS '主键ID'; +COMMENT ON COLUMN "sys_plugins"."plugin_id" IS '插件ID'; +COMMENT ON COLUMN "sys_plugins"."bootstrap_class" IS '启动类'; +COMMENT ON COLUMN "sys_plugins"."plugin_desc" IS '插件描述'; +COMMENT ON COLUMN "sys_plugins"."logo_path" IS '插件Logo路径'; +COMMENT ON COLUMN "sys_plugins"."plugin_type" IS '插件类别'; +COMMENT ON COLUMN "sys_plugins"."plugin_version" IS '插件版本'; +COMMENT ON COLUMN "sys_plugins"."plugin_provider" IS '插件提供者'; +COMMENT ON COLUMN "sys_plugins"."plugin_status" IS '插件状态:1:启动;2:停用'; +COMMENT ON COLUMN "sys_plugins"."is_need_configured" IS '是否需要配置数据'; +COMMENT ON COLUMN "sys_plugins"."theme" IS '主题'; + +-- ---------------------------- +-- Table structure for sys_plugins_repository +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS sys_plugins_repository ( + "id" int8 NOT NULL PRIMARY KEY AUTOINCREMENT, + "plugin_id" varchar(255), + "is_downloaded" int8 DEFAULT 0, + "plugin_version" varchar(50), + "download_url" varchar(255), + "plugin_desc" text, + "plugin_desc_en" text +) +; +COMMENT ON COLUMN "sys_plugins_repository"."id" IS '主键ID'; +COMMENT ON COLUMN "sys_plugins_repository"."plugin_id" IS '插件ID'; +COMMENT ON COLUMN "sys_plugins_repository"."is_downloaded" IS '插件是否已下载:0:未下载;1:已下载'; +COMMENT ON COLUMN "sys_plugins_repository"."plugin_version" IS '插件版本'; +COMMENT ON COLUMN "sys_plugins_repository"."download_url" IS '插件下载地址'; +COMMENT ON COLUMN "sys_plugins_repository"."plugin_desc" IS '插件描述'; +COMMENT ON COLUMN "sys_plugins_repository"."plugin_desc_en" IS '插件英文描述'; + +--pluginsURL-(7.0.0-RC2) +INSERT INTO sys_plugins_repository (id, plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES (1, 'alert-monitor', 0, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/alert-monitor-7.0.0-RC2-repackage.jar', '告警插件', 'Alarm Plugin'); + +INSERT INTO sys_plugins_repository (id, plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES (2, 'webds-plugin', 1, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/webds-plugin-7.0.0-RC2-repackage.jar', '业务开发', 'Data Studio Plugin'); + +INSERT INTO sys_plugins_repository (id, plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES (4, 'observability-log-search', 0, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/observability-log-search-7.0.0-RC2-repackage.jar', '日志检索插件', 'Log Retrieval Plugin'); + +INSERT INTO sys_plugins_repository (id, plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES (6, 'observability-instance', 0, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/observability-instance-7.0.0-RC2-repackage.jar', '实例监控插件', 'Instance Monitoring Plugin'); + +INSERT INTO sys_plugins_repository (id, plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES (7, 'data-migration', 0, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/data-migration-7.0.0-RC2-repackage.jar', '数据迁移插件', 'Data Migration Plugin'); + +INSERT INTO sys_plugins_repository (id, plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES (8, 'base-ops', 1, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/base-ops-7.0.0-RC2-repackage.jar', '基础运维插件', 'Basic Operation Plugin'); + +INSERT INTO sys_plugins_repository (id, plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES (9, 'observability-sql-diagnosis', 0, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/observability-sql-diagnosis-7.0.0-RC2-repackage.jar', '智能诊断插件', 'Intelligent Diagnosis Plugin'); + +INSERT INTO sys_plugins_repository (id, plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES (11, 'compatibility-assessment', 0, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/compatibility-assessment-7.0.0-RC2-repackage.jar', 'openGauss兼容性评估工具', 'The openGauss compatibility evaluation tool'); + +-- ---------------------------- +-- Table structure for sys_role +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "sys_role" ( + "role_id" int8 NOT NULL PRIMARY KEY AUTOINCREMENT, + "role_name" varchar(30) NOT NULL, + "role_key" varchar(100) , + "role_sort" int4, + "data_scope" char(1) , + "menu_check_strictly" int2 DEFAULT 1, + "dept_check_strictly" int2, + "status" char(1) NOT NULL DEFAULT 0, + "del_flag" char(1) DEFAULT 0, + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp, + "remark" varchar(500) + ) +; +COMMENT ON COLUMN "sys_role"."role_id" IS '角色ID'; +COMMENT ON COLUMN "sys_role"."role_name" IS '角色名称'; +COMMENT ON COLUMN "sys_role"."role_key" IS '角色权限字符串'; +COMMENT ON COLUMN "sys_role"."role_sort" IS '显示顺序'; +COMMENT ON COLUMN "sys_role"."data_scope" IS '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)'; +COMMENT ON COLUMN "sys_role"."menu_check_strictly" IS '菜单树选择项是否关联显示'; +COMMENT ON COLUMN "sys_role"."dept_check_strictly" IS '医院树选择项是否关联显示'; +COMMENT ON COLUMN "sys_role"."status" IS '角色状态(0正常 1停用)'; +COMMENT ON COLUMN "sys_role"."del_flag" IS '删除标志(0代表存在 2代表删除)'; +COMMENT ON COLUMN "sys_role"."create_by" IS '创建者'; +COMMENT ON COLUMN "sys_role"."create_time" IS '创建时间'; +COMMENT ON COLUMN "sys_role"."update_by" IS '更新者'; +COMMENT ON COLUMN "sys_role"."update_time" IS '更新时间'; +COMMENT ON COLUMN "sys_role"."remark" IS '备注'; +COMMENT ON TABLE "sys_role" IS '角色信息表'; + +-- ---------------------------- +-- Table structure for sys_role_menu +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "sys_role_menu" ( + "role_id" int8 NOT NULL, + "menu_id" int8 NOT NULL +) +; +COMMENT ON COLUMN "sys_role_menu"."role_id" IS '角色ID'; +COMMENT ON COLUMN "sys_role_menu"."menu_id" IS '菜单ID'; +COMMENT ON TABLE "sys_role_menu" IS '角色和菜单关联表'; + +-- ---------------------------- +-- Table structure for sys_user +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "sys_user" ( + "user_id" int8 NOT NULL PRIMARY KEY AUTOINCREMENT, + "user_name" varchar(30) NOT NULL, + "nick_name" varchar(30) NOT NULL, + "user_type" varchar(2) , + "email" varchar(50) , + "phonenumber" varchar(11) , + "sex" char(1) , + "avatar" varchar(100) , + "password" varchar(100) , + "status" char(1) , + "del_flag" char(1) DEFAULT 0, + "login_ip" varchar(128) , + "login_date" timestamp, + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp, + "remark" varchar(500) + ) +; +COMMENT ON COLUMN "sys_user"."user_id" IS '用户ID'; +COMMENT ON COLUMN "sys_user"."user_name" IS '用户账号'; +COMMENT ON COLUMN "sys_user"."nick_name" IS '用户昵称'; +COMMENT ON COLUMN "sys_user"."user_type" IS '用户类型(00系统用户)'; +COMMENT ON COLUMN "sys_user"."email" IS '用户邮箱'; +COMMENT ON COLUMN "sys_user"."phonenumber" IS '手机号码'; +COMMENT ON COLUMN "sys_user"."sex" IS '用户性别(0男 1女 2未知)'; +COMMENT ON COLUMN "sys_user"."avatar" IS '头像地址'; +COMMENT ON COLUMN "sys_user"."password" IS '密码'; +COMMENT ON COLUMN "sys_user"."status" IS '帐号状态(0正常 1停用)'; +COMMENT ON COLUMN "sys_user"."del_flag" IS '删除标志(0代表存在 2代表删除)'; +COMMENT ON COLUMN "sys_user"."login_ip" IS '最后登录IP'; +COMMENT ON COLUMN "sys_user"."login_date" IS '最后登录时间'; +COMMENT ON COLUMN "sys_user"."create_by" IS '创建者'; +COMMENT ON COLUMN "sys_user"."create_time" IS '创建时间'; +COMMENT ON COLUMN "sys_user"."update_by" IS '更新者'; +COMMENT ON COLUMN "sys_user"."update_time" IS '更新时间'; +COMMENT ON COLUMN "sys_user"."remark" IS '备注'; +COMMENT ON TABLE "sys_user" IS '用户信息表'; + +-- ---------------------------- +-- Table structure for sys_user_role +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "sys_user_role" ( + "user_id" int8 NOT NULL PRIMARY KEY, + "role_id" int8 NOT NULL +) +; +COMMENT ON COLUMN "sys_user_role"."user_id" IS '用户ID'; +COMMENT ON COLUMN "sys_user_role"."role_id" IS '角色ID'; +COMMENT ON TABLE "sys_user_role" IS '用户和角色关联表'; + +-- ---------------------------- +-- Table structure for sys_white_list +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "sys_white_list" ( + "id" int8 NOT NULL PRIMARY KEY AUTOINCREMENT, + "title" varchar(255) , + "ip_list" varchar(800) , + "create_time" timestamp + ) +; +COMMENT +ON COLUMN "sys_white_list"."id" IS '主键ID'; +COMMENT +ON COLUMN "sys_white_list"."title" IS '白名单名称'; +COMMENT +ON COLUMN "sys_white_list"."ip_list" IS 'ip列表,逗号隔开'; +COMMENT +ON COLUMN "sys_white_list"."create_time" IS '创建时间'; + + +CREATE TABLE IF NOT EXISTS "ops_package_manager"( + "package_id" varchar(255) NOT NULL PRIMARY KEY, + "os" varchar(255) , + "cpu_arch" varchar(255) , + "package_version" varchar(255) , + "package_version_num" varchar(255) , + "package_url" varchar(1024) , + "remark" varchar(255) , + "source" varchar(255) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ); + + +-- ---------------------------- +-- Table structure for ops_cluster_operate_log +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "ops_cluster_operate_log" ( + "operate_id" varchar(255) NOT NULL PRIMARY KEY, + "cluster_id" varchar(255) , + "cluster_node_id" varchar(255) , + "operate_type" varchar(255) , + "operate_log" text , + "operate_time" timestamp, + "remark" varchar(255) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ) +; + +COMMENT ON COLUMN "ops_cluster_operate_log"."operate_id" IS '操作记录Id'; +COMMENT ON COLUMN "ops_cluster_operate_log"."cluster_id" IS '集群ID'; +COMMENT ON COLUMN "ops_cluster_operate_log"."cluster_node_id" IS '集群节点ID'; +COMMENT ON COLUMN "ops_cluster_operate_log"."operate_type" IS '操作类型'; +COMMENT ON COLUMN "ops_cluster_operate_log"."operate_log" IS '操作记录日志'; +COMMENT ON COLUMN "ops_cluster_operate_log"."operate_time" IS '操作时间'; +COMMENT ON COLUMN "ops_cluster_operate_log"."create_by" IS '创建者'; +COMMENT ON COLUMN "ops_cluster_operate_log"."create_time" IS '创建时间'; +COMMENT ON COLUMN "ops_cluster_operate_log"."update_by" IS '更新者'; +COMMENT ON COLUMN "ops_cluster_operate_log"."update_time" IS '更新时间'; +COMMENT ON TABLE "ops_cluster_operate_log" IS '集群安装任务操作日志'; + +CREATE TABLE IF NOT EXISTS "sys_task" ( + "id" int8 NOT NULL AUTOINCREMENT, + "task_name" varchar(255) , + "task_type" int4, + "exec_status" int4, + "exec_params" varchar(512) , + "exec_progress" float4, + "exec_host_id" int8, + "create_time" timestamp, + "finish_time" timestamp, + "exec_time" timestamp, + "plugin_id" varchar(100) , + "sub_task_count" int8, + CONSTRAINT "sys_task_pkey" PRIMARY KEY ("id") + ); + +COMMENT ON COLUMN "sys_task"."id" IS '主键ID'; + +COMMENT ON COLUMN "sys_task"."task_name" IS '任务名称'; + +COMMENT ON COLUMN "sys_task"."task_type" IS '操作类别(1数据迁移)'; + +COMMENT ON COLUMN "sys_task"."exec_status" IS '执行状态(0:未执行;1:执行中;2:已完成;3:执行失败)'; + +COMMENT ON COLUMN "sys_task"."exec_params" IS '执行参数'; + +COMMENT ON COLUMN "sys_task"."exec_progress" IS '执行进度'; + +COMMENT ON COLUMN "sys_task"."exec_host_id" IS '执行物理机ID'; + +COMMENT ON COLUMN "sys_task"."create_time" IS '创建时间'; + +COMMENT ON COLUMN "sys_task"."finish_time" IS '完成时间'; + +COMMENT ON COLUMN "sys_task"."exec_time" IS '执行时间'; + +COMMENT ON COLUMN "sys_task"."plugin_id" IS '插件ID'; + +COMMENT ON COLUMN "sys_task"."sub_task_count" IS '子任务数量'; + +COMMENT ON TABLE "sys_task" IS '平台任务表'; + +ALTER TABLE ops_package_manager ADD COLUMN name VARCHAR(255); + +ALTER TABLE ops_package_manager ADD COLUMN "package_path" text; +ALTER TABLE ops_package_manager ADD COLUMN "type" varchar(255); +UPDATE ops_package_manager set type = 'openGauss'; + +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604735048472268811', 'centos', 'x86_64', 'MINIMAL_LIST', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/x86/openGauss-3.1.1-CentOS-64bit.tar.bz2', NULL, + 'admin', '2022-12-19 15:07:04.749', 'admin', '2022-12-19 15:07:04.749', 'openGauss', 'offical_3.1.1_mini_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1605498723119550126', 'centos', 'x86_64', 'ENTERPRISE', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/x86/openGauss-3.1.1-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2022-12-21 17:41:38.992', 'admin', '2022-12-21 17:41:38.992', 'openGauss', 'offical_3.1.1_enterprise_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1605498799611072134', 'centos', 'x86_64', 'LITE', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/x86/openGauss-Lite-3.1.1-CentOS-x86_64.tar.gz', NULL, + 'admin', '2022-12-21 17:41:57.21', 'admin', '2022-12-21 17:41:57.21', 'openGauss', 'offical_3.1.1_lite_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604855665214541845', 'openEuler', 'aarch64', 'ENTERPRISE', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/arm/openGauss-3.1.1-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2022-12-19 23:06:22.02', 'admin', '2022-12-19 23:06:22.02', 'openGauss', 'offical_3.1.1_enterprise_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604857993288142150', 'openEuler', 'aarch64', 'LITE', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/arm/openGauss-Lite-3.1.1-openEuler-aarch64.tar.gz', + NULL, 'admin', '2022-12-19 23:15:37.077', 'admin', '2022-12-19 23:15:37.077', 'openGauss', 'offical_3.1.1_lite_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627277907599162', 'openEuler', 'aarch64', 'MINIMAL_LIST', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/arm/openGauss-3.1.1-openEuler-64bit.tar.bz2', NULL, + 'admin', '2022-12-24 20:26:07.402', 'admin', '2022-12-24 20:26:07.402', 'openGauss', 'offical_3.1.1_mini_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627421516374177', 'openEuler', 'x86_64', 'ENTERPRISE', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/x86_openEuler/openGauss-3.1.1-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2022-12-24 20:26:41.641', 'admin', '2022-12-24 20:26:41.641', 'openGauss', 'offical_3.1.1_enterprise_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627515523309180', 'openEuler', 'x86_64', 'MINIMAL_LIST', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/x86_openEuler/openGauss-3.1.1-openEuler-64bit.tar.bz2', + NULL, 'admin', '2022-12-24 20:27:04.054', 'admin', '2022-12-24 20:27:04.054', 'openGauss', 'offical_3.1.1_mini_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627585895342192', 'openEuler', 'x86_64', 'LITE', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/x86_openEuler/openGauss-Lite-3.1.1-openEuler-x86_64.tar.gz', + NULL, 'admin', '2022-12-24 20:27:20.832', 'admin', '2022-12-24 20:27:20.832', 'openGauss', 'offical_3.1.1_lite_openEuler2003_x86_64') ; + +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1605398723119550410', 'centos', 'x86_64', 'ENTERPRISE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86/openGauss-5.0.0-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2022-12-21 17:41:38.992', 'admin', '2022-12-21 17:41:38.992', 'openGauss', 'offical_5.0.0_enterpirse_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1605398799611072511', 'centos', 'x86_64', 'LITE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86/openGauss-Lite-5.0.0-CentOS-x86_64.tar.gz', NULL, + 'admin', '2022-12-21 17:41:57.21', 'admin', '2022-12-21 17:41:57.21', 'openGauss', 'offical_5.0.0_lite_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604335048472268012', 'centos', 'x86_64', 'MINIMAL_LIST', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86/openGauss-5.0.0-CentOS-64bit.tar.bz2', NULL, + 'admin', '2022-12-19 15:07:04.749', 'admin', '2022-12-19 15:07:04.749', 'openGauss', 'offical_5.0.0_mini_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627421516374013', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler/openGauss-5.0.0-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2022-12-24 20:26:41.641', 'admin', '2022-12-24 20:26:41.641', 'openGauss', 'offical_5.0.0_enterprise_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627515523309514', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler/openGauss-5.0.0-openEuler-64bit.tar.bz2', + NULL, 'admin', '2022-12-24 20:27:04.054', 'admin', '2022-12-24 20:27:04.054', 'openGauss', 'offical_5.0.0_mini_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627585895342015', 'openEuler', 'x86_64', 'LITE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler/openGauss-Lite-5.0.0-openEuler-x86_64.tar.gz', + NULL, 'admin', '2022-12-24 20:27:20.832', 'admin', '2022-12-24 20:27:20.832', 'openGauss', 'offical_5.0.0_lite_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604855665214541816', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/arm/openGauss-5.0.0-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2022-12-19 23:06:22.02', 'admin', '2022-12-19 23:06:22.02', 'openGauss', 'offical_5.0.0_enterprise_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604857993288142817', 'openEuler', 'aarch64', 'LITE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/arm/openGauss-Lite-5.0.0-openEuler-aarch64.tar.gz', + NULL, 'admin', '2022-12-19 23:15:37.077', 'admin', '2022-12-19 23:15:37.077', 'openGauss', 'offical_5.0.0_lite_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627277907599318', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/arm/openGauss-5.0.0-openEuler-64bit.tar.bz2', NULL, + 'admin', '2022-12-24 20:26:07.402', 'admin', '2022-12-24 20:26:07.402', 'openGauss', 'offical_5.0.0_mini_openEuler2003_aarch64') ; + +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627421516374019', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler_2203/openGauss-5.0.0-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2022-12-24 20:26:41.641', 'admin', '2022-12-24 20:26:41.641', 'openGauss', 'offical_5.0.0_enterprise_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627515523309520', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler_2203/openGauss-5.0.0-openEuler-64bit.tar.bz2', + NULL, 'admin', '2022-12-24 20:27:04.054', 'admin', '2022-12-24 20:27:04.054', 'openGauss', 'offical_5.0.0_mini_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627585895342021', 'openEuler', 'x86_64', 'LITE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler_2203/openGauss-Lite-5.0.0-openEuler-x86_64.tar.gz', + NULL, 'admin', '2022-12-24 20:27:20.832', 'admin', '2022-12-24 20:27:20.832', 'openGauss', 'offical_5.0.0_lite_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604855665214541822', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/arm_2203/openGauss-5.0.0-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2022-12-19 23:06:22.02', 'admin', '2022-12-19 23:06:22.02', 'openGauss', 'offical_5.0.0_enterprise_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604857993288142823', 'openEuler', 'aarch64', 'LITE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/arm_2203/openGauss-Lite-5.0.0-openEuler-aarch64.tar.gz', + NULL, 'admin', '2022-12-19 23:15:37.077', 'admin', '2022-12-19 23:15:37.077', 'openGauss', 'offical_5.0.0_lite_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627277907599324', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/arm_2203/openGauss-5.0.0-openEuler-64bit.tar.bz2', NULL, + 'admin', '2022-12-24 20:26:07.402', 'admin', '2022-12-24 20:26:07.402', 'openGauss', 'offical_5.0.0_mini_openEuler2203_aarch64') ; + +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813634', 'centos', 'x86_64', 'ENTERPRISE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86/openGauss-5.1.0-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2023-09-25 17:41:38.992', 'admin', '2023-09-25 17:41:38.992', 'openGauss', 'offical_5.1.0_enterpirse_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813635', 'centos', 'x86_64', 'LITE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86/openGauss-Lite-5.1.0-CentOS-x86_64.tar.gz', NULL, + 'admin', '2023-09-25 17:41:57.21', 'admin', '2023-09-25 17:41:57.21', 'openGauss', 'offical_5.1.0_lite_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813636', 'centos', 'x86_64', 'MINIMAL_LIST', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86/openGauss-5.1.0-CentOS-64bit.tar.bz2', NULL, + 'admin', '2023-09-25 15:07:04.749', 'admin', '2023-09-25 15:07:04.749', 'openGauss', 'offical_5.1.0_mini_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813637', 'openEuler', 'x86_64', 'ENTERPRISE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86_openEuler/openGauss-5.1.0-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2023-09-25 20:26:41.641', 'admin', '2023-09-25 20:26:41.641', 'openGauss', 'offical_5.1.0_enterprise_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813638', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86_openEuler/openGauss-5.1.0-openEuler-64bit.tar.bz2', + NULL, 'admin', '2023-09-25 20:27:04.054', 'admin', '2023-09-25 20:27:04.054', 'openGauss', 'offical_5.1.0_mini_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813639', 'openEuler', 'x86_64', 'LITE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86_openEuler/openGauss-Lite-5.1.0-openEuler-x86_64.tar.gz', + NULL, 'admin', '2023-09-25 20:27:20.832', 'admin', '2023-09-25 20:27:20.832', 'openGauss', 'offical_5.1.0_lite_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813640', 'openEuler', 'aarch64', 'ENTERPRISE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/arm/openGauss-5.1.0-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2023-09-25 23:06:22.02', 'admin', '2023-09-25 23:06:22.02', 'openGauss', 'offical_5.1.0_enterprise_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813641', 'openEuler', 'aarch64', 'LITE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/arm/openGauss-Lite-5.1.0-openEuler-aarch64.tar.gz', + NULL, 'admin', '2023-09-25 23:15:37.077', 'admin', '2023-09-25 23:15:37.077', 'openGauss', 'offical_5.1.0_lite_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813642', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/arm/openGauss-5.1.0-openEuler-64bit.tar.bz2', NULL, + 'admin', '2023-09-25 20:26:07.402', 'admin', '2023-09-25 20:26:07.402', 'openGauss', 'offical_5.1.0_mini_openEuler2003_aarch64') ; + +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813643', 'openEuler', 'x86_64', 'ENTERPRISE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86_openEuler_2203/openGauss-5.1.0-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2023-09-25 20:26:41.641', 'admin', '2023-09-25 20:26:41.641', 'openGauss', 'offical_5.1.0_enterprise_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813644', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86_openEuler_2203/openGauss-5.1.0-openEuler-64bit.tar.bz2', + NULL, 'admin', '2023-09-25 20:27:04.054', 'admin', '2023-09-25 20:27:04.054', 'openGauss', 'offical_5.1.0_mini_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813645', 'openEuler', 'x86_64', 'LITE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86_openEuler_2203/openGauss-Lite-5.1.0-openEuler-x86_64.tar.gz', + NULL, 'admin', '2023-09-25 20:27:20.832', 'admin', '2023-09-25 20:27:20.832', 'openGauss', 'offical_5.1.0_lite_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813646', 'openEuler', 'aarch64', 'ENTERPRISE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/arm_2203/openGauss-5.1.0-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2023-09-25 23:06:22.02', 'admin', '2023-09-25 23:06:22.02', 'openGauss', 'offical_5.1.0_enterprise_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813647', 'openEuler', 'aarch64', 'LITE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/arm_2203/openGauss-Lite-5.1.0-openEuler-aarch64.tar.gz', + NULL, 'admin', '2023-09-25 23:15:37.077', 'admin', '2023-09-25 23:15:37.077', 'openGauss', 'offical_5.1.0_lite_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813648', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/arm_2203/openGauss-5.1.0-openEuler-64bit.tar.bz2', NULL, + 'admin', '2023-09-25 20:26:07.402', 'admin', '2023-09-25 20:26:07.402', 'openGauss', 'offical_5.1.0_mini_openEuler2203_aarch64') ; + +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813664', 'centos', 'x86_64', 'ENTERPRISE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86/openGauss-6.0.0-RC1-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2024-04-15 17:41:38.992', 'admin', '2024-04-15 17:41:38.992', 'openGauss', 'offical_6.0.0-RC1_enterpirse_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813665', 'centos', 'x86_64', 'LITE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86/openGauss-Lite-6.0.0-RC1-CentOS-x86_64.tar.gz', NULL, + 'admin', '2024-04-15 17:41:57.21', 'admin', '2024-04-15 17:41:57.21', 'openGauss', 'offical_6.0.0-RC1_lite_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813666', 'centos', 'x86_64', 'MINIMAL_LIST', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86/openGauss-6.0.0-RC1-CentOS-64bit.tar.bz2', NULL, + 'admin', '2024-04-15 15:07:04.749', 'admin', '2024-04-15 15:07:04.749', 'openGauss', 'offical_6.0.0-RC1_mini_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813667', 'openEuler', 'x86_64', 'ENTERPRISE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86_openEuler/openGauss-6.0.0-RC1-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-04-15 20:26:41.641', 'admin', '2024-04-15 20:26:41.641', 'openGauss', 'offical_6.0.0-RC1_enterprise_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813668', 'openEuler', 'x86_64', 'MINIMAL_LIST', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86_openEuler/openGauss-6.0.0-RC1-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-04-15 20:27:04.054', 'admin', '2024-04-15 20:27:04.054', 'openGauss', 'offical_6.0.0-RC1_mini_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813669', 'openEuler', 'x86_64', 'LITE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86_openEuler/openGauss-Lite-6.0.0-RC1-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-04-15 20:27:20.832', 'admin', '2024-04-15 20:27:20.832', 'openGauss', 'offical_6.0.0-RC1_lite_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813670', 'openEuler', 'aarch64', 'ENTERPRISE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/arm/openGauss-6.0.0-RC1-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-04-15 23:06:22.02', 'admin', '2024-04-15 23:06:22.02', 'openGauss', 'offical_6.0.0-RC1_enterprise_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813671', 'openEuler', 'aarch64', 'LITE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/arm/openGauss-Lite-6.0.0-RC1-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-04-15 23:15:37.077', 'admin', '2024-04-15 23:15:37.077', 'openGauss', 'offical_6.0.0-RC1_lite_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813672', 'openEuler', 'aarch64', 'MINIMAL_LIST', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/arm/openGauss-6.0.0-RC1-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-04-15 20:26:07.402', 'admin', '2024-04-15 20:26:07.402', 'openGauss', 'offical_6.0.0-RC1_mini_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813673', 'openEuler', 'x86_64', 'ENTERPRISE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86_openEuler_2203/openGauss-6.0.0-RC1-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-04-15 20:26:41.641', 'admin', '2024-04-15 20:26:41.641', 'openGauss', 'offical_6.0.0-RC1_enterprise_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813674', 'openEuler', 'x86_64', 'MINIMAL_LIST', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86_openEuler_2203/openGauss-6.0.0-RC1-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-04-15 20:27:04.054', 'admin', '2024-04-15 20:27:04.054', 'openGauss', 'offical_6.0.0-RC1_mini_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813675', 'openEuler', 'x86_64', 'LITE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86_openEuler_2203/openGauss-Lite-6.0.0-RC1-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-04-15 20:27:20.832', 'admin', '2024-04-15 20:27:20.832', 'openGauss', 'offical_6.0.0-RC1_lite_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813676', 'openEuler', 'aarch64', 'ENTERPRISE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/arm_2203/openGauss-6.0.0-RC1-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-04-15 23:06:22.02', 'admin', '2024-04-15 23:06:22.02', 'openGauss', 'offical_6.0.0-RC1_enterprise_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813677', 'openEuler', 'aarch64', 'LITE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/arm_2203/openGauss-Lite-6.0.0-RC1-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-04-15 23:15:37.077', 'admin', '2024-04-15 23:15:37.077', 'openGauss', 'offical_6.0.0-RC1_lite_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813678', 'openEuler', 'aarch64', 'MINIMAL_LIST', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/arm_2203/openGauss-6.0.0-RC1-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-04-15 20:26:07.402', 'admin', '2024-04-15 20:26:07.402', 'openGauss', 'offical_6.0.0-RC1_mini_openEuler2203_aarch64') ; + +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813679', 'centos', 'x86_64', 'ENTERPRISE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86/openGauss-5.0.1-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 17:41:38.992', 'admin', '2024-08-29 17:41:38.992', 'openGauss', 'offical_5.0.1_enterpirse_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813680', 'centos', 'x86_64', 'LITE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86/openGauss-Lite-5.0.1-CentOS-x86_64.tar.gz', NULL, + 'admin', '2024-08-29 17:41:57.21', 'admin', '2024-08-29 17:41:57.21', 'openGauss', 'offical_5.0.1_lite_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813681', 'centos', 'x86_64', 'MINIMAL_LIST', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86/openGauss-5.0.1-CentOS-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 15:07:04.749', 'admin', '2024-08-29 15:07:04.749', 'openGauss', 'offical_5.0.1_mini_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813682', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86_openEuler/openGauss-5.0.1-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-08-29 20:26:41.641', 'admin', '2024-08-29 20:26:41.641', 'openGauss', 'offical_5.0.1_enterprise_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813683', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86_openEuler/openGauss-5.0.1-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-08-29 20:27:04.054', 'admin', '2024-08-29 20:27:04.054', 'openGauss', 'offical_5.0.1_mini_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813684', 'openEuler', 'x86_64', 'LITE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86_openEuler/openGauss-Lite-5.0.1-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-08-29 20:27:20.832', 'admin', '2024-08-29 20:27:20.832', 'openGauss', 'offical_5.0.1_lite_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813685', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/arm/openGauss-5.0.1-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 23:06:22.02', 'admin', '2024-08-29 23:06:22.02', 'openGauss', 'offical_5.0.1_enterprise_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813686', 'openEuler', 'aarch64', 'LITE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/arm/openGauss-Lite-5.0.1-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-08-29 23:15:37.077', 'admin', '2024-08-29 23:15:37.077', 'openGauss', 'offical_5.0.1_lite_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813687', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/arm/openGauss-5.0.1-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 20:26:07.402', 'admin', '2024-08-29 20:26:07.402', 'openGauss', 'offical_5.0.1_mini_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813688', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86_openEuler_2203/openGauss-5.0.1-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-08-29 20:26:41.641', 'admin', '2024-08-29 20:26:41.641', 'openGauss', 'offical_5.0.1_enterprise_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813689', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86_openEuler_2203/openGauss-5.0.1-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-08-29 20:27:04.054', 'admin', '2024-08-29 20:27:04.054', 'openGauss', 'offical_5.0.1_mini_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813690', 'openEuler', 'x86_64', 'LITE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86_openEuler_2203/openGauss-Lite-5.0.1-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-08-29 20:27:20.832', 'admin', '2024-08-29 20:27:20.832', 'openGauss', 'offical_5.0.1_lite_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813691', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/arm_2203/openGauss-5.0.1-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 23:06:22.02', 'admin', '2024-08-29 23:06:22.02', 'openGauss', 'offical_5.0.1_enterprise_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813692', 'openEuler', 'aarch64', 'LITE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/arm_2203/openGauss-Lite-5.0.1-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-08-29 23:15:37.077', 'admin', '2024-08-29 23:15:37.077', 'openGauss', 'offical_5.0.1_lite_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813693', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/arm_2203/openGauss-5.0.1-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 20:26:07.402', 'admin', '2024-08-29 20:26:07.402', 'openGauss', 'offical_5.0.1_mini_openEuler2203_aarch64') ; + +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813694', 'centos', 'x86_64', 'ENTERPRISE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86/openGauss-5.0.2-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 17:41:38.992', 'admin', '2024-08-29 17:41:38.992', 'openGauss', 'offical_5.0.2_enterpirse_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813695', 'centos', 'x86_64', 'LITE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86/openGauss-Lite-5.0.2-CentOS-x86_64.tar.gz', NULL, + 'admin', '2024-08-29 17:41:57.21', 'admin', '2024-08-29 17:41:57.21', 'openGauss', 'offical_5.0.2_lite_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813696', 'centos', 'x86_64', 'MINIMAL_LIST', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86/openGauss-5.0.2-CentOS-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 15:07:04.749', 'admin', '2024-08-29 15:07:04.749', 'openGauss', 'offical_5.0.2_mini_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813697', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86_openEuler/openGauss-5.0.2-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-08-29 20:26:41.641', 'admin', '2024-08-29 20:26:41.641', 'openGauss', 'offical_5.0.2_enterprise_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813698', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86_openEuler/openGauss-5.0.2-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-08-29 20:27:04.054', 'admin', '2024-08-29 20:27:04.054', 'openGauss', 'offical_5.0.2_mini_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813699', 'openEuler', 'x86_64', 'LITE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86_openEuler/openGauss-Lite-5.0.2-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-08-29 20:27:20.832', 'admin', '2024-08-29 20:27:20.832', 'openGauss', 'offical_5.0.2_lite_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813700', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/arm/openGauss-5.0.2-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 23:06:22.02', 'admin', '2024-08-29 23:06:22.02', 'openGauss', 'offical_5.0.2_enterprise_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813701', 'openEuler', 'aarch64', 'LITE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/arm/openGauss-Lite-5.0.2-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-08-29 23:15:37.077', 'admin', '2024-08-29 23:15:37.077', 'openGauss', 'offical_5.0.2_lite_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813702', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/arm/openGauss-5.0.2-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 20:26:07.402', 'admin', '2024-08-29 20:26:07.402', 'openGauss', 'offical_5.0.2_mini_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813703', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86_openEuler_2203/openGauss-5.0.2-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-08-29 20:26:41.641', 'admin', '2024-08-29 20:26:41.641', 'openGauss', 'offical_5.0.2_enterprise_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813704', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86_openEuler_2203/openGauss-5.0.2-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-08-29 20:27:04.054', 'admin', '2024-08-29 20:27:04.054', 'openGauss', 'offical_5.0.2_mini_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813705', 'openEuler', 'x86_64', 'LITE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86_openEuler_2203/openGauss-Lite-5.0.2-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-08-29 20:27:20.832', 'admin', '2024-08-29 20:27:20.832', 'openGauss', 'offical_5.0.2_lite_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813706', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/arm_2203/openGauss-5.0.2-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 23:06:22.02', 'admin', '2024-08-29 23:06:22.02', 'openGauss', 'offical_5.0.2_enterprise_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813707', 'openEuler', 'aarch64', 'LITE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/arm_2203/openGauss-Lite-5.0.2-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-08-29 23:15:37.077', 'admin', '2024-08-29 23:15:37.077', 'openGauss', 'offical_5.0.2_lite_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813708', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/arm_2203/openGauss-5.0.2-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 20:26:07.402', 'admin', '2024-08-29 20:26:07.402', 'openGauss', 'offical_5.0.2_mini_openEuler2203_aarch64') ; + +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813709', 'centos', 'x86_64', 'ENTERPRISE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86/openGauss-5.0.3-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 17:41:38.992', 'admin', '2024-08-29 17:41:38.992', 'openGauss', 'offical_5.0.3_enterpirse_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813710', 'centos', 'x86_64', 'LITE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86/openGauss-Lite-5.0.3-CentOS-x86_64.tar.gz', NULL, + 'admin', '2024-08-29 17:41:57.21', 'admin', '2024-08-29 17:41:57.21', 'openGauss', 'offical_5.0.3_lite_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813711', 'centos', 'x86_64', 'MINIMAL_LIST', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86/openGauss-5.0.3-CentOS-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 15:07:04.749', 'admin', '2024-08-29 15:07:04.749', 'openGauss', 'offical_5.0.3_mini_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813712', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86_openEuler/openGauss-5.0.3-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-08-29 20:26:41.641', 'admin', '2024-08-29 20:26:41.641', 'openGauss', 'offical_5.0.3_enterprise_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813713', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86_openEuler/openGauss-5.0.3-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-08-29 20:27:04.054', 'admin', '2024-08-29 20:27:04.054', 'openGauss', 'offical_5.0.3_mini_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813714', 'openEuler', 'x86_64', 'LITE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86_openEuler/openGauss-Lite-5.0.3-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-08-29 20:27:20.832', 'admin', '2024-08-29 20:27:20.832', 'openGauss', 'offical_5.0.3_lite_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813715', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/arm/openGauss-5.0.3-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 23:06:22.02', 'admin', '2024-08-29 23:06:22.02', 'openGauss', 'offical_5.0.3_enterprise_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813716', 'openEuler', 'aarch64', 'LITE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/arm/openGauss-Lite-5.0.3-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-08-29 23:15:37.077', 'admin', '2024-08-29 23:15:37.077', 'openGauss', 'offical_5.0.3_lite_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813717', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/arm/openGauss-5.0.3-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 20:26:07.402', 'admin', '2024-08-29 20:26:07.402', 'openGauss', 'offical_5.0.3_mini_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813718', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86_openEuler_2203/openGauss-5.0.3-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-08-29 20:26:41.641', 'admin', '2024-08-29 20:26:41.641', 'openGauss', 'offical_5.0.3_enterprise_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813719', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86_openEuler_2203/openGauss-5.0.3-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-08-29 20:27:04.054', 'admin', '2024-08-29 20:27:04.054', 'openGauss', 'offical_5.0.3_mini_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813720', 'openEuler', 'x86_64', 'LITE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86_openEuler_2203/openGauss-Lite-5.0.3-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-08-29 20:27:20.832', 'admin', '2024-08-29 20:27:20.832', 'openGauss', 'offical_5.0.3_lite_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813721', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/arm_2203/openGauss-5.0.3-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 23:06:22.02', 'admin', '2024-08-29 23:06:22.02', 'openGauss', 'offical_5.0.3_enterprise_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813722', 'openEuler', 'aarch64', 'LITE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/arm_2203/openGauss-Lite-5.0.3-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-08-29 23:15:37.077', 'admin', '2024-08-29 23:15:37.077', 'openGauss', 'offical_5.0.3_lite_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813723', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/arm_2203/openGauss-5.0.3-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 20:26:07.402', 'admin', '2024-08-29 20:26:07.402', 'openGauss', 'offical_5.0.3_mini_openEuler2203_aarch64') ; + + +ALTER TABLE ops_package_manager ADD COLUMN os_version VARCHAR(20); +UPDATE ops_package_manager SET os_version = '7' WHERE (os_version IS NULL OR os_version = '''') AND os='centos' and package_url LIKE '%CentOS%'; +UPDATE ops_package_manager SET os_version = '22.03' WHERE (os_version IS NULL OR os_version = '''') AND os = 'openEuler' AND package_url LIKE '%2203%'; +UPDATE ops_package_manager SET os_version = '20.03' WHERE (os_version IS NULL OR os_version = '''') AND os = 'openEuler' AND package_url not LIKE '%2203%'; + +UPDATE ops_package_manager SET os_version = '7' WHERE (os_version IS NULL OR os_version = '''') AND os='centos' and package_url LIKE '%CentOS%'; +UPDATE ops_package_manager SET os_version = '22.03' WHERE (os_version IS NULL OR os_version = '''') AND os = 'openEuler' AND package_url LIKE '%2203%'; +UPDATE ops_package_manager SET os_version = '20.03' WHERE (os_version IS NULL OR os_version = '''') AND os = 'openEuler' AND package_url not LIKE '%2203%'; + + +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813724', 'centos', '7', 'x86_64', 'ENTERPRISE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/CentOS7/x86/openGauss-All-6.0.0-CentOS7-x86_64.tar.gz', NULL, + 'admin', '2024-09-24 17:41:38.992', 'admin', '2024-09-24 17:41:38.992', 'openGauss', 'offical_6.0.0_enterpirse_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813725', 'centos', '7', 'x86_64', 'LITE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/CentOS7/x86/openGauss-Lite-6.0.0-CentOS7-x86_64.tar.gz', NULL, + 'admin', '2024-09-24 17:41:57.21', 'admin', '2024-09-24 17:41:57.21', 'openGauss', 'offical_6.0.0_lite_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813726', 'centos', '7', 'x86_64', 'MINIMAL_LIST', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/CentOS7/x86/openGauss-Server-6.0.0-CentOS7-x86_64.tar.bz2', NULL, + 'admin', '2024-09-24 15:07:04.749', 'admin', '2024-09-24 15:07:04.749', 'openGauss', 'offical_6.0.0_mini_centos_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813727', 'openEuler', '20.03', 'x86_64', 'ENTERPRISE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler20.03/x86/openGauss-All-6.0.0-openEuler20.03-x86_64.tar.gz', + NULL, 'admin', '2024-09-24 20:26:41.641', 'admin', '2024-09-24 20:26:41.641', 'openGauss', 'offical_6.0.0_enterprise_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813728', 'openEuler', '20.03', 'x86_64', 'LITE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler20.03/x86/openGauss-Lite-6.0.0-openEuler20.03-x86_64.tar.gz', + NULL, 'admin', '2024-09-24 20:27:20.832', 'admin', '2024-09-24 20:27:20.832', 'openGauss', 'offical_6.0.0_lite_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813729', 'openEuler', '20.03', 'x86_64', 'MINIMAL_LIST', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler20.03/x86/openGauss-Server-6.0.0-openEuler20.03-x86_64.tar.bz2', + NULL, 'admin', '2024-09-24 20:27:04.054', 'admin', '2024-09-24 20:27:04.054', 'openGauss', 'offical_6.0.0_mini_openEuler2003_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813730', 'openEuler', '20.03', 'aarch64', 'ENTERPRISE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler20.03/arm/openGauss-All-6.0.0-openEuler20.03-aarch64.tar.gz', NULL, + 'admin', '2024-09-24 23:06:22.02', 'admin', '2024-09-24 23:06:22.02', 'openGauss', 'offical_6.0.0_enterprise_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813731', 'openEuler', '20.03', 'aarch64', 'LITE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler20.03/arm/openGauss-Lite-6.0.0-openEuler20.03-aarch64.tar.gz', + NULL, 'admin', '2024-09-24 23:15:37.077', 'admin', '2024-09-24 23:15:37.077', 'openGauss', 'offical_6.0.0_lite_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813732', 'openEuler', '20.03', 'aarch64', 'MINIMAL_LIST', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler20.03/arm/openGauss-Server-6.0.0-openEuler20.03-aarch64.tar.bz2', NULL, + 'admin', '2024-09-24 20:26:07.402', 'admin', '2024-09-24 20:26:07.402', 'openGauss', 'offical_6.0.0_mini_openEuler2003_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813733', 'openEuler', '22.03', 'x86_64', 'ENTERPRISE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler22.03/x86/openGauss-All-6.0.0-openEuler22.03-x86_64.tar.gz', + NULL, 'admin', '2024-09-24 20:26:41.641', 'admin', '2024-09-24 20:26:41.641', 'openGauss', 'offical_6.0.0_enterprise_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813734', 'openEuler', '22.03', 'x86_64', 'LITE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler22.03/x86/openGauss-Lite-6.0.0-openEuler22.03-x86_64.tar.gz', + NULL, 'admin', '2024-09-24 20:27:20.832', 'admin', '2024-09-24 20:27:20.832', 'openGauss', 'offical_6.0.0_lite_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813735', 'openEuler', '22.03', 'x86_64', 'MINIMAL_LIST', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler22.03/x86/openGauss-Server-6.0.0-openEuler22.03-x86_64.tar.bz2', + NULL, 'admin', '2024-09-24 20:27:04.054', 'admin', '2024-09-24 20:27:04.054', 'openGauss', 'offical_6.0.0_mini_openEuler2203_x86_64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813736', 'openEuler', '22.03', 'aarch64', 'ENTERPRISE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler22.03/arm/openGauss-All-6.0.0-openEuler22.03-aarch64.tar.gz', NULL, + 'admin', '2024-09-24 23:06:22.02', 'admin', '2024-09-24 23:06:22.02', 'openGauss', 'offical_6.0.0_enterprise_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813737', 'openEuler', '22.03', 'aarch64', 'LITE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler22.03/arm/openGauss-Lite-6.0.0-openEuler22.03-aarch64.tar.gz', + NULL, 'admin', '2024-09-24 23:15:37.077', 'admin', '2024-09-24 23:15:37.077', 'openGauss', 'offical_6.0.0_lite_openEuler2203_aarch64') ; +INSERT INTO "ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813738', 'openEuler', '22.03', 'aarch64', 'MINIMAL_LIST', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler22.03/arm/openGauss-Server-6.0.0-openEuler22.03-aarch64.tar.bz2', NULL, + 'admin', '2024-09-24 20:26:07.402', 'admin', '2024-09-24 20:26:07.402', 'openGauss', 'offical_6.0.0_mini_openEuler2203_aarch64'); + + +INSERT INTO "ops_package_path_dict" VALUES ('01', 'CentOS','7', 'x86_64', 'LITE', 'x86', 'openGauss-Lite-%s-CentOS-x86_64.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('02', 'CentOS','7', 'x86_64', 'MINIMAL_LIST', 'x86', 'openGauss-%s-CentOS-64bit.tar.bz2', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('03', 'CentOS', '7','x86_64', 'ENTERPRISE', 'x86', 'openGauss-%s-CentOS-64bit-all.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('04', 'openEuler','20.03', 'aarch64', 'LITE', 'arm', 'openGauss-Lite-%s-openEuler-aarch64.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('05', 'openEuler', '20.03','aarch64', 'MINIMAL_LIST', 'arm', 'openGauss-%s-openEuler-64bit.tar.bz2','3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('06', 'openEuler','20.03', 'aarch64', 'ENTERPRISE', 'arm', 'openGauss-%s-openEuler-64bit-all.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('07', 'openEuler','20.03', 'x86_64', 'LITE', 'x86_openEuler', 'openGauss-Lite-%s-openEuler-aarch64.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('08', 'openEuler','20.03', 'x86_64', 'MINIMAL_LIST', 'x86_openEuler', 'openGauss-%s-openEuler-64bit.tar.bz2', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('09', 'openEuler', '20.03','x86_64', 'ENTERPRISE', 'x86_openEuler', 'openGauss-%s-openEuler-64bit-all.tar.gz','3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('10', 'openEuler','22.03', 'aarch64', 'LITE', 'arm_2203', 'openGauss-Lite-%s-openEuler-aarch64.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('11', 'openEuler', '22.03','aarch64', 'MINIMAL_LIST', 'arm_2203', 'openGauss-%s-openEuler-64bit.tar.bz2','3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('12', 'openEuler','22.03', 'aarch64', 'ENTERPRISE', 'arm_2203', 'openGauss-%s-openEuler-64bit-all.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('13', 'openEuler','22.03', 'x86_64', 'LITE', 'x86_openEuler_2203', 'openGauss-Lite-%s-openEuler-aarch64.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('14', 'openEuler','22.03', 'x86_64', 'MINIMAL_LIST', 'x86_openEuler_2203', 'openGauss-%s-openEuler-64bit.tar.bz2','3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('15', 'openEuler','22.03', 'x86_64', 'ENTERPRISE', 'x86_openEuler_2203', 'openGauss-%s-openEuler-64bit-all.tar.gz','3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('16', 'KYLIN', '','x86_64', 'ENTERPRISE', 'x86', 'openGauss-%s-openEuler-64bit-all.tar.gz','3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1' ,NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('17', 'CentOS', '7','x86_64', 'LITE', 'CentOS7/x86', 'openGauss-Lite-%s-CentOS7-x86_64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('18', 'CentOS', '7', 'x86_64', 'MINIMAL_LIST', 'CentOS7/x86', 'openGauss-Server-%s-CentOS7-x86_64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('19', 'CentOS', '7', 'x86_64', 'ENTERPRISE', 'CentOS7/x86', 'openGauss-All-%s-CentOS7-x86_64.tar.gz', '6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('20', 'openEuler', '20.03','x86_64', 'LITE', 'openEuler20.03/x86', 'openGauss-Lite-%s-openEuler20.03-x86_64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('21', 'openEuler', '20.03', 'x86_64', 'MINIMAL_LIST', 'openEuler20.03/x86', 'openGauss-Server-%s-openEuler20.03-x86_64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('22', 'openEuler', '20.03', 'x86_64', 'ENTERPRISE', 'openEuler20.03/x86', 'openGauss-All-%s-openEuler20.03-x86_64.tar.gz', '6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('23', 'openEuler', '22.03','x86_64', 'LITE', 'openEuler22.03/x86', 'openGauss-Lite-%s-openEuler22.03-x86_64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('24', 'openEuler', '22.03', 'x86_64', 'MINIMAL_LIST', 'openEuler22.03/x86', 'openGauss-Server-%s-openEuler22.03-x86_64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('25', 'openEuler', '22.03', 'x86_64', 'ENTERPRISE', 'openEuler22.03/x86', 'openGauss-All-%s-openEuler22.03-x86_64.tar.gz', '6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('26', 'openEuler', '20.03','aarch64', 'LITE', 'openEuler20.03/arm', 'openGauss-Lite-%s-openEuler20.03-aarch64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('27', 'openEuler', '20.03', 'aarch64', 'MINIMAL_LIST', 'openEuler20.03/arm', 'openGauss-Server-%s-openEuler20.03-aarch64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('28', 'openEuler', '20.03', 'aarch64', 'ENTERPRISE', 'openEuler20.03/arm', 'openGauss-All-%s-openEuler20.03-aarch64.tar.gz', '6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('29', 'openEuler', '22.03','aarch64', 'LITE', 'openEuler22.03/arm', 'openGauss-Lite-%s-openEuler22.03-aarch64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('30', 'openEuler', '22.03', 'aarch64', 'MINIMAL_LIST', 'openEuler22.03/arm', 'openGauss-Server-%s-openEuler22.03-aarch64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()); +INSERT INTO "ops_package_path_dict" VALUES ('31', 'openEuler', '22.03', 'aarch64', 'ENTERPRISE', 'openEuler22.03/arm', 'openGauss-All-%s-openEuler22.03-aarch64.tar.gz', '6.0.0', NULL, 'admin', now(), 'admin', now()); + +update "ops_package_path_dict" set package_name_tmp= replace(package_name_tmp,'gz','bz2') where id>17 and version='MINIMAL_LIST'; + + INSERT INTO "sys_role" ("role_id", "role_name", "role_key", "role_sort", "data_scope", "menu_check_strictly", "dept_check_strictly", "status", "del_flag", "create_by", "create_time", "update_by", "update_time", "remark") VALUES (1, '超级管理员', 'admin', 1, '1', 1, 1, '0', '0', 'admin', '2021-11-01 09:38:10', NULL, NULL, '超级管理员'); + INSERT INTO "sys_role" ("role_id", "role_name", "role_key", "role_sort", "data_scope", "menu_check_strictly", "dept_check_strictly", "status", "del_flag", "create_by", "create_time", "update_by", "update_time", "remark") VALUES (2, '普通角色', 'common', 2, '2', 1, 1, '0', '1', 'admin', '2021-11-01 09:38:10', NULL, NULL, '普通角色'); + + + + INSERT INTO "sys_user" ("user_id", "user_name", "nick_name", "user_type", "email", "phonenumber", "sex", "avatar", "password", "status", "del_flag", "login_ip", "login_date", "create_by", "create_time", "update_by", "update_time", "remark") VALUES (1, 'admin', '超级管理员', '00', NULL, NULL, NULL, NULL, '$2a$10$MeXrFYhTOrDXqVDXPBDwrOnxg7NVe1ADX1qnhQe04m94VFKwq3cRy', '0', '0', NULL, NULL, 'admin', '2021-11-01 09:38:09', 'admin', '2022-11-23 13:58:14.047', '管理员'); + + + INSERT INTO "sys_user_role" ("user_id", "role_id") VALUES (1, 1); + + +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (2, '资源中心', 0, 2, '/resource', NULL, NULL, 1, 0, 'M', '0', '0', NULL, 'resource', 'admin', '2022-10-10 22:16:07.030737', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, 'Resources'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (5, '插件管理', 0, 5, '/plugin/manage', 'plugin/manage/index', NULL, 1, 0, 'C', '0', '0', NULL, 'plugin', 'admin', '2022-10-10 22:16:07.030737', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, 'Plugins'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (6, '安全中心', 0, 6, '/security', NULL, NULL, 1, 0, 'M', '0', '0', NULL, 'security', 'admin', '2022-10-10 22:16:07.030737', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, 'Security'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (7, '日志中心', 0, 7, '/logs', NULL, NULL, 1, 0, 'M', '0', '0', NULL, 'logs', 'admin', '2022-11-23 20:54:36.439', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, 'Logs'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (201, '数据库资源', 2, 1, '/resource/database', 'resource/database/index', NULL, 1, 0, 'C', '0', '0', NULL, NULL, 'admin', '2022-10-10 22:16:07.030737', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, 'Database'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (202, '协议资源', 2, 2, '/resource/protocol', 'resource/protocol/index', NULL, 1, 0, 'C', '0', '0', NULL, NULL, 'admin', '2022-10-10 22:16:07.030737', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, 'Protocol'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (203, '物理机资源', 2, 3, '/resource/physical', 'resource/physical/index', NULL, 1, 0, 'C', '0', '0', NULL, NULL, 'admin', '2022-10-10 22:16:07.030737', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, 'Host'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (204, 'AZ管理', 2, 4, '/resource/az', 'resource/az/index', NULL, 1, 0, 'C', '0', '0', NULL, NULL, 'admin', '2022-10-10 22:16:07.030737', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, 'Az Manager'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (601, '账号管理', 6, 1, '/security/user', 'security/user/index', NULL, 1, 0, 'C', '0', '0', NULL, NULL, 'admin', '2022-10-10 22:16:07.030737', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, 'Accounts'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (602, '角色与权限', 6, 2, '/security/role', 'security/role/index', NULL, 1, 0, 'C', '0', '0', NULL, NULL, 'admin', '2022-10-10 22:16:07.030737', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, 'Roles'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (603, '菜单管理', 6, 3, '/security/menu', 'security/menu/index', NULL, 1, 0, 'C', '1', '0', NULL, NULL, 'admin', '2022-10-10 22:16:07.030737', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, 'Menus'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (604, '访问白名单', 6, 4, '/security/whitelist', 'security/whitelist/index', NULL, 1, 0, 'C', '0', '0', NULL, NULL, 'admin', '2022-10-10 22:16:07.030737', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, 'Whitelist'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (605, '个人中心', 6, 5, '/security/usercenter', 'security/user/ucenter/index', NULL, 1, 0, 'C', '1', '0', NULL, NULL, 'admin', NULL, NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, 'User Center'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (701, '系统日志', 7, 1, '/logs/sys-log', 'logs/sys-log/index', NULL, 1, 0, 'C', '0', '0', NULL, NULL, 'admin', '2022-11-23 20:54:36.439', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, 'System Log'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (702, '操作日志', 7, 2, '/logs/oper-log', 'logs/oper-log/index', NULL, 1, 0, 'C', '0', '0', NULL, NULL, 'admin', '2022-11-23 20:54:36.439', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, 'Operation Log'); +INSERT INTO "sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (208, '安装包管理', 2, 8, '/resource/package', 'resource/package/index', NULL, 1, 0, 'C', '0', '0', NULL, NULL, 'admin', '2022-11-23 20:54:36.439', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, 'Package Management'); + +INSERT INTO "sys_log_config" ("key", "value", "id") VALUES ('log_level', 'info', 1); +INSERT INTO "sys_log_config" ("key", "value", "id") VALUES ('log_max_file_size', '5mb', 3); +INSERT INTO "sys_log_config" ("key", "value", "id") VALUES ('log_total_size_cap', '10gb', 4); +INSERT INTO "sys_log_config" ("key", "value", "id") VALUES ('log_max_history', '30', 2); + + + +CREATE TABLE IF NOT EXISTS "ops_jdbcdb_cluster_node" +("cluster_node_id" varchar(25) NOT NULL PRIMARY KEY, + "cluster_id" varchar(255) , + "name" varchar(255) , + "ip" varchar(255) , + "port" varchar(255) , + "username" varchar(255) , + "password" text , + "url" varchar(255) , + "remark" varchar(255) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ); + +CREATE TABLE IF NOT EXISTS "ops_jdbcdb_cluster" +( + "cluster_id" varchar(255) NOT NULL PRIMARY KEY, + "name" varchar(255) , + "deploy_type" varchar(255) , + "db_type" varchar(255) , + "remark" varchar(255) , + "create_by" varchar(64) , + "create_time" timestamp, + "update_by" varchar(64) , + "update_time" timestamp + ); + +-- ---------------------------- +-- Table structure for sys_setting +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "sys_setting"( + "id" int4 NOT NULL PRIMARY KEY AUTOINCREMENT, + "user_id" int8 NOT NULL, + "upload_path" text NOT NULL + ); + + +ALTER TABLE sys_setting ADD COLUMN portal_pkg_download_url text ; +COMMENT ON COLUMN "sys_setting"."portal_pkg_download_url" IS 'portal的在线下载地址'; + +ALTER TABLE sys_setting ADD COLUMN portal_pkg_name text ; +COMMENT ON COLUMN "sys_setting"."portal_pkg_name" IS 'portal的安装包名称'; + +ALTER TABLE sys_setting ADD COLUMN portal_jar_name text ; +COMMENT ON COLUMN "sys_setting"."portal_jar_name" IS 'portal的jar名称'; +UPDATE "sys_setting" SET portal_pkg_download_url = 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/'; +UPDATE "sys_setting" SET portal_pkg_name = 'PortalControl-7.0.0rc1.tar.gz'; +UPDATE "sys_setting" SET portal_jar_name = 'portalControl-7.0.0rc1-exec.jar'; + +COMMENT ON COLUMN "sys_setting"."id" IS 'ID'; +COMMENT ON COLUMN "sys_setting"."user_id" IS '关联的用户ID'; +COMMENT ON COLUMN "sys_setting"."upload_path" IS '文件上传目录'; + +-- ---------------------------- +-- Records of sys_setting +-- ---------------------------- +INSERT INTO "sys_setting" VALUES (1, 1, '/ops/files/', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/', 'PortalControl-7.0.0rc3.tar.gz', 'portalControl-7.0.0rc3-exec.jar'); +INSERT INTO "sys_menu" VALUES (209, '创建服务器', 2, 9, '/resource/physical/createserver', 'resource/physical/components/CreateServerPage', NULL, 1, 0, 'C', '1', '0', NULL, NULL, 'admin', '2025-07-15 20:11:27.003', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, 'Create Server'); +ALTER TABLE sys_user ADD COLUMN update_pwd int2 DEFAULT 0; +COMMENT ON COLUMN "sys_user"."update_pwd" IS '是否修改密码;1:是;0:否'; + +ALTER TABLE ops_cluster ADD COLUMN env_path varchar(255); +COMMENT ON COLUMN "ops_cluster"."env_path" IS '环境变量文件路径'; + +ALTER TABLE ops_cluster ADD COLUMN xml_config_path varchar(255); +COMMENT ON COLUMN "ops_cluster"."xml_config_path" IS 'xml配置路径'; + +ALTER TABLE ops_host ADD COLUMN os varchar(255); +COMMENT ON COLUMN "ops_host"."os" IS '操作系统'; + +ALTER TABLE ops_host ADD COLUMN cpu_arch varchar(255); +COMMENT ON COLUMN "ops_host"."cpu_arch" IS 'CPU架构'; + +ALTER TABLE ops_host ADD COLUMN name varchar(255); +COMMENT ON COLUMN "ops_host"."name" IS '名称'; + + +ALTER TABLE sys_plugins ADD COLUMN plugin_desc_en text ; +COMMENT ON COLUMN "sys_plugins"."plugin_desc_en" IS '插件英文描述'; + +delete from "sys_menu" where menu_id = 202; +update "sys_menu" set menu_name = '实例管理', order_num = 1 where menu_id = 201; +update "sys_menu" set menu_name = '设备管理', order_num = 2 where menu_id = 203; +update "sys_menu" set order_num = 4 where menu_id = 204; + +ALTER TABLE "sys_oper_log" ALTER COLUMN "oper_param" type text; + +CREATE TABLE IF NOT EXISTS "sys_setting"( + "id" int4 NOT NULL PRIMARY KEY AUTOINCREMENT, + "user_id" int8 NOT NULL, + "upload_path" text NOT NULL + ); +COMMENT ON COLUMN "sys_setting"."id" IS 'ID'; +COMMENT ON COLUMN "sys_setting"."user_id" IS '关联的用户ID'; +COMMENT ON COLUMN "sys_setting"."upload_path" IS '文件上传目录'; + +CREATE TABLE IF NOT EXISTS "sys_plugin_logo" ( + "id" int8 NOT NULL PRIMARY KEY AUTOINCREMENT, + "plugin_id" varchar(100), + "logo_path" varchar(255), + ); + +COMMENT ON COLUMN "sys_plugin_logo"."id" IS '主键ID'; + +COMMENT ON COLUMN "sys_plugin_logo"."plugin_id" IS '插件ID'; + +COMMENT ON COLUMN "sys_plugin_logo"."logo_path" IS 'logo路径'; + +update "sys_menu" set menu_name = '服务器管理', menu_en_name = 'server', order_num = 2 where menu_id = 203; + +ALTER TABLE ops_host ADD COLUMN os_version varchar(255); +COMMENT ON COLUMN "ops_host"."os_version" IS '操作系统版本'; + + +CREATE TABLE IF NOT EXISTS "agent_install" ( + "agent_id" varchar(100) NOT NULL PRIMARY KEY, + "agent_ip" varchar(100), + "agent_name" varchar(100), + "agent_port" int4 DEFAULT 1025, + "install_path" varchar(255), + "install_user" varchar(100), + "install_user_id" varchar(100), + "status" varchar(100), + "create_time" timestamp, + "update_time" timestamp + ); + +CREATE TABLE IF NOT EXISTS "agent_task_template_definition" ( + "name" varchar(100) NOT NULL PRIMARY KEY, + "type" varchar(100), + "group_tag" varchar(100), + "plugins_tag" varchar(100), + "operate_obj_type" varchar(100), + "operate_obj" text, + "period" varchar(100), + "collect_metric" varchar(100), + "storage_policy" varchar(100), + "keep_period" varchar(100), + "receive_api" varchar(100), + "create_time" timestamp + ); +COMMENT ON COLUMN "agent_task_template_definition"."name" IS '主键ID 采集任务名称'; +COMMENT ON COLUMN "agent_task_template_definition"."type" IS '采集任务类型 实时任务 统计历史任务'; +COMMENT ON COLUMN "agent_task_template_definition"."group_tag" IS '采集任务所属分组-用户自定义标签'; +COMMENT ON COLUMN "agent_task_template_definition"."plugins_tag" IS '采集任务所属插件-插件标签'; +COMMENT ON COLUMN "agent_task_template_definition"."operate_obj_type" IS '采集任务操作对象类型OS,DB,OSHI,HTTP'; +COMMENT ON COLUMN "agent_task_template_definition"."operate_obj" IS '采集任务操作对象类'; +COMMENT ON COLUMN "agent_task_template_definition"."period" IS '采集任务周期'; +COMMENT ON COLUMN "agent_task_template_definition"."collect_metric" IS '采集任务收集指标'; +COMMENT ON COLUMN "agent_task_template_definition"."storage_policy" IS '采集任务收集数据存储策略'; +COMMENT ON COLUMN "agent_task_template_definition"."keep_period" IS '采集任务收集数据保存周期'; +COMMENT ON COLUMN "agent_task_template_definition"."receive_api" IS '采集任务收集数据接收地址'; +COMMENT ON TABLE "agent_task_template_definition" IS '采集任务模版定义表'; + + +INSERT INTO "agent_task_template_definition" VALUES ('base_host_info', 'OSHI_FIXED_METRIC', 'datakit', NULL, 'OSHI', NULL, 'PT0S', 'base_host_info', 'CUSTOM', NULL, '/receive/fixed/host/info', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_template_definition" VALUES ('host_history_dynamic_metrics', 'OSHI_DYNAMIC_METRIC', 'datakit', NULL, 'OSHI', NULL, 'PT10S', 'host_dynamic_metrics', 'HISTORY', 'PT1H', '/receive/metrics', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_template_definition" VALUES ('opengauss_instance_monitor_history', 'DB_METRIC', 'datakit', NULL, 'DB', NULL, 'PT10S', 'opengauss_instance_monitor', 'HISTORY', 'PT1H', '/receive/metrics', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_template_definition" VALUES ('mysql_instance_monitor_history', 'DB_METRIC', 'datakit', NULL, 'DB', NULL, 'PT10S', 'mysql_instance_monitor', 'HISTORY', 'PT1H', '/receive/metrics', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_template_definition" VALUES ('opengauss_instance_memory_top10', 'DB_PIPE', 'datakit', NULL, 'DB', 'SELECT * FROM pv_session_memory_detail() ORDER BY usedsize desc limit 10;', 'PT10S', 'opengauss_instance_memory_top10', 'HISTORY', 'PT1H', '/receive/pipeline', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_template_definition" VALUES ('opengauss_sql_trace', 'DB_PIPE', 'datakit', NULL, 'HTTP', NULL, 'PT10S', 'opengauss_sql_trace', 'TREE', 'P7D', '/receive/pipeline', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_template_definition" VALUES ('opengauss_alarm_notification', 'DB_PIPE', 'datakit', NULL, 'HTTP', NULL, 'PT10S', 'opengauss_alarm_notification', 'HISTORY', 'P7D', '/receive/pipeline', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_template_definition" VALUES ('opengauss_slow_sql_query', 'DB_PIPE', 'datakit', NULL, 'DB', NULL, 'PT10S', 'opengauss_slow_sql_query', 'FINGERPRINT', 'P7D', '/receive/pipeline', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_template_definition" VALUES ('host_dynamic_metrics', 'OSHI_DYNAMIC_METRIC', 'datakit', NULL, 'OSHI', NULL, 'PT10S', 'host_dynamic_metrics', 'REAL_TIME', NULL, '/receive/metrics', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_template_definition" VALUES ('opengauss_instance_monitor', 'DB_METRIC', 'datakit', NULL, 'DB', NULL, 'PT10S', 'opengauss_instance_monitor', 'REAL_TIME', NULL, '/receive/metrics', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_template_definition" VALUES ('mysql_instance_monitor', 'DB_METRIC', 'datakit', NULL, 'DB', NULL, 'PT10S', 'mysql_instance_monitor', 'REAL_TIME', NULL, '/receive/metrics', '2025-04-16 16:34:16'); + + +CREATE TABLE IF NOT EXISTS "agent_task_schema_definition" ( + "id" int8 NOT NULL PRIMARY KEY AUTOINCREMENT, + "name" varchar(100) NOT NULL , + "metric" varchar(100) , + "create_time" timestamp + ); +COMMENT ON COLUMN "agent_task_schema_definition"."name" IS '采集任务名称'; +COMMENT ON COLUMN "agent_task_schema_definition"."metric" IS '采集任务绑定指标名称'; +COMMENT ON TABLE "agent_task_schema_definition" IS '采集任务数据结构定义表'; + +INSERT INTO "agent_task_schema_definition" VALUES (1, 'base_host_info', 'system.hostname', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (2, 'base_host_info', 'system.os.name', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (3, 'base_host_info', 'system.os.version', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (4, 'base_host_info', 'system.cpu.name', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (5, 'base_host_info', 'system.cpu.max.freq', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (6, 'base_host_info', 'system.cpu.arch', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (7, 'base_host_info', 'system.cpu.core.count', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (8, 'host_dynamic_metrics', 'system.memory.total', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (9, 'host_dynamic_metrics', 'system.cpu.usage', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (10, 'host_dynamic_metrics', 'system.memory.available', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (11, 'host_dynamic_metrics', 'system.memory.usage', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (12, 'host_dynamic_metrics', 'system.net.bytes.received', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (13, 'host_dynamic_metrics', 'system.net.bytes.sent', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (14, 'host_dynamic_metrics', 'system.disk.total', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (15, 'host_dynamic_metrics', 'system.disk.bytes.write', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (16, 'host_dynamic_metrics', 'system.disk.bytes.read', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (17, 'host_dynamic_metrics', 'system.disk.fs.total', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (18, 'host_dynamic_metrics', 'system.disk.fs.used', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (19, 'host_dynamic_metrics', 'system.disk.fs.free', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (24, 'opengauss_instance_monitor', 'opengauss.pg.locks.count', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_schema_definition" VALUES (25, 'opengauss_instance_monitor', 'opengauss.pg.session.count', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_schema_definition" VALUES (26, 'opengauss_instance_monitor', 'opengauss.pg.connect.count', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_schema_definition" VALUES (28, 'mysql_instance_monitor', 'mysql.jdbc.monitor.conn.num', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_schema_definition" VALUES (29, 'mysql_instance_monitor', 'mysql.jdbc.monitor.qps', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_schema_definition" VALUES (30, 'mysql_instance_monitor', 'mysql.jdbc.monitor.tps', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_schema_definition" VALUES (31, 'mysql_instance_monitor', 'mysql.jdbc.monitor.memory.used', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_schema_definition" VALUES (32, 'mysql_instance_monitor', 'mysql.jdbc.monitor.table.space.used', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_schema_definition" VALUES (33, 'opengauss_instance_memory_top10', 'opengauss.pg.session.memory.top10.sessid', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (34, 'opengauss_instance_memory_top10', 'opengauss.pg.session.memory.top10.usedsize', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (35, 'opengauss_instance_memory_top10', 'opengauss.pg.session.memory.top10.freesize', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (36, 'opengauss_instance_memory_top10', 'opengauss.pg.session.memory.top10.totalsize', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (37, 'opengauss_instance_memory_top10', 'opengauss.pg.session.memory.top10.contextname', '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_schema_definition" VALUES (201,'opengauss_alarm_notification', 'opengauss.alarm.id', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_schema_definition" VALUES (202,'opengauss_alarm_notification','opengauss.alarm.name', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_schema_definition" VALUES (203,'opengauss_alarm_notification','opengauss.alarm.level', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_schema_definition" VALUES (204,'opengauss_alarm_notification','opengauss.alarm.scope', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_schema_definition" VALUES (205,'opengauss_alarm_notification','opengauss.alarm.source_tag', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_schema_definition" VALUES (206,'opengauss_alarm_notification','opengauss.alarm.details', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_schema_definition" VALUES (207,'opengauss_alarm_notification','opengauss.alarm.op_type', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_schema_definition" VALUES (208,'opengauss_alarm_notification','opengauss.alarm.clear_type', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_schema_definition" VALUES (209,'opengauss_alarm_notification','opengauss.alarm.start_timestamp', '2025-04-16 16:34:16'); +INSERT INTO "agent_task_schema_definition" VALUES (210,'opengauss_alarm_notification','opengauss.alarm.end_timestamp', '2025-04-16 16:34:16'); + + +CREATE TABLE IF NOT EXISTS "agent_task_metrics_definition" ( + "name" varchar(100) NOT NULL PRIMARY KEY , + "description" varchar(100), + "field_name" varchar(100), + "unit" varchar(100), + "data_type" varchar(100), + "prop" varchar(100), + "collect_cmd" varchar(1024), + "create_time" timestamp + ); +COMMENT ON COLUMN "agent_task_metrics_definition"."name" IS '采集任务指标名称'; +COMMENT ON COLUMN "agent_task_metrics_definition"."description" IS '采集任务指标描述'; +COMMENT ON COLUMN "agent_task_metrics_definition"."field_name" IS '采集任务指标字段名称'; +COMMENT ON COLUMN "agent_task_metrics_definition"."unit" IS '采集任务指标数据展示单位'; +COMMENT ON COLUMN "agent_task_metrics_definition"."data_type" IS '采集任务指标数据类型'; +COMMENT ON COLUMN "agent_task_metrics_definition"."prop" IS '采集任务指标属性'; +COMMENT ON COLUMN "agent_task_metrics_definition"."collect_cmd" IS '指标采集指令-SQL'; +COMMENT ON TABLE "agent_task_metrics_definition" IS '采集任务指标定义表'; + +INSERT INTO "agent_task_metrics_definition" VALUES ('system.hostname', '服务器Host名称', 'hostname', NULL, 'String', NULL, NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.os.name', '操作系统名称', 'osName', NULL, 'String', NULL, NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.os.version', '操作系统版本', 'osVersion', NULL, 'String', NULL, NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.cpu.name', 'cpu名称', 'cpuName', NULL, 'String', NULL, NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.cpu.max.freq', 'cpu最大主频', 'cpuMaxFreq', 'GHz', 'long', NULL, NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.cpu.arch', 'cpu架构', 'cpuArch', NULL, 'String', NULL, NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.cpu.core.count', 'cpu核数', 'cpuCoreCount', NULL, 'long', NULL, NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.memory.total', '内存总量', 'memoryTotal', 'GB', 'long', NULL, NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.cpu.usage', 'CPU使用率', 'cpuUsage', '%', 'double', NULL, NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.memory.available', '剩余内存', 'memoryAvailable', 'GB', 'long', NULL, NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.memory.usage', '内存使用率', 'memoryUsage', '%', 'double', NULL, NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.net.bytes.received', '网卡下行速率', 'netRecv', 'kb/s', 'long', 'faceName', NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.net.bytes.sent', '网卡上行速率', 'netSent', 'kb/s', 'long', 'faceName', NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.disk.total', '磁盘总大小', 'diskTotal', 'GB', 'long', 'diskName', NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.disk.bytes.write', '磁盘写入速率', 'diskWrite', 'GB/s', 'long', 'diskName', NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.disk.bytes.read', '磁盘读取速率', 'diskRead', 'GB/s', 'long', 'diskName', NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.disk.fs.total', '磁盘分区大小', 'diskFsTotal', 'GB', 'long', 'mountPoint', NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.disk.fs.used', '磁盘分区已使用大小', 'diskFsUsed', 'GB', 'long', 'mountPoint', NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('system.disk.fs.free', '磁盘分区剩余大小', 'diskFsFree', 'GB', 'long', 'mountPoint', NULL, '2025-04-16 16:34:16') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.status.cm.state', 'openGauss集群CM状态', 'cmState', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.status.cluster.state', 'openGauss集群Cluster状态', 'cluster_state', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.status.node.state', 'openGauss集群节点状态', 'nodeState', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.status.node.role', 'openGauss集群节点角色', 'nodeRole', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.pg.locks.count', 'openGauss锁数量', 'count', NULL, 'Long', NULL, 'SELECT count(*) count FROM pg_locks', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.pg.session.count', 'openGauss会话数量', 'count', NULL, 'Long', NULL, 'SELECT count(*) count FROM pg_stat_activity', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.pg.connect.count', 'openGauss连接数量', 'count', NULL, 'Long', NULL, 'SELECT count(*) count FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS s', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('mysql.jdbc.monitor.role', 'mysql主备role', 'role', NULL, 'String', NULL, 'SHOW SLAVE STATUS', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('mysql.jdbc.monitor.conn.num', 'mysql连接数', 'value', NULL, 'Long', NULL, 'SHOW STATUS LIKE ''Threads_connected''', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('mysql.jdbc.monitor.qps', 'mysql QPS', 'qps', NULL, 'double', NULL, 'SELECT ROUND(CAST(questions AS UNSIGNED) / IF(CAST(uptime AS UNSIGNED) = 0, 1, CAST(uptime AS UNSIGNED)),2) AS qps FROM ( SELECT MAX(CASE WHEN VARIABLE_NAME = ''Questions'' THEN VARIABLE_VALUE END) AS questions, MAX(CASE WHEN VARIABLE_NAME = ''Uptime'' THEN VARIABLE_VALUE END) AS uptime FROM performance_schema.global_status WHERE VARIABLE_NAME IN (''Questions'', ''Uptime'')) AS status;', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('mysql.jdbc.monitor.tps', 'mysql TPS', 'tps', NULL, 'double', NULL, 'SELECT ROUND(((COALESCE(CAST(com_commit AS UNSIGNED), 0) + COALESCE(CAST(com_rollback AS UNSIGNED), 0)) / IF(COALESCE(CAST(uptime AS UNSIGNED), 0) = 0, 1, COALESCE(CAST(uptime AS UNSIGNED), 0))), 2) AS tps FROM (SELECT MAX(CASE WHEN VARIABLE_NAME = ''Com_commit'' THEN VARIABLE_VALUE END) AS com_commit,MAX(CASE WHEN VARIABLE_NAME = ''Com_rollback'' THEN VARIABLE_VALUE END) AS com_rollback,MAX(CASE WHEN VARIABLE_NAME = ''Uptime'' THEN VARIABLE_VALUE END) AS uptime FROM performance_schema.global_status WHERE VARIABLE_NAME IN (''Com_commit'', ''Com_rollback'', ''Uptime'')) AS status;', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('mysql.jdbc.monitor.memory.used', 'mysql 内存使用量', 'memoryUsed', NULL, 'double', NULL, 'SELECT (@@key_buffer_size + @@innodb_buffer_pool_size + @@innodb_log_buffer_size + @@max_connections * ( @@read_buffer_size + @@read_rnd_buffer_size + @@sort_buffer_size + @@join_buffer_size + @@binlog_cache_size + @@thread_stack + @@tmp_table_size )) AS ''memoryUsed''', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('mysql.jdbc.monitor.table.space.used', 'mysql 表空间使用量', 'tableSpaceUsed', NULL, 'double', NULL, 'SELECT SUM( table_schema_size.table_schema_size ) AS ''tableSpaceUsed'' FROM ( SELECT table_schema, SUM( data_length + index_length ) AS table_schema_size FROM information_schema.TABLES GROUP BY table_schema ) table_schema_size', '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.pg.session.memory.top10.sessid', 'openGauss会话内存使用Top10', 'sessid', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.pg.session.memory.top10.usedsize', 'openGauss会话内存使用Top10', 'usedsize', NULL, 'Long', NULL, NULL, '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.pg.session.memory.top10.freesize', 'openGauss会话内存使用Top10', 'freesize', NULL, 'Long', NULL, NULL, '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.pg.session.memory.top10.totalsize', 'openGauss会话内存使用Top10', 'totalsize', NULL, 'Long', NULL, NULL, '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.pg.session.memory.top10.contextname', 'openGauss会话内存使用Top10', 'contextname', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28') ; +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.alarm.id', 'openGauss告警信息ID', 'id', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28.000'); +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.alarm.name', 'openGauss告警信息名称', 'name', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28.000'); +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.alarm.level', 'openGauss告警信息级别', 'level', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28.000'); +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.alarm.scope', 'openGauss告警信息范围', 'scope', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28.000'); +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.alarm.source_tag', 'openGauss告警信息标签', 'source_tag', '', 'String', NULL, NULL, '2025-04-18 15:36:28.000'); +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.alarm.details', 'openGauss告警信息详情', 'details', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28.000'); +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.alarm.op_type', 'openGauss告警信息操作类型', 'op_type', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28.000'); +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.alarm.clear_type', 'openGauss告警信息清除类型', 'clear_type', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28.000'); +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.alarm.start_timestamp', 'openGauss告警信息开始时间', 'start_timestamp', NULL, 'long', NULL, NULL, '2025-04-18 15:36:28.000'); +INSERT INTO "agent_task_metrics_definition" VALUES ('opengauss.alarm.end_timestamp', 'openGauss告警信息结束时间', 'end_timestamp', NULL, 'long', NULL, NULL, '2025-04-18 15:36:28.000'); + +CREATE TABLE IF NOT EXISTS "agent_task_instance" ( + "id" int8 NOT NULL PRIMARY KEY AUTOINCREMENT, + "task_template_id" varchar(100) NOT NULL, + "task_name" varchar(100) NOT NULL, + "agent_id" varchar(100) NOT NULL, + "cluster_id" varchar(100), + "cluster_original" varchar(100), + "task_status" varchar(100) NOT NULL, + "start_time" timestamp, + "end_time" timestamp, + "error_msg" text, + "task_params" text, + "create_time" timestamp, + "update_time" timestamp + ); +COMMENT ON COLUMN "agent_task_instance"."id" IS '采集任务ID'; +COMMENT ON COLUMN "agent_task_instance"."task_template_id" IS '采集任务绑定模版ID'; +COMMENT ON COLUMN "agent_task_instance"."task_name" IS '采集任务名称'; +COMMENT ON COLUMN "agent_task_instance"."agent_id" IS '采集任务执行AgentID'; +COMMENT ON COLUMN "agent_task_instance"."cluster_id" IS '采集任务关联数据库集群实例ID'; +COMMENT ON COLUMN "agent_task_instance"."cluster_original" IS '采集任务关联集群来源:OPS_CLUSTER;OPS_JDBC_CLUSTER'; +COMMENT ON COLUMN "agent_task_instance"."task_status" IS '采集任务状态'; +COMMENT ON COLUMN "agent_task_instance"."start_time" IS '采集任务开始时间'; +COMMENT ON COLUMN "agent_task_instance"."end_time" IS '采集任务结束时间'; +COMMENT ON COLUMN "agent_task_instance"."error_msg" IS '采集任务异常信息'; +COMMENT ON COLUMN "agent_task_instance"."task_params" IS '采集任务执行参数'; +COMMENT ON TABLE "agent_task_instance" IS 'Agent任务实例表'; + + +CREATE TABLE IF NOT EXISTS "agent_cluster_relation" ( + "cluster_node_id" varchar(100) NOT NULL PRIMARY KEY, + "cluster_original" varchar(100) NOT NULL + ); +COMMENT ON COLUMN "agent_cluster_relation"."cluster_node_id" IS '集群ID'; +COMMENT ON COLUMN "agent_cluster_relation"."cluster_original" IS '采集任务关联集群来源:OPS_CLUSTER;OPS_JDBC_CLUSTER'; +COMMENT ON TABLE "agent_cluster_relation" IS 'Agent任务集群关联表'; + + +CREATE TABLE IF NOT EXISTS "agent_metric_real_time" ( + "multi_id" varchar(300) NOT NULL , + "agent_id" int8 NOT NULL, + "cluster_node_id" varchar(100) , + "metric" varchar(100) NOT NULL , + "property" varchar(100) , + "value" varchar(100) NOT NULL , + "start_time" timestamp, + "end_time" timestamp, + "create_time" timestamp, + PRIMARY KEY ("agent_id","cluster_node_id","metric","property") + ); +COMMENT ON COLUMN "agent_metric_real_time"."multi_id" IS 'Agent 联合主键值'; +COMMENT ON COLUMN "agent_metric_real_time"."agent_id" IS 'AgentID'; +COMMENT ON COLUMN "agent_metric_real_time"."cluster_node_id" IS '集群ID'; +COMMENT ON COLUMN "agent_metric_real_time"."metric" IS '指标ID,指标名称'; +COMMENT ON COLUMN "agent_metric_real_time"."property" IS '指标属性对应值'; +COMMENT ON COLUMN "agent_metric_real_time"."value" IS '指标值'; +COMMENT ON COLUMN "agent_metric_real_time"."start_time" IS '指标采集开始时间'; +COMMENT ON COLUMN "agent_metric_real_time"."end_time" IS '指标采集结束时间'; +COMMENT ON COLUMN "agent_metric_real_time"."create_time" IS '指标保存时间'; +COMMENT ON TABLE "agent_metric_real_time" IS 'Agent实时指标数据表'; + +CREATE TABLE IF NOT EXISTS "agent_metric_historical" ( + "id" int8 NOT NULL PRIMARY KEY, + "task_id" int8 NOT NULL, + "agent_id" int8 NOT NULL, + "cluster_node_id" varchar(100) , + "metric" varchar(100) NOT NULL , + "property" varchar(100) , + "value" varchar(100) NOT NULL , + "start_time" timestamp, + "end_time" timestamp, + "create_time" timestamp + ); +COMMENT ON COLUMN "agent_metric_historical"."id" IS 'Agent 主键值'; +COMMENT ON COLUMN "agent_metric_historical"."task_id" IS '数据采集任务ID'; +COMMENT ON COLUMN "agent_metric_historical"."agent_id" IS 'AgentID'; +COMMENT ON COLUMN "agent_metric_historical"."cluster_node_id" IS '集群ID'; +COMMENT ON COLUMN "agent_metric_historical"."metric" IS '指标ID,指标名称'; +COMMENT ON COLUMN "agent_metric_historical"."property" IS '指标属性对应值'; +COMMENT ON COLUMN "agent_metric_historical"."value" IS '指标值'; +COMMENT ON COLUMN "agent_metric_historical"."start_time" IS '指标采集开始时间'; +COMMENT ON COLUMN "agent_metric_historical"."end_time" IS '指标采集结束时间'; +COMMENT ON COLUMN "agent_metric_historical"."create_time" IS '指标保存时间'; +COMMENT ON TABLE "agent_metric_historical" IS 'Agent历史指标数据表'; + +CREATE TABLE IF NOT EXISTS "agent_task_table_relation" ( + "task_id" varchar(100) NOT NULL PRIMARY KEY , + "task_table_name" varchar(100) NOT NULL, + ); +COMMENT ON COLUMN "agent_task_table_relation"."task_id" IS 'Agent任务集群关联关系-任务ID'; +COMMENT ON COLUMN "agent_task_table_relation"."task_table_name" IS '集群ID'; +COMMENT ON TABLE "agent_task_table_relation" IS 'Agent任务集群关联表'; + +ALTER TABLE "ops_encryption" ALTER COLUMN "public_key" TYPE text; +ALTER TABLE "ops_encryption" ALTER COLUMN "encryption_key" TYPE text; +ALTER TABLE "ops_encryption" ALTER COLUMN "private_key" TYPE text; +ALTER TABLE "ops_jdbcdb_cluster_node" ALTER COLUMN "password" TYPE text; +ALTER TABLE "ops_host_user" ALTER COLUMN "password" TYPE text; +ALTER TABLE "ops_cluster_task" ALTER COLUMN "database_password" TYPE text; +ALTER TABLE "ops_cluster" ALTER COLUMN "database_password" TYPE text; + + +CREATE TABLE IF NOT EXISTS "sys_setting_ext" ( + "id" int8 NOT NULL PRIMARY KEY AUTOINCREMENT, + "user_id" int8 NOT NULL, + "param_name" varchar(64) NOT NULL, + "param_value" varchar(64), + "remark" varchar(128) +); +insert into "sys_setting_ext" values(1,1,'datakit_server_host','',''); \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/db/openGauss-visualtool.sql b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/db/openGauss-visualtool.sql new file mode 100644 index 000000000..e4c62bf27 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/db/openGauss-visualtool.sql @@ -0,0 +1,2676 @@ +CREATE OR REPLACE FUNCTION create_sequences_not_exists() RETURNS integer AS 'BEGIN + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_sys_log_config_id'' ) +THEN +CREATE SEQUENCE "public"."sq_sys_log_config_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_sys_menu_id'' ) +THEN +CREATE SEQUENCE "public"."sq_sys_menu_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1000 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_sys_log_id'' ) +THEN +CREATE SEQUENCE "public"."sq_sys_log_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_sys_plugin_config_data_id'' ) +THEN +CREATE SEQUENCE "public"."sq_sys_plugin_config_data_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_sys_plugin_config_id'' ) +THEN +CREATE SEQUENCE "public"."sq_sys_plugin_config_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_sys_plugin_id'' ) +THEN +CREATE SEQUENCE "public"."sq_sys_plugin_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_sys_role_id'' ) +THEN +CREATE SEQUENCE "public"."sq_sys_role_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 100 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_sys_user_id'' ) +THEN +CREATE SEQUENCE "public"."sq_sys_user_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 2 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_sys_white_list_id'' ) +THEN +CREATE SEQUENCE "public"."sq_sys_white_list_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_sys_task_id'' ) +THEN +CREATE SEQUENCE "public"."sq_sys_task_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_sys_setting_id'' ) +THEN +CREATE SEQUENCE "public"."sq_sys_setting_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1000 +CACHE 1; +END IF; + +IF NOT EXISTS(SELECT 1 FROM information_schema.sequences WHERE sequence_schema = ''public'' AND sequence_name = ''sq_sys_plugin_logo_id'') +THEN + CREATE SEQUENCE "public"."sq_sys_plugin_logo_id" + INCREMENT 1 + MINVALUE 1 + MAXVALUE 9223372036854775807 + START 1000 + CACHE 1; +END IF; + +RETURN 0; +END;' +LANGUAGE plpgsql; + +SELECT create_sequences_not_exists(); +DROP FUNCTION create_sequences_not_exists; + + +CREATE TABLE IF NOT EXISTS "public"."ops_az" ( + "az_id" int8 NOT NULL PRIMARY KEY, + "name" varchar(255) COLLATE "pg_catalog"."default" NOT NULL, + "priority" int2, + "address" varchar(255) COLLATE "pg_catalog"."default", + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ) +; +COMMENT ON COLUMN "public"."ops_az"."az_id" IS 'az主键'; +COMMENT ON COLUMN "public"."ops_az"."name" IS 'az名称'; +COMMENT ON COLUMN "public"."ops_az"."priority" IS '优先级'; +COMMENT ON COLUMN "public"."ops_az"."address" IS '实际地理位置'; +COMMENT ON COLUMN "public"."ops_az"."remark" IS '描述'; +COMMENT ON COLUMN "public"."ops_az"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."ops_az"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."ops_az"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."ops_az"."update_time" IS '更新时间'; + +INSERT INTO "public"."ops_az"("az_id", "name", "priority", "address", "remark", "create_by", "create_time", "update_by", "update_time") VALUES (1639451067550920705, 'az1', NULL, '中国', NULL, 'admin', '2023-03-25 10:16:08.69', 'admin', '2023-03-25 10:16:08.69') ON DUPLICATE KEY +UPDATE NOTHING;; + +-- ---------------------------- +-- Table structure for ops_check +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."ops_check" ( + "check_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "cluster_id" varchar(255) COLLATE "pg_catalog"."default", + "check_res" text COLLATE "pg_catalog"."default", + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ) +; +COMMENT ON COLUMN "public"."ops_check"."remark" IS '描述'; +COMMENT ON COLUMN "public"."ops_check"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."ops_check"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."ops_check"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."ops_check"."update_time" IS '更新时间'; + +-- ---------------------------- +-- Table structure for ops_cluster +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."ops_cluster" ( + "cluster_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "version" varchar(255) COLLATE "pg_catalog"."default", + "version_num" varchar(255) COLLATE "pg_catalog"."default", + "install_mode" varchar(64) COLLATE "pg_catalog"."default", + "deploy_type" varchar(64) COLLATE "pg_catalog"."default", + "cluster_name" varchar(255) COLLATE "pg_catalog"."default", + "install_package_path" varchar(255) COLLATE "pg_catalog"."default", + "database_password" text COLLATE "pg_catalog"."default", + "database_username" varchar(255) COLLATE "pg_catalog"."default", + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "install_path" varchar(255) COLLATE "pg_catalog"."default", + "log_path" varchar(255) COLLATE "pg_catalog"."default", + "tmp_path" varchar(255) COLLATE "pg_catalog"."default", + "om_tools_path" varchar(255) COLLATE "pg_catalog"."default", + "core_path" varchar(255) COLLATE "pg_catalog"."default", + "port" varchar(255) COLLATE "pg_catalog"."default", + "enable_dcf" int2, + "dcf_port" varchar(255) COLLATE "pg_catalog"."default" + ) +; +COMMENT ON COLUMN "public"."ops_cluster"."cluster_id" IS '集群标识'; +COMMENT ON COLUMN "public"."ops_cluster"."version" IS '版本'; +COMMENT ON COLUMN "public"."ops_cluster"."version_num" IS '版本号'; +COMMENT ON COLUMN "public"."ops_cluster"."install_mode" IS '安装模式'; +COMMENT ON COLUMN "public"."ops_cluster"."deploy_type" IS '部署方式'; +COMMENT ON COLUMN "public"."ops_cluster"."cluster_name" IS '集群名称'; +COMMENT ON COLUMN "public"."ops_cluster"."install_package_path" IS '安装包路径'; +COMMENT ON COLUMN "public"."ops_cluster"."database_password" IS '数据库密码'; +COMMENT ON COLUMN "public"."ops_cluster"."database_username" IS '数据库用户名'; +COMMENT ON COLUMN "public"."ops_cluster"."remark" IS '描述'; +COMMENT ON COLUMN "public"."ops_cluster"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."ops_cluster"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."ops_cluster"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."ops_cluster"."update_time" IS '更新时间'; +COMMENT ON COLUMN "public"."ops_cluster"."install_path" IS '企业版安装路径'; +COMMENT ON COLUMN "public"."ops_cluster"."log_path" IS '企业版日志路径'; +COMMENT ON COLUMN "public"."ops_cluster"."tmp_path" IS '企业版临时文件路径'; +COMMENT ON COLUMN "public"."ops_cluster"."om_tools_path" IS '企业版om路径'; +COMMENT ON COLUMN "public"."ops_cluster"."core_path" IS '企业版核心路径'; +COMMENT ON COLUMN "public"."ops_cluster"."port" IS '企业版端口'; +COMMENT ON COLUMN "public"."ops_cluster"."enable_dcf" IS '是否开启DCF 0否1是'; + +CREATE OR REPLACE FUNCTION add_dcf_port() RETURNS integer AS ' +BEGIN +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_cluster'' AND COLUMN_NAME = +''dcf_port'' ) = 0 +THEN +ALTER TABLE ops_cluster ADD COLUMN dcf_port varchar(255); +COMMENT ON COLUMN "public"."ops_cluster"."dcf_port" IS ''DCF端口''; +END IF; +RETURN 0; +END;' +LANGUAGE plpgsql; + +SELECT add_dcf_port(); + +DROP FUNCTION add_dcf_port; + +-- ---------------------------- +-- Table structure for ops_cluster_node +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."ops_cluster_node" ( + "cluster_node_id" int8 NOT NULL PRIMARY KEY, + "cluster_role" varchar(255) COLLATE "pg_catalog"."default", + "host_id" int8, + "install_user_id" int8, + "install_path" varchar(255) COLLATE "pg_catalog"."default", + "data_path" varchar(255) COLLATE "pg_catalog"."default", + "pkg_path" varchar(255) COLLATE "pg_catalog"."default", + "install_demo_database" int2, + "cluster_id" varchar(255) COLLATE "pg_catalog"."default", + "is_install_cm" int2, + "is_cm_master" int2, + "cm_data_path" varchar(255) COLLATE "pg_catalog"."default", + "cm_port" varchar(255) COLLATE "pg_catalog"."default", + "xlog_path" varchar(255) COLLATE "pg_catalog"."default", + "is_enable_dss" int2, + "dss_data_lun_link_path" varchar(255) COLLATE "pg_catalog"."default", + "xlog_lun_link_path" varchar(255) COLLATE "pg_catalog"."default", + "cm_voting_lun_link_path" varchar(255) COLLATE "pg_catalog"."default", + "cm_sharing_lun_link_path" varchar(255) COLLATE "pg_catalog"."default", + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ) +; + +CREATE OR REPLACE FUNCTION add_lun_link_path() RETURNS integer AS ' +BEGIN +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_cluster_node'' AND COLUMN_NAME = +''is_enable_dss'' ) = 0 +THEN +ALTER TABLE ops_cluster_node ADD COLUMN is_enable_dss int2; +ALTER TABLE ops_cluster_node ADD COLUMN dss_data_lun_link_path VARCHAR(255); +ALTER TABLE ops_cluster_node ADD COLUMN xlog_lun_link_path VARCHAR(255); +ALTER TABLE ops_cluster_node ADD COLUMN cm_voting_lun_link_path VARCHAR(255); +ALTER TABLE ops_cluster_node ADD COLUMN cm_sharing_lun_link_path VARCHAR(255); +END IF; +RETURN 0; + END;' + LANGUAGE plpgsql; + +SELECT add_lun_link_path(); + +DROP FUNCTION add_lun_link_path; + +COMMENT ON COLUMN "public"."ops_cluster_node"."cluster_role" IS '集群角色'; +COMMENT ON COLUMN "public"."ops_cluster_node"."host_id" IS '主机ID'; +COMMENT ON COLUMN "public"."ops_cluster_node"."install_user_id" IS '安装用户ID'; +COMMENT ON COLUMN "public"."ops_cluster_node"."install_path" IS '安装路径'; +COMMENT ON COLUMN "public"."ops_cluster_node"."data_path" IS '数据目录'; +COMMENT ON COLUMN "public"."ops_cluster_node"."pkg_path" IS '安装包目录'; +COMMENT ON COLUMN "public"."ops_cluster_node"."install_demo_database" IS '是否安装示例数据库'; +COMMENT ON COLUMN "public"."ops_cluster_node"."cluster_id" IS '集群ID'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."is_install_cm" IS '是否安装 CM'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."is_cm_master" IS '是否CM master'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."cm_data_path" IS 'cm数据路径'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."cm_port" IS 'cm端口'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."xlog_path" IS 'xlog路径'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."is_enable_dss" IS '是否开启Dss'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."dss_data_lun_link_path" IS 'data卷软链接'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."xlog_lun_link_path" IS 'xlog卷软链接'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."cm_voting_lun_link_path" IS 'cm投票卷软链接'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."cm_sharing_lun_link_path" IS 'cm共享卷软链接'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."remark" IS '描述'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."create_by" IS '创建者'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."create_time" IS '创建时间'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."update_by" IS '更新者'; +COMMENT +ON COLUMN "public"."ops_cluster_node"."update_time" IS '更新时间'; + +ALTER TABLE "public"."ops_cluster_node" +ALTER +COLUMN "cluster_node_id" TYPE varchar(255) USING "cluster_node_id"::varchar(255); + + + +-- ---------------------------- +-- Table structure for ops_cluster_task +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."ops_cluster_task" ( + "cluster_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "host_id" varchar(255) COLLATE "pg_catalog"."default", + "host_user_id" varchar(255) COLLATE "pg_catalog"."default", + "os" varchar(255) COLLATE "pg_catalog"."default", + "cpu_arch" varchar(255) COLLATE "pg_catalog"."default", + "version" varchar(255) COLLATE "pg_catalog"."default", + "version_num" varchar(255) COLLATE "pg_catalog"."default", + "package_name" varchar(255) COLLATE "pg_catalog"."default", + "package_id" varchar(255) COLLATE "pg_catalog"."default", + "cluster_name" varchar(255) COLLATE "pg_catalog"."default", + "database_username" varchar(255) COLLATE "pg_catalog"."default", + "database_password" text COLLATE "pg_catalog"."default", + "database_port" int4 DEFAULT 5432, + "install_package_path" varchar(255) COLLATE "pg_catalog"."default", + "install_path" varchar(255) COLLATE "pg_catalog"."default", + "log_path" varchar(255) COLLATE "pg_catalog"."default", + "tmp_path" varchar(255) COLLATE "pg_catalog"."default", + "om_tools_path" varchar(255) COLLATE "pg_catalog"."default", + "core_path" varchar(255) COLLATE "pg_catalog"."default", + "env_path" varchar(255) COLLATE "pg_catalog"."default", + "enable_cm_tool" int4 DEFAULT 0, + "enable_dcf" int4 DEFAULT 0, + "enable_generate_environment_variable_file" int4 DEFAULT 0, + "xml_config_path" varchar(255) COLLATE "pg_catalog"."default", + "deploy_type" varchar(255) COLLATE "pg_catalog"."default", + "cluster_node_num" int4 DEFAULT 0, + "status" varchar(255) COLLATE "pg_catalog"."default", + "execute_time" timestamp(6), + "env_check_result" varchar(255) COLLATE "pg_catalog"."default", + "remark" text COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ) +; +COMMENT ON COLUMN "public"."ops_cluster_task"."cluster_id" IS '集群ID'; +COMMENT ON COLUMN "public"."ops_cluster_task"."host_id" IS '服务器IP'; +COMMENT ON COLUMN "public"."ops_cluster_task"."host_user_id" IS '安装服务器用户ID'; +COMMENT ON COLUMN "public"."ops_cluster_task"."os" IS '操作系统名称'; +COMMENT ON COLUMN "public"."ops_cluster_task"."cpu_arch" IS 'CPU架构'; +COMMENT ON COLUMN "public"."ops_cluster_task"."version" IS '数据库版本'; +COMMENT ON COLUMN "public"."ops_cluster_task"."version_num" IS '数据库版本号'; +COMMENT ON COLUMN "public"."ops_cluster_task"."package_name" IS '安装包名称'; +COMMENT ON COLUMN "public"."ops_cluster_task"."package_id" IS '安装包ID'; +COMMENT ON COLUMN "public"."ops_cluster_task"."cluster_name" IS '集群名称'; +COMMENT ON COLUMN "public"."ops_cluster_task"."database_username" IS '数据库用户名称'; +COMMENT ON COLUMN "public"."ops_cluster_task"."database_password" IS '数据库密码'; +COMMENT ON COLUMN "public"."ops_cluster_task"."database_port" IS '数据库端口'; +COMMENT ON COLUMN "public"."ops_cluster_task"."install_package_path" IS '安装包路径'; +COMMENT ON COLUMN "public"."ops_cluster_task"."install_path" IS '安装路径'; +COMMENT ON COLUMN "public"."ops_cluster_task"."log_path" IS 'log路径'; +COMMENT ON COLUMN "public"."ops_cluster_task"."tmp_path" IS 'tmp路径'; +COMMENT ON COLUMN "public"."ops_cluster_task"."om_tools_path" IS 'om工具安装路径'; +COMMENT ON COLUMN "public"."ops_cluster_task"."core_path" IS 'core 路径'; +COMMENT ON COLUMN "public"."ops_cluster_task"."env_path" IS '环境变量路径'; +COMMENT ON COLUMN "public"."ops_cluster_task"."enable_cm_tool" IS '是否安装CM工具'; +COMMENT ON COLUMN "public"."ops_cluster_task"."enable_dcf" IS '是否dcf'; +COMMENT ON COLUMN "public"."ops_cluster_task"."enable_generate_environment_variable_file" IS '是否分离环境变量文件'; +COMMENT ON COLUMN "public"."ops_cluster_task"."xml_config_path" IS 'xml安装配置路径'; +COMMENT ON COLUMN "public"."ops_cluster_task"."deploy_type" IS '集群部署类型'; +COMMENT ON COLUMN "public"."ops_cluster_task"."cluster_node_num" IS '集群节点数量'; +COMMENT ON COLUMN "public"."ops_cluster_task"."status" IS '安装任务执行状态'; +COMMENT ON COLUMN "public"."ops_cluster_task"."execute_time" IS '任务执行时间'; +COMMENT ON COLUMN "public"."ops_cluster_task"."env_check_result" IS '环境检查结果'; +COMMENT ON COLUMN "public"."ops_cluster_task"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."ops_cluster_task"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."ops_cluster_task"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."ops_cluster_task"."update_time" IS '更新时间'; +COMMENT ON TABLE "public"."ops_cluster_task" IS 'openGauss集群安装任务表'; + + + +-- ---------------------------- +-- Table structure for ops_cluster_task_node +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."ops_cluster_task_node" ( + "cluster_node_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "cluster_id" varchar(255) COLLATE "pg_catalog"."default", + "host_id" varchar(255) COLLATE "pg_catalog"."default", + "host_user_id" varchar(255) COLLATE "pg_catalog"."default", + "node_type" varchar(255) COLLATE "pg_catalog"."default", + "data_path" varchar(255) COLLATE "pg_catalog"."default", + "az_owner" varchar(255) COLLATE "pg_catalog"."default", + "az_priority" varchar(255) COLLATE "pg_catalog"."default", + "env_check_result" varchar(255) COLLATE "pg_catalog"."default", + "env_check_detail" text COLLATE "pg_catalog"."default", + "is_cm_master" int4 DEFAULT 0, + "cm_data_path" varchar(255) COLLATE "pg_catalog"."default", + "cm_port" int4 DEFAULT 0, + "remark" text COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ) +; + +COMMENT ON COLUMN "public"."ops_cluster_task_node"."cluster_node_id" IS '集群节点ID'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."cluster_id" IS '集群ID'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."host_id" IS '节点服务器Id'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."host_user_id" IS '节点用户ID'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."node_type" IS '节点类型'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."data_path" IS '数据路径'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."az_owner" IS '所属AZ'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."az_priority" IS 'AZ优先级'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."env_check_result" IS '环境检查结果'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."env_check_detail" IS '环境检查详情'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."is_cm_master" IS '是否CM主节点'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."cm_data_path" IS 'CM数据路径'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."cm_port" IS 'CM端口'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."ops_cluster_task_node"."update_time" IS '更新时间'; +COMMENT ON TABLE "public"."ops_cluster_task_node" IS '集群安装任务节点信息'; + +-- ---------------------------- +-- Table structure for ops_encryption +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."ops_encryption" ( + "encryption_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "key_security" varchar(64) COLLATE "pg_catalog"."default", + "encryption_key" text COLLATE "pg_catalog"."default", + "public_key" text COLLATE "pg_catalog"."default", + "private_key" text COLLATE "pg_catalog"."default" + ) +; +COMMENT ON COLUMN "public"."ops_encryption"."remark" IS '描述'; +COMMENT ON COLUMN "public"."ops_encryption"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."ops_encryption"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."ops_encryption"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."ops_encryption"."update_time" IS '更新时间'; + +CREATE OR REPLACE FUNCTION add_ops_encryption_field_func() RETURNS integer AS 'BEGIN +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_encryption'' AND COLUMN_NAME = ''key_security'' ) = 0 +THEN +ALTER TABLE ops_encryption ADD COLUMN key_security varchar(64); +COMMENT ON COLUMN "public"."ops_encryption"."key_security" IS ''检查当前密钥是否安全存储''; +END IF; +RETURN 0; +END;' +LANGUAGE plpgsql; + +SELECT add_ops_encryption_field_func(); + +DROP FUNCTION add_ops_encryption_field_func; + +-- ---------------------------- +-- Table structure for ops_host +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."ops_host" ( + "host_id" int8 NOT NULL PRIMARY KEY, + "hostname" varchar(255) COLLATE "pg_catalog"."default", + "private_ip" varchar(64) COLLATE "pg_catalog"."default" NOT NULL, + "public_ip" varchar(64) COLLATE "pg_catalog"."default" NOT NULL, + "port" int8 NOT NULL, + "az_id" int8, + "os_build" varchar(255) , + "cpu_model" varchar(255) , + "cpu_arch" varchar(64) , + "cpu_freq" varchar(64) , + "physical_cores" int4 , + "logical_cores" int4 , + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ) +; +COMMENT ON COLUMN "public"."ops_host"."host_id" IS '主机id'; +COMMENT ON COLUMN "public"."ops_host"."hostname" IS '主机名'; +COMMENT ON COLUMN "public"."ops_host"."private_ip" IS '内网ip地址'; +COMMENT ON COLUMN "public"."ops_host"."public_ip" IS '公网ip地址'; +COMMENT ON COLUMN "public"."ops_host"."port" IS '端口'; +COMMENT ON COLUMN "public"."ops_host"."az_id" IS 'az_id'; +COMMENT ON COLUMN "public"."ops_host"."remark" IS '描述'; +COMMENT ON COLUMN "public"."ops_host"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."ops_host"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."ops_host"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."ops_host"."update_time" IS '更新时间'; + +-- ---------------------------- +-- Table structure for ops_host_user +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."ops_host_user" ( + "host_user_id" int8 NOT NULL PRIMARY KEY, + "username" varchar(255) COLLATE "pg_catalog"."default" NOT NULL, + "password" text COLLATE "pg_catalog"."default", + "host_id" int8 NOT NULL, + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ) +; +COMMENT ON COLUMN "public"."ops_host_user"."host_user_id" IS '主机用户主键'; +COMMENT ON COLUMN "public"."ops_host_user"."username" IS '用户名'; +COMMENT ON COLUMN "public"."ops_host_user"."password" IS '密码'; +COMMENT ON COLUMN "public"."ops_host_user"."host_id" IS '所属主机id'; +COMMENT ON COLUMN "public"."ops_host_user"."remark" IS '备注'; +COMMENT ON COLUMN "public"."ops_host_user"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."ops_host_user"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."ops_host_user"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."ops_host_user"."update_time" IS '更新时间'; + +CREATE TABLE IF NOT EXISTS "public"."ops_host_tag" ( + "host_tag_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "name" varchar(255) COLLATE "pg_catalog"."default", + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ) +; +COMMENT ON COLUMN "public"."ops_host_tag"."name" IS '标签名'; +COMMENT ON COLUMN "public"."ops_host_tag"."remark" IS '备注'; +COMMENT ON COLUMN "public"."ops_host_tag"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."ops_host_tag"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."ops_host_tag"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."ops_host_tag"."update_time" IS '更新时间'; + +-- ---------------------------- +-- Table structure for ops_host_tag_rel +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."ops_host_tag_rel" ( + "id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "host_id" varchar(255) COLLATE "pg_catalog"."default", + "tag_id" varchar(255) COLLATE "pg_catalog"."default", + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ) +; +COMMENT ON COLUMN "public"."ops_host_tag_rel"."host_id" IS '主机ID'; +COMMENT ON COLUMN "public"."ops_host_tag_rel"."tag_id" IS 'tag ID'; +COMMENT ON COLUMN "public"."ops_host_tag_rel"."remark" IS '描述'; +COMMENT ON COLUMN "public"."ops_host_tag_rel"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."ops_host_tag_rel"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."ops_host_tag_rel"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."ops_host_tag_rel"."update_time" IS '更新时间'; + + +-- ---------------------------- +-- Table structure for ops_package_path_dict +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."ops_package_path_dict" ( + "id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "os" varchar(255) COLLATE "pg_catalog"."default", + "os_version" varchar(255) COLLATE "pg_catalog"."default", + "cpu_arch" varchar(255) COLLATE "pg_catalog"."default", + "version" varchar(255) COLLATE "pg_catalog"."default", + "url_path" varchar(255) COLLATE "pg_catalog"."default", + "package_name_tmp" varchar(255) COLLATE "pg_catalog"."default", + "pkg_tmp_use_version" varchar(255) COLLATE "pg_catalog"."default", + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ) +; +COMMENT ON COLUMN "public"."ops_package_path_dict"."os" IS '操作系统名称'; +COMMENT ON COLUMN "public"."ops_package_path_dict"."os_version" IS '操作系统版本'; +COMMENT ON COLUMN "public"."ops_package_path_dict"."cpu_arch" IS 'CPU架构'; +COMMENT ON COLUMN "public"."ops_package_path_dict"."version" IS 'openGauss版本'; +COMMENT ON COLUMN "public"."ops_package_path_dict"."url_path" IS 'openGauss包URI路径'; +COMMENT ON COLUMN "public"."ops_package_path_dict"."package_name_tmp" IS 'openGauss包名称模版'; +COMMENT ON COLUMN "public"."ops_package_path_dict"."pkg_tmp_use_version" IS 'openGauss模版使用版本号'; +COMMENT ON COLUMN "public"."ops_package_path_dict"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."ops_package_path_dict"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."ops_package_path_dict"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."ops_package_path_dict"."update_time" IS '更新时间'; +COMMENT ON TABLE "public"."ops_package_path_dict" IS 'openGauss包名称字典表'; + + +CREATE OR REPLACE FUNCTION update_ops_package_path_dict_structure() RETURNS integer AS ' +BEGIN + IF + ( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = ''public'' AND TABLE_NAME = ''ops_package_path_dict'' AND COLUMN_NAME = ''os_version'' ) = 0 + THEN + ALTER TABLE ops_package_path_dict ADD COLUMN os_version varchar(255) AFTER os; + UPDATE ops_package_path_dict SET os_version = ''7'' WHERE (os_version IS NULL OR os_version = '''') AND os=''CentOS''; + UPDATE ops_package_path_dict SET os_version = ''20.03'' WHERE (os_version IS NULL OR os_version = '''') AND os = ''openEuler'' AND url_path NOT LIKE ''%2203%''; + UPDATE ops_package_path_dict SET os_version = ''22.03'', os = ''openEuler'' WHERE (os_version IS NULL OR os_version = '''') AND os LIKE ''%%openEuler%%'' AND url_path LIKE ''%2203%'' ; + END IF; + IF + ( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = ''public'' AND TABLE_NAME = ''ops_package_path_dict'' AND COLUMN_NAME = ''pkg_tmp_use_version'' ) = 0 + THEN + ALTER TABLE ops_package_path_dict ADD COLUMN pkg_tmp_use_version varchar(255) AFTER package_name_tmp; + UPDATE ops_package_path_dict SET pkg_tmp_use_version = ''3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1'' WHERE (pkg_tmp_use_version IS NULL OR pkg_tmp_use_version = '''') ; + END IF; + RETURN 0; +END;' +LANGUAGE plpgsql; + +SELECT update_ops_package_path_dict_structure(); + +DROP FUNCTION update_ops_package_path_dict_structure; + + +CREATE OR REPLACE FUNCTION add_host_user_field_func() RETURNS integer AS 'BEGIN +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_host_user'' AND COLUMN_NAME = ''sudo'' ) = 0 +THEN +ALTER TABLE ops_host_user ADD COLUMN sudo int2 DEFAULT 0; +COMMENT ON COLUMN "public"."ops_host_user"."sudo" IS ''管理员''; +END IF; +RETURN 0; +END;' +LANGUAGE plpgsql; + +SELECT add_host_user_field_func(); + +DROP FUNCTION add_host_user_field_func; + +-- ---------------------------- +-- Table structure for sys_log_config +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."sys_log_config" ( + "key" varchar(100) COLLATE "pg_catalog"."default" NOT NULL, + "value" varchar(500) COLLATE "pg_catalog"."default" NOT NULL, + "id" int4 NOT NULL PRIMARY KEY DEFAULT nextval('sq_sys_log_config_id'::regclass) + ) +; + +-- ---------------------------- +-- Table structure for sys_menu +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."sys_menu" ( + "menu_id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_sys_menu_id'::regclass), + "menu_name" varchar(50) COLLATE "pg_catalog"."default" NOT NULL, + "parent_id" int8, + "order_num" int4, + "path" varchar(200) COLLATE "pg_catalog"."default", + "component" varchar(255) COLLATE "pg_catalog"."default", + "query" varchar(255) COLLATE "pg_catalog"."default", + "is_frame" int4 DEFAULT 1, + "is_cache" int4 DEFAULT 0, + "menu_type" char(1) COLLATE "pg_catalog"."default", + "visible" char(1) COLLATE "pg_catalog"."default" DEFAULT 0, + "status" char(1) COLLATE "pg_catalog"."default" DEFAULT 0, + "perms" varchar(100) COLLATE "pg_catalog"."default", + "icon" varchar(100) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "remark" varchar(500) COLLATE "pg_catalog"."default", + "open_way" int4 DEFAULT 1, + "plugin_id" varchar(100) COLLATE "pg_catalog"."default", + "open_position" int2 DEFAULT 1, + "query_template" varchar(100) COLLATE "pg_catalog"."default", + "plugin_theme" varchar(50) COLLATE "pg_catalog"."default", + "menu_classify" int2 DEFAULT 1, + "menu_en_name" varchar(50) COLLATE "pg_catalog"."default" + ) +; +COMMENT ON COLUMN "public"."sys_menu"."menu_id" IS '菜单ID'; +COMMENT ON COLUMN "public"."sys_menu"."menu_name" IS '菜单名称'; +COMMENT ON COLUMN "public"."sys_menu"."parent_id" IS '父菜单ID'; +COMMENT ON COLUMN "public"."sys_menu"."order_num" IS '显示顺序'; +COMMENT ON COLUMN "public"."sys_menu"."path" IS '路由地址'; +COMMENT ON COLUMN "public"."sys_menu"."component" IS '组件路径'; +COMMENT ON COLUMN "public"."sys_menu"."query" IS '路由参数'; +COMMENT ON COLUMN "public"."sys_menu"."is_frame" IS '是否为外链(0是 1否)'; +COMMENT ON COLUMN "public"."sys_menu"."is_cache" IS '是否缓存(0缓存 1不缓存)'; +COMMENT ON COLUMN "public"."sys_menu"."menu_type" IS '菜单类型(M目录 C菜单 F按钮)'; +COMMENT ON COLUMN "public"."sys_menu"."visible" IS '菜单状态(0显示 1隐藏)'; +COMMENT ON COLUMN "public"."sys_menu"."status" IS '菜单状态(0正常 1停用)'; +COMMENT ON COLUMN "public"."sys_menu"."perms" IS '权限标识'; +COMMENT ON COLUMN "public"."sys_menu"."icon" IS '菜单图标'; +COMMENT ON COLUMN "public"."sys_menu"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."sys_menu"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."sys_menu"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."sys_menu"."update_time" IS '更新时间'; +COMMENT ON COLUMN "public"."sys_menu"."remark" IS '备注'; +COMMENT ON COLUMN "public"."sys_menu"."open_way" IS '打开方式;1:页面打开;2:弹窗打开'; +COMMENT ON COLUMN "public"."sys_menu"."plugin_id" IS '所属插件ID'; +COMMENT ON COLUMN "public"."sys_menu"."open_position" IS '打开位置;1:左边菜单;2:首页实例'; +COMMENT ON COLUMN "public"."sys_menu"."query_template" IS 'query参数模板'; +COMMENT ON COLUMN "public"."sys_menu"."plugin_theme" IS '插件主题'; +COMMENT ON COLUMN "public"."sys_menu"."menu_classify" IS '菜单分类;1:业务菜单;2:平台菜单'; +COMMENT ON COLUMN "public"."sys_menu"."menu_en_name" IS '英文名称'; +COMMENT ON TABLE "public"."sys_menu" IS '菜单权限表'; + +-- ---------------------------- +-- Table structure for sys_oper_log +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."sys_oper_log" ( + "oper_id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_sys_log_id'::regclass), + "title" varchar(50) COLLATE "pg_catalog"."default", + "business_type" int4, + "method" varchar(100) COLLATE "pg_catalog"."default", + "request_method" varchar(10) COLLATE "pg_catalog"."default", + "operator_type" int4, + "oper_name" varchar(50) COLLATE "pg_catalog"."default", + "oper_url" varchar(255) COLLATE "pg_catalog"."default", + "oper_ip" varchar(128) COLLATE "pg_catalog"."default", + "oper_location" varchar(255) COLLATE "pg_catalog"."default", + "oper_param" varchar(2000) COLLATE "pg_catalog"."default", + "json_result" varchar(2000) COLLATE "pg_catalog"."default", + "status" int4, + "error_msg" varchar(2000) COLLATE "pg_catalog"."default", + "oper_time" timestamp(6) + ) +; +COMMENT ON COLUMN "public"."sys_oper_log"."oper_id" IS '日志主键'; +COMMENT ON COLUMN "public"."sys_oper_log"."title" IS '模块标题'; +COMMENT ON COLUMN "public"."sys_oper_log"."business_type" IS '业务类型(0其它 1新增 2修改 3删除)'; +COMMENT ON COLUMN "public"."sys_oper_log"."method" IS '方法名称'; +COMMENT ON COLUMN "public"."sys_oper_log"."request_method" IS '请求方式'; +COMMENT ON COLUMN "public"."sys_oper_log"."operator_type" IS '操作类别(0其它 1后台用户 2手机端用户)'; +COMMENT ON COLUMN "public"."sys_oper_log"."oper_name" IS '操作人员'; +COMMENT ON COLUMN "public"."sys_oper_log"."oper_url" IS '请求URL'; +COMMENT ON COLUMN "public"."sys_oper_log"."oper_ip" IS '主机地址'; +COMMENT ON COLUMN "public"."sys_oper_log"."oper_location" IS '操作地点'; +COMMENT ON COLUMN "public"."sys_oper_log"."oper_param" IS '请求参数'; +COMMENT ON COLUMN "public"."sys_oper_log"."json_result" IS '返回参数'; +COMMENT ON COLUMN "public"."sys_oper_log"."status" IS '操作状态(0正常 1异常)'; +COMMENT ON COLUMN "public"."sys_oper_log"."error_msg" IS '错误消息'; +COMMENT ON COLUMN "public"."sys_oper_log"."oper_time" IS '操作时间'; +COMMENT ON TABLE "public"."sys_oper_log" IS '操作日志记录'; + +-- ---------------------------- +-- Table structure for sys_plugin_config +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."sys_plugin_config" ( + "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_sys_plugin_config_id'::regclass), + "plugin_id" varchar(100) COLLATE "pg_catalog"."default", + "config_json" varchar(1000) COLLATE "pg_catalog"."default" + ) +; +COMMENT ON COLUMN "public"."sys_plugin_config"."id" IS '主键ID'; +COMMENT ON COLUMN "public"."sys_plugin_config"."plugin_id" IS '插件ID'; +COMMENT ON COLUMN "public"."sys_plugin_config"."config_json" IS '配置参数'; +COMMENT ON TABLE "public"."sys_plugin_config" IS '插件配置结构表'; + +-- ---------------------------- +-- Table structure for sys_plugin_config_data +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."sys_plugin_config_data" ( + "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_sys_plugin_config_data_id'::regclass), + "plugin_id" varchar(100) COLLATE "pg_catalog"."default", + "config_data" varchar(1000) COLLATE "pg_catalog"."default" + ) +; +COMMENT ON COLUMN "public"."sys_plugin_config_data"."id" IS '主键ID'; +COMMENT ON COLUMN "public"."sys_plugin_config_data"."plugin_id" IS '插件ID'; +COMMENT ON COLUMN "public"."sys_plugin_config_data"."config_data" IS '配置数据'; +COMMENT ON TABLE "public"."sys_plugin_config_data" IS '插件配置数据'; + +-- ---------------------------- +-- Table structure for sys_plugins +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."sys_plugins" ( + "id" int4 NOT NULL PRIMARY KEY DEFAULT nextval('sq_sys_plugin_id'::regclass), + "plugin_id" varchar(100) COLLATE "pg_catalog"."default", + "bootstrap_class" varchar(255) COLLATE "pg_catalog"."default", + "plugin_desc" text COLLATE "pg_catalog"."default", + "logo_path" varchar(255) COLLATE "pg_catalog"."default", + "plugin_type" int4, + "plugin_version" varchar(50) COLLATE "pg_catalog"."default", + "plugin_provider" varchar(50) COLLATE "pg_catalog"."default", + "plugin_status" int2 DEFAULT 1, + "is_need_configured" int2, + "theme" varchar(50) COLLATE "pg_catalog"."default" + ) +; +COMMENT ON COLUMN "public"."sys_plugins"."id" IS '主键ID'; +COMMENT ON COLUMN "public"."sys_plugins"."plugin_id" IS '插件ID'; +COMMENT ON COLUMN "public"."sys_plugins"."bootstrap_class" IS '启动类'; +COMMENT ON COLUMN "public"."sys_plugins"."plugin_desc" IS '插件描述'; +COMMENT ON COLUMN "public"."sys_plugins"."logo_path" IS '插件Logo路径'; +COMMENT ON COLUMN "public"."sys_plugins"."plugin_type" IS '插件类别'; +COMMENT ON COLUMN "public"."sys_plugins"."plugin_version" IS '插件版本'; +COMMENT ON COLUMN "public"."sys_plugins"."plugin_provider" IS '插件提供者'; +COMMENT ON COLUMN "public"."sys_plugins"."plugin_status" IS '插件状态:1:启动;2:停用'; +COMMENT ON COLUMN "public"."sys_plugins"."is_need_configured" IS '是否需要配置数据'; +COMMENT ON COLUMN "public"."sys_plugins"."theme" IS '主题'; + +-- ---------------------------- +-- Table structure for sys_plugins_repository +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."sys_plugins_repository" ( + "id" BIGSERIAL NOT NULL PRIMARY KEY, + "plugin_id" character varying(100), + "is_downloaded" bigint DEFAULT 0, + "plugin_version" varchar(50), + "download_url" character varying(255) UNIQUE, + "plugin_desc" text, + "plugin_desc_en" text + ) +; +COMMENT ON COLUMN "sys_plugins_repository"."id" IS '主键ID'; +COMMENT ON COLUMN "sys_plugins_repository"."plugin_id" IS '插件ID'; +COMMENT ON COLUMN "sys_plugins_repository"."is_downloaded" IS '插件是否已下载:0:未下载;1:已下载'; +COMMENT ON COLUMN "sys_plugins_repository"."plugin_version" IS '插件版本'; +COMMENT ON COLUMN "sys_plugins_repository"."download_url" IS '插件下载地址'; +COMMENT ON COLUMN "sys_plugins_repository"."plugin_desc" IS '插件描述'; +COMMENT ON COLUMN "sys_plugins_repository"."plugin_desc_en" IS '插件英文描述'; + +--pluginsURL-(7.0.0-RC2) +INSERT INTO public.sys_plugins_repository (plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES ('alert-monitor', 0, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/alert-monitor-7.0.0-RC2-repackage.jar', '告警插件', 'Alarm Plugin') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO public.sys_plugins_repository (plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES ('webds-plugin', 1, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/webds-plugin-7.0.0-RC2-repackage.jar', '业务开发', 'Data Studio Plugin') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO public.sys_plugins_repository (plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES ('observability-log-search', 0, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/observability-log-search-7.0.0-RC2-repackage.jar', '日志检索插件', 'Log Retrieval Plugin') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO public.sys_plugins_repository (plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES ('observability-instance', 0, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/observability-instance-7.0.0-RC2-repackage.jar', '实例监控插件', 'Instance Monitoring Plugin') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO public.sys_plugins_repository (plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES ('data-migration', 0, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/data-migration-7.0.0-RC2-repackage.jar', '数据迁移插件', 'Data Migration Plugin') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO public.sys_plugins_repository (plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES ('base-ops', 1, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/base-ops-7.0.0-RC2-repackage.jar', '基础运维插件', 'Basic Operation Plugin') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO public.sys_plugins_repository (plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES ('observability-sql-diagnosis', 0, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/observability-sql-diagnosis-7.0.0-RC2-repackage.jar', '智能诊断插件', 'Intelligent Diagnosis Plugin') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO public.sys_plugins_repository (plugin_id, is_downloaded, plugin_version, download_url, plugin_desc, plugin_desc_en) +VALUES ('compatibility-assessment', 0, '7.0.0-RC2', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/Datakit/visualtool-plugin/compatibility-assessment-7.0.0-RC2-repackage.jar', 'openGauss兼容性评估工具', 'The openGauss compatibility evaluation tool') ON DUPLICATE KEY +UPDATE NOTHING; + +DELETE FROM sys_plugins_repository WHERE plugin_id IN ('oauth-login', 'MetaTune', 'monitor-tools'); + +-- ---------------------------- +-- Table structure for sys_role +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."sys_role" ( + "role_id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_sys_role_id'::regclass), + "role_name" varchar(30) COLLATE "pg_catalog"."default" NOT NULL, + "role_key" varchar(100) COLLATE "pg_catalog"."default", + "role_sort" int4, + "data_scope" char(1) COLLATE "pg_catalog"."default", + "menu_check_strictly" int2 DEFAULT 1, + "dept_check_strictly" int2, + "status" char(1) COLLATE "pg_catalog"."default" NOT NULL DEFAULT 0, + "del_flag" char(1) COLLATE "pg_catalog"."default" DEFAULT 0, + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "remark" varchar(500) COLLATE "pg_catalog"."default" + ) +; +COMMENT ON COLUMN "public"."sys_role"."role_id" IS '角色ID'; +COMMENT ON COLUMN "public"."sys_role"."role_name" IS '角色名称'; +COMMENT ON COLUMN "public"."sys_role"."role_key" IS '角色权限字符串'; +COMMENT ON COLUMN "public"."sys_role"."role_sort" IS '显示顺序'; +COMMENT ON COLUMN "public"."sys_role"."data_scope" IS '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)'; +COMMENT ON COLUMN "public"."sys_role"."menu_check_strictly" IS '菜单树选择项是否关联显示'; +COMMENT ON COLUMN "public"."sys_role"."dept_check_strictly" IS '医院树选择项是否关联显示'; +COMMENT ON COLUMN "public"."sys_role"."status" IS '角色状态(0正常 1停用)'; +COMMENT ON COLUMN "public"."sys_role"."del_flag" IS '删除标志(0代表存在 2代表删除)'; +COMMENT ON COLUMN "public"."sys_role"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."sys_role"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."sys_role"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."sys_role"."update_time" IS '更新时间'; +COMMENT ON COLUMN "public"."sys_role"."remark" IS '备注'; +COMMENT ON TABLE "public"."sys_role" IS '角色信息表'; + +-- ---------------------------- +-- Table structure for sys_role_menu +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."sys_role_menu" ( + "role_id" int8 NOT NULL, + "menu_id" int8 NOT NULL +) +; +COMMENT ON COLUMN "public"."sys_role_menu"."role_id" IS '角色ID'; +COMMENT ON COLUMN "public"."sys_role_menu"."menu_id" IS '菜单ID'; +COMMENT ON TABLE "public"."sys_role_menu" IS '角色和菜单关联表'; + +-- ---------------------------- +-- Table structure for sys_user +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."sys_user" ( + "user_id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_sys_user_id'::regclass), + "user_name" varchar(30) COLLATE "pg_catalog"."default" NOT NULL, + "nick_name" varchar(30) COLLATE "pg_catalog"."default" NOT NULL, + "user_type" varchar(2) COLLATE "pg_catalog"."default", + "email" varchar(50) COLLATE "pg_catalog"."default", + "phonenumber" varchar(11) COLLATE "pg_catalog"."default", + "sex" char(1) COLLATE "pg_catalog"."default", + "avatar" varchar(100) COLLATE "pg_catalog"."default", + "password" varchar(100) COLLATE "pg_catalog"."default", + "status" char(1) COLLATE "pg_catalog"."default", + "del_flag" char(1) COLLATE "pg_catalog"."default" DEFAULT 0, + "login_ip" varchar(128) COLLATE "pg_catalog"."default", + "login_date" timestamp(6), + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "remark" varchar(500) COLLATE "pg_catalog"."default" + ) +; +COMMENT ON COLUMN "public"."sys_user"."user_id" IS '用户ID'; +COMMENT ON COLUMN "public"."sys_user"."user_name" IS '用户账号'; +COMMENT ON COLUMN "public"."sys_user"."nick_name" IS '用户昵称'; +COMMENT ON COLUMN "public"."sys_user"."user_type" IS '用户类型(00系统用户)'; +COMMENT ON COLUMN "public"."sys_user"."email" IS '用户邮箱'; +COMMENT ON COLUMN "public"."sys_user"."phonenumber" IS '手机号码'; +COMMENT ON COLUMN "public"."sys_user"."sex" IS '用户性别(0男 1女 2未知)'; +COMMENT ON COLUMN "public"."sys_user"."avatar" IS '头像地址'; +COMMENT ON COLUMN "public"."sys_user"."password" IS '密码'; +COMMENT ON COLUMN "public"."sys_user"."status" IS '帐号状态(0正常 1停用)'; +COMMENT ON COLUMN "public"."sys_user"."del_flag" IS '删除标志(0代表存在 2代表删除)'; +COMMENT ON COLUMN "public"."sys_user"."login_ip" IS '最后登录IP'; +COMMENT ON COLUMN "public"."sys_user"."login_date" IS '最后登录时间'; +COMMENT ON COLUMN "public"."sys_user"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."sys_user"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."sys_user"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."sys_user"."update_time" IS '更新时间'; +COMMENT ON COLUMN "public"."sys_user"."remark" IS '备注'; +COMMENT ON TABLE "public"."sys_user" IS '用户信息表'; + +-- ---------------------------- +-- Table structure for sys_user_role +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."sys_user_role" ( + "user_id" int8 NOT NULL, + "role_id" int8 NOT NULL +) +; +COMMENT ON COLUMN "public"."sys_user_role"."user_id" IS '用户ID'; +COMMENT ON COLUMN "public"."sys_user_role"."role_id" IS '角色ID'; +COMMENT ON TABLE "public"."sys_user_role" IS '用户和角色关联表'; + +-- ---------------------------- +-- Table structure for sys_white_list +-- ---------------------------- + +CREATE TABLE IF NOT EXISTS "public"."sys_white_list" ( + "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_sys_white_list_id'::regclass), + "title" varchar(255) COLLATE "pg_catalog"."default", + "ip_list" varchar(800) COLLATE "pg_catalog"."default", + "create_time" timestamp(6) + ) +; +COMMENT +ON COLUMN "public"."sys_white_list"."id" IS '主键ID'; +COMMENT +ON COLUMN "public"."sys_white_list"."title" IS '白名单名称'; +COMMENT +ON COLUMN "public"."sys_white_list"."ip_list" IS 'ip列表,逗号隔开'; +COMMENT +ON COLUMN "public"."sys_white_list"."create_time" IS '创建时间'; + + +CREATE TABLE IF NOT EXISTS "public"."ops_package_manager"( + "package_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "os" varchar(255) COLLATE "pg_catalog"."default", + "cpu_arch" varchar(255) COLLATE "pg_catalog"."default", + "package_version" varchar(255) COLLATE "pg_catalog"."default", + "package_version_num" varchar(255) COLLATE "pg_catalog"."default", + "package_url" varchar(1024) COLLATE "pg_catalog"."default", + "remark" varchar(255) COLLATE "pg_catalog"."default", + "source" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ); + + +-- ---------------------------- +-- Table structure for ops_cluster_operate_log +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."ops_cluster_operate_log" ( + "operate_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "cluster_id" varchar(255) COLLATE "pg_catalog"."default", + "cluster_node_id" varchar(255) COLLATE "pg_catalog"."default", + "operate_type" varchar(255) COLLATE "pg_catalog"."default", + "operate_log" text COLLATE "pg_catalog"."default", + "operate_time" timestamp(6), + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ) +; + +COMMENT ON COLUMN "public"."ops_cluster_operate_log"."operate_id" IS '操作记录Id'; +COMMENT ON COLUMN "public"."ops_cluster_operate_log"."cluster_id" IS '集群ID'; +COMMENT ON COLUMN "public"."ops_cluster_operate_log"."cluster_node_id" IS '集群节点ID'; +COMMENT ON COLUMN "public"."ops_cluster_operate_log"."operate_type" IS '操作类型'; +COMMENT ON COLUMN "public"."ops_cluster_operate_log"."operate_log" IS '操作记录日志'; +COMMENT ON COLUMN "public"."ops_cluster_operate_log"."operate_time" IS '操作时间'; +COMMENT ON COLUMN "public"."ops_cluster_operate_log"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."ops_cluster_operate_log"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."ops_cluster_operate_log"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."ops_cluster_operate_log"."update_time" IS '更新时间'; +COMMENT ON TABLE "public"."ops_cluster_operate_log" IS '集群安装任务操作日志'; + +CREATE TABLE IF NOT EXISTS "public"."sys_task" ( + "id" int8 NOT NULL DEFAULT nextval('sq_sys_task_id'::regclass), + "task_name" varchar(255) COLLATE "pg_catalog"."default", + "task_type" int4, + "exec_status" int4, + "exec_params" varchar(512) COLLATE "pg_catalog"."default", + "exec_progress" float4, + "exec_host_id" int8, + "create_time" timestamp(6), + "finish_time" timestamp(6), + "exec_time" timestamp(6), + "plugin_id" varchar(100) COLLATE "pg_catalog"."default", + "sub_task_count" int8, + CONSTRAINT "sys_task_pkey" PRIMARY KEY ("id") +); + +COMMENT ON COLUMN "public"."sys_task"."id" IS '主键ID'; + +COMMENT ON COLUMN "public"."sys_task"."task_name" IS '任务名称'; + +COMMENT ON COLUMN "public"."sys_task"."task_type" IS '操作类别(1数据迁移)'; + +COMMENT ON COLUMN "public"."sys_task"."exec_status" IS '执行状态(0:未执行;1:执行中;2:已完成;3:执行失败)'; + +COMMENT ON COLUMN "public"."sys_task"."exec_params" IS '执行参数'; + +COMMENT ON COLUMN "public"."sys_task"."exec_progress" IS '执行进度'; + +COMMENT ON COLUMN "public"."sys_task"."exec_host_id" IS '执行物理机ID'; + +COMMENT ON COLUMN "public"."sys_task"."create_time" IS '创建时间'; + +COMMENT ON COLUMN "public"."sys_task"."finish_time" IS '完成时间'; + +COMMENT ON COLUMN "public"."sys_task"."exec_time" IS '执行时间'; + +COMMENT ON COLUMN "public"."sys_task"."plugin_id" IS '插件ID'; + +COMMENT ON COLUMN "public"."sys_task"."sub_task_count" IS '子任务数量'; + +COMMENT ON TABLE "public"."sys_task" IS '平台任务表'; + + +CREATE OR REPLACE FUNCTION add_pkg_name() RETURNS integer AS ' +BEGIN +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_package_manager'' AND COLUMN_NAME = ''name'' ) = 0 +THEN +ALTER TABLE ops_package_manager ADD COLUMN name VARCHAR(255); +END IF; +RETURN 0; + END;' + LANGUAGE plpgsql; + +SELECT add_pkg_name(); + +DROP FUNCTION add_pkg_name; + +CREATE OR REPLACE FUNCTION add_pkg_source() RETURNS integer AS ' + BEGIN + IF + ( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_package_manager'' AND COLUMN_NAME = ''source'' ) = 0 + THEN + ALTER TABLE ops_package_manager ADD COLUMN source VARCHAR(255); + END IF; + RETURN 0; + END;' + LANGUAGE plpgsql; + +SELECT add_pkg_source(); + +DROP FUNCTION add_pkg_source; + +CREATE +OR REPLACE FUNCTION add_field_ops_package_manager() RETURNS integer AS ' +BEGIN +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_package_manager'' AND (COLUMN_NAME = ''package_path'' OR COLUMN_NAME = ''type'') ) = 0 +THEN +ALTER TABLE ops_package_manager ADD COLUMN "package_path" text; +ALTER TABLE ops_package_manager ADD COLUMN "type" varchar(255); +UPDATE ops_package_manager set type = ''openGauss''; +END IF; +RETURN 0; + END;' +LANGUAGE plpgsql; + +SELECT add_field_ops_package_manager(); + +DROP FUNCTION add_field_ops_package_manager; + +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604735048472268811', 'centos', 'x86_64', 'MINIMAL_LIST', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/x86/openGauss-3.1.1-CentOS-64bit.tar.bz2', NULL, + 'admin', '2022-12-19 15:07:04.749', 'admin', '2022-12-19 15:07:04.749', 'openGauss', 'offical_3.1.1_mini_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1605498723119550126', 'centos', 'x86_64', 'ENTERPRISE', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/x86/openGauss-3.1.1-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2022-12-21 17:41:38.992', 'admin', '2022-12-21 17:41:38.992', 'openGauss', 'offical_3.1.1_enterprise_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1605498799611072134', 'centos', 'x86_64', 'LITE', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/x86/openGauss-Lite-3.1.1-CentOS-x86_64.tar.gz', NULL, + 'admin', '2022-12-21 17:41:57.21', 'admin', '2022-12-21 17:41:57.21', 'openGauss', 'offical_3.1.1_lite_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604855665214541845', 'openEuler', 'aarch64', 'ENTERPRISE', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/arm/openGauss-3.1.1-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2022-12-19 23:06:22.02', 'admin', '2022-12-19 23:06:22.02', 'openGauss', 'offical_3.1.1_enterprise_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604857993288142150', 'openEuler', 'aarch64', 'LITE', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/arm/openGauss-Lite-3.1.1-openEuler-aarch64.tar.gz', + NULL, 'admin', '2022-12-19 23:15:37.077', 'admin', '2022-12-19 23:15:37.077', 'openGauss', 'offical_3.1.1_lite_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627277907599162', 'openEuler', 'aarch64', 'MINIMAL_LIST', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/arm/openGauss-3.1.1-openEuler-64bit.tar.bz2', NULL, + 'admin', '2022-12-24 20:26:07.402', 'admin', '2022-12-24 20:26:07.402', 'openGauss', 'offical_3.1.1_mini_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627421516374177', 'openEuler', 'x86_64', 'ENTERPRISE', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/x86_openEuler/openGauss-3.1.1-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2022-12-24 20:26:41.641', 'admin', '2022-12-24 20:26:41.641', 'openGauss', 'offical_3.1.1_enterprise_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627515523309180', 'openEuler', 'x86_64', 'MINIMAL_LIST', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/x86_openEuler/openGauss-3.1.1-openEuler-64bit.tar.bz2', + NULL, 'admin', '2022-12-24 20:27:04.054', 'admin', '2022-12-24 20:27:04.054', 'openGauss', 'offical_3.1.1_mini_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627585895342192', 'openEuler', 'x86_64', 'LITE', '3.1.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.1.1/x86_openEuler/openGauss-Lite-3.1.1-openEuler-x86_64.tar.gz', + NULL, 'admin', '2022-12-24 20:27:20.832', 'admin', '2022-12-24 20:27:20.832', 'openGauss', 'offical_3.1.1_lite_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1605398723119550410', 'centos', 'x86_64', 'ENTERPRISE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86/openGauss-5.0.0-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2022-12-21 17:41:38.992', 'admin', '2022-12-21 17:41:38.992', 'openGauss', 'offical_5.0.0_enterpirse_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1605398799611072511', 'centos', 'x86_64', 'LITE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86/openGauss-Lite-5.0.0-CentOS-x86_64.tar.gz', NULL, + 'admin', '2022-12-21 17:41:57.21', 'admin', '2022-12-21 17:41:57.21', 'openGauss', 'offical_5.0.0_lite_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604335048472268012', 'centos', 'x86_64', 'MINIMAL_LIST', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86/openGauss-5.0.0-CentOS-64bit.tar.bz2', NULL, + 'admin', '2022-12-19 15:07:04.749', 'admin', '2022-12-19 15:07:04.749', 'openGauss', 'offical_5.0.0_mini_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627421516374013', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler/openGauss-5.0.0-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2022-12-24 20:26:41.641', 'admin', '2022-12-24 20:26:41.641', 'openGauss', 'offical_5.0.0_enterprise_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627515523309514', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler/openGauss-5.0.0-openEuler-64bit.tar.bz2', + NULL, 'admin', '2022-12-24 20:27:04.054', 'admin', '2022-12-24 20:27:04.054', 'openGauss', 'offical_5.0.0_mini_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627585895342015', 'openEuler', 'x86_64', 'LITE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler/openGauss-Lite-5.0.0-openEuler-x86_64.tar.gz', + NULL, 'admin', '2022-12-24 20:27:20.832', 'admin', '2022-12-24 20:27:20.832', 'openGauss', 'offical_5.0.0_lite_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604855665214541816', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/arm/openGauss-5.0.0-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2022-12-19 23:06:22.02', 'admin', '2022-12-19 23:06:22.02', 'openGauss', 'offical_5.0.0_enterprise_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604857993288142817', 'openEuler', 'aarch64', 'LITE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/arm/openGauss-Lite-5.0.0-openEuler-aarch64.tar.gz', + NULL, 'admin', '2022-12-19 23:15:37.077', 'admin', '2022-12-19 23:15:37.077', 'openGauss', 'offical_5.0.0_lite_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627277907599318', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/arm/openGauss-5.0.0-openEuler-64bit.tar.bz2', NULL, + 'admin', '2022-12-24 20:26:07.402', 'admin', '2022-12-24 20:26:07.402', 'openGauss', 'offical_5.0.0_mini_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627421516374019', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler_2203/openGauss-5.0.0-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2022-12-24 20:26:41.641', 'admin', '2022-12-24 20:26:41.641', 'openGauss', 'offical_5.0.0_enterprise_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627515523309520', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler_2203/openGauss-5.0.0-openEuler-64bit.tar.bz2', + NULL, 'admin', '2022-12-24 20:27:04.054', 'admin', '2022-12-24 20:27:04.054', 'openGauss', 'offical_5.0.0_mini_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627585895342021', 'openEuler', 'x86_64', 'LITE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler_2203/openGauss-Lite-5.0.0-openEuler-x86_64.tar.gz', + NULL, 'admin', '2022-12-24 20:27:20.832', 'admin', '2022-12-24 20:27:20.832', 'openGauss', 'offical_5.0.0_lite_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604855665214541822', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/arm_2203/openGauss-5.0.0-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2022-12-19 23:06:22.02', 'admin', '2022-12-19 23:06:22.02', 'openGauss', 'offical_5.0.0_enterprise_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1604857993288142823', 'openEuler', 'aarch64', 'LITE', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/arm_2203/openGauss-Lite-5.0.0-openEuler-aarch64.tar.gz', + NULL, 'admin', '2022-12-19 23:15:37.077', 'admin', '2022-12-19 23:15:37.077', 'openGauss', 'offical_5.0.0_lite_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1606627277907599324', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/arm_2203/openGauss-5.0.0-openEuler-64bit.tar.bz2', NULL, + 'admin', '2022-12-24 20:26:07.402', 'admin', '2022-12-24 20:26:07.402', 'openGauss', 'offical_5.0.0_mini_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813634', 'centos', 'x86_64', 'ENTERPRISE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86/openGauss-5.1.0-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2023-09-25 17:41:38.992', 'admin', '2023-09-25 17:41:38.992', 'openGauss', 'offical_5.1.0_enterpirse_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813635', 'centos', 'x86_64', 'LITE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86/openGauss-Lite-5.1.0-CentOS-x86_64.tar.gz', NULL, + 'admin', '2023-09-25 17:41:57.21', 'admin', '2023-09-25 17:41:57.21', 'openGauss', 'offical_5.1.0_lite_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813636', 'centos', 'x86_64', 'MINIMAL_LIST', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86/openGauss-5.1.0-CentOS-64bit.tar.bz2', NULL, + 'admin', '2023-09-25 15:07:04.749', 'admin', '2023-09-25 15:07:04.749', 'openGauss', 'offical_5.1.0_mini_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813637', 'openEuler', 'x86_64', 'ENTERPRISE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86_openEuler/openGauss-5.1.0-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2023-09-25 20:26:41.641', 'admin', '2023-09-25 20:26:41.641', 'openGauss', 'offical_5.1.0_enterprise_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813638', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86_openEuler/openGauss-5.1.0-openEuler-64bit.tar.bz2', + NULL, 'admin', '2023-09-25 20:27:04.054', 'admin', '2023-09-25 20:27:04.054', 'openGauss', 'offical_5.1.0_mini_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813639', 'openEuler', 'x86_64', 'LITE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86_openEuler/openGauss-Lite-5.1.0-openEuler-x86_64.tar.gz', + NULL, 'admin', '2023-09-25 20:27:20.832', 'admin', '2023-09-25 20:27:20.832', 'openGauss', 'offical_5.1.0_lite_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813640', 'openEuler', 'aarch64', 'ENTERPRISE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/arm/openGauss-5.1.0-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2023-09-25 23:06:22.02', 'admin', '2023-09-25 23:06:22.02', 'openGauss', 'offical_5.1.0_enterprise_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813641', 'openEuler', 'aarch64', 'LITE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/arm/openGauss-Lite-5.1.0-openEuler-aarch64.tar.gz', + NULL, 'admin', '2023-09-25 23:15:37.077', 'admin', '2023-09-25 23:15:37.077', 'openGauss', 'offical_5.1.0_lite_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813642', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/arm/openGauss-5.1.0-openEuler-64bit.tar.bz2', NULL, + 'admin', '2023-09-25 20:26:07.402', 'admin', '2023-09-25 20:26:07.402', 'openGauss', 'offical_5.1.0_mini_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813643', 'openEuler', 'x86_64', 'ENTERPRISE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86_openEuler_2203/openGauss-5.1.0-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2023-09-25 20:26:41.641', 'admin', '2023-09-25 20:26:41.641', 'openGauss', 'offical_5.1.0_enterprise_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813644', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86_openEuler_2203/openGauss-5.1.0-openEuler-64bit.tar.bz2', + NULL, 'admin', '2023-09-25 20:27:04.054', 'admin', '2023-09-25 20:27:04.054', 'openGauss', 'offical_5.1.0_mini_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813645', 'openEuler', 'x86_64', 'LITE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86_openEuler_2203/openGauss-Lite-5.1.0-openEuler-x86_64.tar.gz', + NULL, 'admin', '2023-09-25 20:27:20.832', 'admin', '2023-09-25 20:27:20.832', 'openGauss', 'offical_5.1.0_lite_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813646', 'openEuler', 'aarch64', 'ENTERPRISE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/arm_2203/openGauss-5.1.0-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2023-09-25 23:06:22.02', 'admin', '2023-09-25 23:06:22.02', 'openGauss', 'offical_5.1.0_enterprise_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813647', 'openEuler', 'aarch64', 'LITE', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/arm_2203/openGauss-Lite-5.1.0-openEuler-aarch64.tar.gz', + NULL, 'admin', '2023-09-25 23:15:37.077', 'admin', '2023-09-25 23:15:37.077', 'openGauss', 'offical_5.1.0_lite_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813648', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.1.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/arm_2203/openGauss-5.1.0-openEuler-64bit.tar.bz2', NULL, + 'admin', '2023-09-25 20:26:07.402', 'admin', '2023-09-25 20:26:07.402', 'openGauss', 'offical_5.1.0_mini_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813664', 'centos', 'x86_64', 'ENTERPRISE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86/openGauss-6.0.0-RC1-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2024-04-15 17:41:38.992', 'admin', '2024-04-15 17:41:38.992', 'openGauss', 'offical_6.0.0-RC1_enterpirse_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813665', 'centos', 'x86_64', 'LITE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86/openGauss-Lite-6.0.0-RC1-CentOS-x86_64.tar.gz', NULL, + 'admin', '2024-04-15 17:41:57.21', 'admin', '2024-04-15 17:41:57.21', 'openGauss', 'offical_6.0.0-RC1_lite_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813666', 'centos', 'x86_64', 'MINIMAL_LIST', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86/openGauss-6.0.0-RC1-CentOS-64bit.tar.bz2', NULL, + 'admin', '2024-04-15 15:07:04.749', 'admin', '2024-04-15 15:07:04.749', 'openGauss', 'offical_6.0.0-RC1_mini_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813667', 'openEuler', 'x86_64', 'ENTERPRISE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86_openEuler/openGauss-6.0.0-RC1-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-04-15 20:26:41.641', 'admin', '2024-04-15 20:26:41.641', 'openGauss', 'offical_6.0.0-RC1_enterprise_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813668', 'openEuler', 'x86_64', 'MINIMAL_LIST', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86_openEuler/openGauss-6.0.0-RC1-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-04-15 20:27:04.054', 'admin', '2024-04-15 20:27:04.054', 'openGauss', 'offical_6.0.0-RC1_mini_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813669', 'openEuler', 'x86_64', 'LITE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86_openEuler/openGauss-Lite-6.0.0-RC1-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-04-15 20:27:20.832', 'admin', '2024-04-15 20:27:20.832', 'openGauss', 'offical_6.0.0-RC1_lite_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813670', 'openEuler', 'aarch64', 'ENTERPRISE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/arm/openGauss-6.0.0-RC1-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-04-15 23:06:22.02', 'admin', '2024-04-15 23:06:22.02', 'openGauss', 'offical_6.0.0-RC1_enterprise_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813671', 'openEuler', 'aarch64', 'LITE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/arm/openGauss-Lite-6.0.0-RC1-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-04-15 23:15:37.077', 'admin', '2024-04-15 23:15:37.077', 'openGauss', 'offical_6.0.0-RC1_lite_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813672', 'openEuler', 'aarch64', 'MINIMAL_LIST', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/arm/openGauss-6.0.0-RC1-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-04-15 20:26:07.402', 'admin', '2024-04-15 20:26:07.402', 'openGauss', 'offical_6.0.0-RC1_mini_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813673', 'openEuler', 'x86_64', 'ENTERPRISE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86_openEuler_2203/openGauss-6.0.0-RC1-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-04-15 20:26:41.641', 'admin', '2024-04-15 20:26:41.641', 'openGauss', 'offical_6.0.0-RC1_enterprise_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813674', 'openEuler', 'x86_64', 'MINIMAL_LIST', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86_openEuler_2203/openGauss-6.0.0-RC1-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-04-15 20:27:04.054', 'admin', '2024-04-15 20:27:04.054', 'openGauss', 'offical_6.0.0-RC1_mini_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813675', 'openEuler', 'x86_64', 'LITE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/x86_openEuler_2203/openGauss-Lite-6.0.0-RC1-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-04-15 20:27:20.832', 'admin', '2024-04-15 20:27:20.832', 'openGauss', 'offical_6.0.0-RC1_lite_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813676', 'openEuler', 'aarch64', 'ENTERPRISE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/arm_2203/openGauss-6.0.0-RC1-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-04-15 23:06:22.02', 'admin', '2024-04-15 23:06:22.02', 'openGauss', 'offical_6.0.0-RC1_enterprise_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813677', 'openEuler', 'aarch64', 'LITE', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/arm_2203/openGauss-Lite-6.0.0-RC1-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-04-15 23:15:37.077', 'admin', '2024-04-15 23:15:37.077', 'openGauss', 'offical_6.0.0-RC1_lite_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813678', 'openEuler', 'aarch64', 'MINIMAL_LIST', '6.0.0-RC1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0-RC1/arm_2203/openGauss-6.0.0-RC1-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-04-15 20:26:07.402', 'admin', '2024-04-15 20:26:07.402', 'openGauss', 'offical_6.0.0-RC1_mini_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813679', 'centos', 'x86_64', 'ENTERPRISE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86/openGauss-5.0.1-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 17:41:38.992', 'admin', '2024-08-29 17:41:38.992', 'openGauss', 'offical_5.0.1_enterpirse_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813680', 'centos', 'x86_64', 'LITE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86/openGauss-Lite-5.0.1-CentOS-x86_64.tar.gz', NULL, + 'admin', '2024-08-29 17:41:57.21', 'admin', '2024-08-29 17:41:57.21', 'openGauss', 'offical_5.0.1_lite_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813681', 'centos', 'x86_64', 'MINIMAL_LIST', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86/openGauss-5.0.1-CentOS-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 15:07:04.749', 'admin', '2024-08-29 15:07:04.749', 'openGauss', 'offical_5.0.1_mini_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813682', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86_openEuler/openGauss-5.0.1-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-08-29 20:26:41.641', 'admin', '2024-08-29 20:26:41.641', 'openGauss', 'offical_5.0.1_enterprise_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813683', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86_openEuler/openGauss-5.0.1-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-08-29 20:27:04.054', 'admin', '2024-08-29 20:27:04.054', 'openGauss', 'offical_5.0.1_mini_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813684', 'openEuler', 'x86_64', 'LITE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86_openEuler/openGauss-Lite-5.0.1-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-08-29 20:27:20.832', 'admin', '2024-08-29 20:27:20.832', 'openGauss', 'offical_5.0.1_lite_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813685', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/arm/openGauss-5.0.1-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 23:06:22.02', 'admin', '2024-08-29 23:06:22.02', 'openGauss', 'offical_5.0.1_enterprise_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813686', 'openEuler', 'aarch64', 'LITE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/arm/openGauss-Lite-5.0.1-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-08-29 23:15:37.077', 'admin', '2024-08-29 23:15:37.077', 'openGauss', 'offical_5.0.1_lite_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813687', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/arm/openGauss-5.0.1-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 20:26:07.402', 'admin', '2024-08-29 20:26:07.402', 'openGauss', 'offical_5.0.1_mini_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813688', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86_openEuler_2203/openGauss-5.0.1-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-08-29 20:26:41.641', 'admin', '2024-08-29 20:26:41.641', 'openGauss', 'offical_5.0.1_enterprise_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813689', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86_openEuler_2203/openGauss-5.0.1-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-08-29 20:27:04.054', 'admin', '2024-08-29 20:27:04.054', 'openGauss', 'offical_5.0.1_mini_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813690', 'openEuler', 'x86_64', 'LITE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/x86_openEuler_2203/openGauss-Lite-5.0.1-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-08-29 20:27:20.832', 'admin', '2024-08-29 20:27:20.832', 'openGauss', 'offical_5.0.1_lite_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813691', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/arm_2203/openGauss-5.0.1-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 23:06:22.02', 'admin', '2024-08-29 23:06:22.02', 'openGauss', 'offical_5.0.1_enterprise_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813692', 'openEuler', 'aarch64', 'LITE', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/arm_2203/openGauss-Lite-5.0.1-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-08-29 23:15:37.077', 'admin', '2024-08-29 23:15:37.077', 'openGauss', 'offical_5.0.1_lite_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813693', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.1', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.1/arm_2203/openGauss-5.0.1-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 20:26:07.402', 'admin', '2024-08-29 20:26:07.402', 'openGauss', 'offical_5.0.1_mini_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813694', 'centos', 'x86_64', 'ENTERPRISE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86/openGauss-5.0.2-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 17:41:38.992', 'admin', '2024-08-29 17:41:38.992', 'openGauss', 'offical_5.0.2_enterpirse_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813695', 'centos', 'x86_64', 'LITE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86/openGauss-Lite-5.0.2-CentOS-x86_64.tar.gz', NULL, + 'admin', '2024-08-29 17:41:57.21', 'admin', '2024-08-29 17:41:57.21', 'openGauss', 'offical_5.0.2_lite_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813696', 'centos', 'x86_64', 'MINIMAL_LIST', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86/openGauss-5.0.2-CentOS-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 15:07:04.749', 'admin', '2024-08-29 15:07:04.749', 'openGauss', 'offical_5.0.2_mini_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813697', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86_openEuler/openGauss-5.0.2-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-08-29 20:26:41.641', 'admin', '2024-08-29 20:26:41.641', 'openGauss', 'offical_5.0.2_enterprise_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813698', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86_openEuler/openGauss-5.0.2-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-08-29 20:27:04.054', 'admin', '2024-08-29 20:27:04.054', 'openGauss', 'offical_5.0.2_mini_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813699', 'openEuler', 'x86_64', 'LITE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86_openEuler/openGauss-Lite-5.0.2-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-08-29 20:27:20.832', 'admin', '2024-08-29 20:27:20.832', 'openGauss', 'offical_5.0.2_lite_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813700', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/arm/openGauss-5.0.2-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 23:06:22.02', 'admin', '2024-08-29 23:06:22.02', 'openGauss', 'offical_5.0.2_enterprise_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813701', 'openEuler', 'aarch64', 'LITE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/arm/openGauss-Lite-5.0.2-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-08-29 23:15:37.077', 'admin', '2024-08-29 23:15:37.077', 'openGauss', 'offical_5.0.2_lite_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813702', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/arm/openGauss-5.0.2-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 20:26:07.402', 'admin', '2024-08-29 20:26:07.402', 'openGauss', 'offical_5.0.2_mini_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813703', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86_openEuler_2203/openGauss-5.0.2-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-08-29 20:26:41.641', 'admin', '2024-08-29 20:26:41.641', 'openGauss', 'offical_5.0.2_enterprise_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813704', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86_openEuler_2203/openGauss-5.0.2-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-08-29 20:27:04.054', 'admin', '2024-08-29 20:27:04.054', 'openGauss', 'offical_5.0.2_mini_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813705', 'openEuler', 'x86_64', 'LITE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86_openEuler_2203/openGauss-Lite-5.0.2-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-08-29 20:27:20.832', 'admin', '2024-08-29 20:27:20.832', 'openGauss', 'offical_5.0.2_lite_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813706', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/arm_2203/openGauss-5.0.2-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 23:06:22.02', 'admin', '2024-08-29 23:06:22.02', 'openGauss', 'offical_5.0.2_enterprise_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813707', 'openEuler', 'aarch64', 'LITE', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/arm_2203/openGauss-Lite-5.0.2-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-08-29 23:15:37.077', 'admin', '2024-08-29 23:15:37.077', 'openGauss', 'offical_5.0.2_lite_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813708', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.2', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/arm_2203/openGauss-5.0.2-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 20:26:07.402', 'admin', '2024-08-29 20:26:07.402', 'openGauss', 'offical_5.0.2_mini_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; + +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813709', 'centos', 'x86_64', 'ENTERPRISE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86/openGauss-5.0.3-CentOS-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 17:41:38.992', 'admin', '2024-08-29 17:41:38.992', 'openGauss', 'offical_5.0.3_enterpirse_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813710', 'centos', 'x86_64', 'LITE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86/openGauss-Lite-5.0.3-CentOS-x86_64.tar.gz', NULL, + 'admin', '2024-08-29 17:41:57.21', 'admin', '2024-08-29 17:41:57.21', 'openGauss', 'offical_5.0.3_lite_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813711', 'centos', 'x86_64', 'MINIMAL_LIST', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86/openGauss-5.0.3-CentOS-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 15:07:04.749', 'admin', '2024-08-29 15:07:04.749', 'openGauss', 'offical_5.0.3_mini_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813712', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86_openEuler/openGauss-5.0.3-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-08-29 20:26:41.641', 'admin', '2024-08-29 20:26:41.641', 'openGauss', 'offical_5.0.3_enterprise_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813713', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86_openEuler/openGauss-5.0.3-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-08-29 20:27:04.054', 'admin', '2024-08-29 20:27:04.054', 'openGauss', 'offical_5.0.3_mini_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813714', 'openEuler', 'x86_64', 'LITE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86_openEuler/openGauss-Lite-5.0.3-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-08-29 20:27:20.832', 'admin', '2024-08-29 20:27:20.832', 'openGauss', 'offical_5.0.3_lite_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813715', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/arm/openGauss-5.0.3-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 23:06:22.02', 'admin', '2024-08-29 23:06:22.02', 'openGauss', 'offical_5.0.3_enterprise_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813716', 'openEuler', 'aarch64', 'LITE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/arm/openGauss-Lite-5.0.3-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-08-29 23:15:37.077', 'admin', '2024-08-29 23:15:37.077', 'openGauss', 'offical_5.0.3_lite_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813717', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/arm/openGauss-5.0.3-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 20:26:07.402', 'admin', '2024-08-29 20:26:07.402', 'openGauss', 'offical_5.0.3_mini_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813718', 'openEuler', 'x86_64', 'ENTERPRISE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86_openEuler_2203/openGauss-5.0.3-openEuler-64bit-all.tar.gz', + NULL, 'admin', '2024-08-29 20:26:41.641', 'admin', '2024-08-29 20:26:41.641', 'openGauss', 'offical_5.0.3_enterprise_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813719', 'openEuler', 'x86_64', 'MINIMAL_LIST', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86_openEuler_2203/openGauss-5.0.3-openEuler-64bit.tar.bz2', + NULL, 'admin', '2024-08-29 20:27:04.054', 'admin', '2024-08-29 20:27:04.054', 'openGauss', 'offical_5.0.3_mini_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813720', 'openEuler', 'x86_64', 'LITE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/x86_openEuler_2203/openGauss-Lite-5.0.3-openEuler-x86_64.tar.gz', + NULL, 'admin', '2024-08-29 20:27:20.832', 'admin', '2024-08-29 20:27:20.832', 'openGauss', 'offical_5.0.3_lite_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813721', 'openEuler', 'aarch64', 'ENTERPRISE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/arm_2203/openGauss-5.0.3-openEuler-64bit-all.tar.gz', NULL, + 'admin', '2024-08-29 23:06:22.02', 'admin', '2024-08-29 23:06:22.02', 'openGauss', 'offical_5.0.3_enterprise_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813722', 'openEuler', 'aarch64', 'LITE', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/arm_2203/openGauss-Lite-5.0.3-openEuler-aarch64.tar.gz', + NULL, 'admin', '2024-08-29 23:15:37.077', 'admin', '2024-08-29 23:15:37.077', 'openGauss', 'offical_5.0.3_lite_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813723', 'openEuler', 'aarch64', 'MINIMAL_LIST', '5.0.3', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.3/arm_2203/openGauss-5.0.3-openEuler-64bit.tar.bz2', NULL, + 'admin', '2024-08-29 20:26:07.402', 'admin', '2024-08-29 20:26:07.402', 'openGauss', 'offical_5.0.3_mini_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; + +CREATE OR REPLACE FUNCTION update_ops_package_structure() RETURNS integer AS 'DECLARE + column_exists INT; +BEGIN + + SELECT COUNT(*) INTO column_exists FROM information_schema.columns WHERE table_schema = ''public'' AND table_name = ''ops_package_manager'' AND column_name = ''os_version''; + + IF column_exists = 0 THEN + ALTER TABLE ops_package_manager ADD os_version VARCHAR(20) AFTER os; + UPDATE ops_package_manager SET os_version = ''7'' WHERE (os_version IS NULL OR os_version = '''') AND os=''centos'' and package_url LIKE ''%CentOS%''; + UPDATE ops_package_manager SET os_version = ''22.03'' WHERE (os_version IS NULL OR os_version = '''') AND os = ''openEuler'' AND package_url LIKE ''%2203%''; + UPDATE ops_package_manager SET os_version = ''20.03'' WHERE (os_version IS NULL OR os_version = '''') AND os = ''openEuler'' AND package_url not LIKE ''%2203%''; + ELSE + + UPDATE ops_package_manager SET os_version = ''7'' WHERE (os_version IS NULL OR os_version = '''') AND os=''centos'' and package_url LIKE ''%CentOS%''; + UPDATE ops_package_manager SET os_version = ''22.03'' WHERE (os_version IS NULL OR os_version = '''') AND os = ''openEuler'' AND package_url LIKE ''%2203%''; + UPDATE ops_package_manager SET os_version = ''20.03'' WHERE (os_version IS NULL OR os_version = '''') AND os = ''openEuler'' AND package_url not LIKE ''%2203%''; + END IF; +return 0; +END;' +LANGUAGE plpgsql; + +select update_ops_package_structure(); + +DROP FUNCTION update_ops_package_structure; + +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813724', 'centos', '7', 'x86_64', 'ENTERPRISE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/CentOS7/x86/openGauss-All-6.0.0-CentOS7-x86_64.tar.gz', NULL, + 'admin', '2024-09-24 17:41:38.992', 'admin', '2024-09-24 17:41:38.992', 'openGauss', 'offical_6.0.0_enterpirse_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813725', 'centos', '7', 'x86_64', 'LITE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/CentOS7/x86/openGauss-Lite-6.0.0-CentOS7-x86_64.tar.gz', NULL, + 'admin', '2024-09-24 17:41:57.21', 'admin', '2024-09-24 17:41:57.21', 'openGauss', 'offical_6.0.0_lite_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813726', 'centos', '7', 'x86_64', 'MINIMAL_LIST', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/CentOS7/x86/openGauss-Server-6.0.0-CentOS7-x86_64.tar.bz2', NULL, + 'admin', '2024-09-24 15:07:04.749', 'admin', '2024-09-24 15:07:04.749', 'openGauss', 'offical_6.0.0_mini_centos_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813727', 'openEuler', '20.03', 'x86_64', 'ENTERPRISE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler20.03/x86/openGauss-All-6.0.0-openEuler20.03-x86_64.tar.gz', + NULL, 'admin', '2024-09-24 20:26:41.641', 'admin', '2024-09-24 20:26:41.641', 'openGauss', 'offical_6.0.0_enterprise_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813728', 'openEuler', '20.03', 'x86_64', 'LITE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler20.03/x86/openGauss-Lite-6.0.0-openEuler20.03-x86_64.tar.gz', + NULL, 'admin', '2024-09-24 20:27:20.832', 'admin', '2024-09-24 20:27:20.832', 'openGauss', 'offical_6.0.0_lite_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813729', 'openEuler', '20.03', 'x86_64', 'MINIMAL_LIST', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler20.03/x86/openGauss-Server-6.0.0-openEuler20.03-x86_64.tar.bz2', + NULL, 'admin', '2024-09-24 20:27:04.054', 'admin', '2024-09-24 20:27:04.054', 'openGauss', 'offical_6.0.0_mini_openEuler2003_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813730', 'openEuler', '20.03', 'aarch64', 'ENTERPRISE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler20.03/arm/openGauss-All-6.0.0-openEuler20.03-aarch64.tar.gz', NULL, + 'admin', '2024-09-24 23:06:22.02', 'admin', '2024-09-24 23:06:22.02', 'openGauss', 'offical_6.0.0_enterprise_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813731', 'openEuler', '20.03', 'aarch64', 'LITE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler20.03/arm/openGauss-Lite-6.0.0-openEuler20.03-aarch64.tar.gz', + NULL, 'admin', '2024-09-24 23:15:37.077', 'admin', '2024-09-24 23:15:37.077', 'openGauss', 'offical_6.0.0_lite_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813732', 'openEuler', '20.03', 'aarch64', 'MINIMAL_LIST', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler20.03/arm/openGauss-Server-6.0.0-openEuler20.03-aarch64.tar.bz2', NULL, + 'admin', '2024-09-24 20:26:07.402', 'admin', '2024-09-24 20:26:07.402', 'openGauss', 'offical_6.0.0_mini_openEuler2003_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813733', 'openEuler', '22.03', 'x86_64', 'ENTERPRISE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler22.03/x86/openGauss-All-6.0.0-openEuler22.03-x86_64.tar.gz', + NULL, 'admin', '2024-09-24 20:26:41.641', 'admin', '2024-09-24 20:26:41.641', 'openGauss', 'offical_6.0.0_enterprise_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813734', 'openEuler', '22.03', 'x86_64', 'LITE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler22.03/x86/openGauss-Lite-6.0.0-openEuler22.03-x86_64.tar.gz', + NULL, 'admin', '2024-09-24 20:27:20.832', 'admin', '2024-09-24 20:27:20.832', 'openGauss', 'offical_6.0.0_lite_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813735', 'openEuler', '22.03', 'x86_64', 'MINIMAL_LIST', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler22.03/x86/openGauss-Server-6.0.0-openEuler22.03-x86_64.tar.bz2', + NULL, 'admin', '2024-09-24 20:27:04.054', 'admin', '2024-09-24 20:27:04.054', 'openGauss', 'offical_6.0.0_mini_openEuler2203_x86_64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813736', 'openEuler', '22.03', 'aarch64', 'ENTERPRISE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler22.03/arm/openGauss-All-6.0.0-openEuler22.03-aarch64.tar.gz', NULL, + 'admin', '2024-09-24 23:06:22.02', 'admin', '2024-09-24 23:06:22.02', 'openGauss', 'offical_6.0.0_enterprise_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813737', 'openEuler', '22.03', 'aarch64', 'LITE', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler22.03/arm/openGauss-Lite-6.0.0-openEuler22.03-aarch64.tar.gz', + NULL, 'admin', '2024-09-24 23:15:37.077', 'admin', '2024-09-24 23:15:37.077', 'openGauss', 'offical_6.0.0_lite_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; +INSERT INTO "public"."ops_package_manager"("package_id", "os", "os_version", "cpu_arch", "package_version", "package_version_num", + "package_url", "remark", "create_by", "create_time", "update_by", + "update_time", "type", "name") +VALUES ('1706224187103813738', 'openEuler', '22.03', 'aarch64', 'MINIMAL_LIST', '6.0.0', + 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/6.0.0/openEuler22.03/arm/openGauss-Server-6.0.0-openEuler22.03-aarch64.tar.bz2', NULL, + 'admin', '2024-09-24 20:26:07.402', 'admin', '2024-09-24 20:26:07.402', 'openGauss', 'offical_6.0.0_mini_openEuler2203_aarch64') ON DUPLICATE KEY +UPDATE NOTHING; + + +INSERT INTO "public"."ops_package_path_dict" VALUES ('01', 'CentOS','7', 'x86_64', 'LITE', 'x86', 'openGauss-Lite-%s-CentOS-x86_64.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('02', 'CentOS','7', 'x86_64', 'MINIMAL_LIST', 'x86', 'openGauss-%s-CentOS-64bit.tar.bz2', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('03', 'CentOS', '7','x86_64', 'ENTERPRISE', 'x86', 'openGauss-%s-CentOS-64bit-all.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('04', 'openEuler','20.03', 'aarch64', 'LITE', 'arm', 'openGauss-Lite-%s-openEuler-aarch64.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('05', 'openEuler', '20.03','aarch64', 'MINIMAL_LIST', 'arm', 'openGauss-%s-openEuler-64bit.tar.bz2','3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('06', 'openEuler','20.03', 'aarch64', 'ENTERPRISE', 'arm', 'openGauss-%s-openEuler-64bit-all.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('07', 'openEuler','20.03', 'x86_64', 'LITE', 'x86_openEuler', 'openGauss-Lite-%s-openEuler-aarch64.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('08', 'openEuler','20.03', 'x86_64', 'MINIMAL_LIST', 'x86_openEuler', 'openGauss-%s-openEuler-64bit.tar.bz2', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('09', 'openEuler', '20.03','x86_64', 'ENTERPRISE', 'x86_openEuler', 'openGauss-%s-openEuler-64bit-all.tar.gz','3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('10', 'openEuler','22.03', 'aarch64', 'LITE', 'arm_2203', 'openGauss-Lite-%s-openEuler-aarch64.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('11', 'openEuler', '22.03','aarch64', 'MINIMAL_LIST', 'arm_2203', 'openGauss-%s-openEuler-64bit.tar.bz2','3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('12', 'openEuler','22.03', 'aarch64', 'ENTERPRISE', 'arm_2203', 'openGauss-%s-openEuler-64bit-all.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('13', 'openEuler','22.03', 'x86_64', 'LITE', 'x86_openEuler_2203', 'openGauss-Lite-%s-openEuler-aarch64.tar.gz', '3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1',NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('14', 'openEuler','22.03', 'x86_64', 'MINIMAL_LIST', 'x86_openEuler_2203', 'openGauss-%s-openEuler-64bit.tar.bz2','3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('15', 'openEuler','22.03', 'x86_64', 'ENTERPRISE', 'x86_openEuler_2203', 'openGauss-%s-openEuler-64bit-all.tar.gz','3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('16', 'KYLIN', '','x86_64', 'ENTERPRISE', 'x86', 'openGauss-%s-openEuler-64bit-all.tar.gz','3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1' ,NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('17', 'CentOS', '7','x86_64', 'LITE', 'CentOS7/x86', 'openGauss-Lite-%s-CentOS7-x86_64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('18', 'CentOS', '7', 'x86_64', 'MINIMAL_LIST', 'CentOS7/x86', 'openGauss-Server-%s-CentOS7-x86_64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('19', 'CentOS', '7', 'x86_64', 'ENTERPRISE', 'CentOS7/x86', 'openGauss-All-%s-CentOS7-x86_64.tar.gz', '6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('20', 'openEuler', '20.03','x86_64', 'LITE', 'openEuler20.03/x86', 'openGauss-Lite-%s-openEuler20.03-x86_64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('21', 'openEuler', '20.03', 'x86_64', 'MINIMAL_LIST', 'openEuler20.03/x86', 'openGauss-Server-%s-openEuler20.03-x86_64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('22', 'openEuler', '20.03', 'x86_64', 'ENTERPRISE', 'openEuler20.03/x86', 'openGauss-All-%s-openEuler20.03-x86_64.tar.gz', '6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('23', 'openEuler', '22.03','x86_64', 'LITE', 'openEuler22.03/x86', 'openGauss-Lite-%s-openEuler22.03-x86_64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('24', 'openEuler', '22.03', 'x86_64', 'MINIMAL_LIST', 'openEuler22.03/x86', 'openGauss-Server-%s-openEuler22.03-x86_64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('25', 'openEuler', '22.03', 'x86_64', 'ENTERPRISE', 'openEuler22.03/x86', 'openGauss-All-%s-openEuler22.03-x86_64.tar.gz', '6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('26', 'openEuler', '20.03','aarch64', 'LITE', 'openEuler20.03/arm', 'openGauss-Lite-%s-openEuler20.03-aarch64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('27', 'openEuler', '20.03', 'aarch64', 'MINIMAL_LIST', 'openEuler20.03/arm', 'openGauss-Server-%s-openEuler20.03-aarch64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('28', 'openEuler', '20.03', 'aarch64', 'ENTERPRISE', 'openEuler20.03/arm', 'openGauss-All-%s-openEuler20.03-aarch64.tar.gz', '6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('29', 'openEuler', '22.03','aarch64', 'LITE', 'openEuler22.03/arm', 'openGauss-Lite-%s-openEuler22.03-aarch64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('30', 'openEuler', '22.03', 'aarch64', 'MINIMAL_LIST', 'openEuler22.03/arm', 'openGauss-Server-%s-openEuler22.03-aarch64.tar.gz','6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."ops_package_path_dict" VALUES ('31', 'openEuler', '22.03', 'aarch64', 'ENTERPRISE', 'openEuler22.03/arm', 'openGauss-All-%s-openEuler22.03-aarch64.tar.gz', '6.0.0', NULL, 'admin', now(), 'admin', now()) ON DUPLICATE KEY UPDATE NOTHING; +update "public"."ops_package_path_dict" set package_name_tmp= replace(package_name_tmp,'gz','bz2') where id>17 and version='MINIMAL_LIST'; + +CREATE +OR REPLACE FUNCTION init_data_fuc() RETURNS integer AS 'BEGIN + + IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema=''public'' and table_name=''sys_role'') AND + NOT EXISTS (select 1 from "public"."sys_role") + THEN + INSERT INTO "public"."sys_role" ("role_id", "role_name", "role_key", "role_sort", "data_scope", "menu_check_strictly", "dept_check_strictly", "status", "del_flag", "create_by", "create_time", "update_by", "update_time", "remark") VALUES (1, ''超级管理员'', ''admin'', 1, ''1'', 1, 1, ''0'', ''0'', ''admin'', ''2021-11-01 09:38:10'', NULL, NULL, ''超级管理员''); + INSERT INTO "public"."sys_role" ("role_id", "role_name", "role_key", "role_sort", "data_scope", "menu_check_strictly", "dept_check_strictly", "status", "del_flag", "create_by", "create_time", "update_by", "update_time", "remark") VALUES (2, ''普通角色'', ''common'', 2, ''2'', 1, 1, ''0'', ''1'', ''admin'', ''2021-11-01 09:38:10'', NULL, NULL, ''普通角色''); + END IF; + + IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema=''public'' and table_name=''sys_user'') AND + NOT EXISTS (select 1 from "public"."sys_user") + THEN + INSERT INTO "public"."sys_user" ("user_id", "user_name", "nick_name", "user_type", "email", "phonenumber", "sex", "avatar", "password", "status", "del_flag", "login_ip", "login_date", "create_by", "create_time", "update_by", "update_time", "remark") VALUES (1, ''admin'', ''超级管理员'', ''00'', NULL, NULL, NULL, NULL, ''$2a$10$MeXrFYhTOrDXqVDXPBDwrOnxg7NVe1ADX1qnhQe04m94VFKwq3cRy'', ''0'', ''0'', NULL, NULL, ''admin'', ''2021-11-01 09:38:09'', ''admin'', ''2022-11-23 13:58:14.047'', ''管理员''); + END IF; + + IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema=''public'' and table_name=''sys_user_role'') AND + NOT EXISTS (select 1 from "public"."sys_user_role") + THEN + INSERT INTO "public"."sys_user_role" ("user_id", "role_id") VALUES (1, 1); + END IF; + + IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema=''public'' and table_name=''sys_menu'') AND + NOT EXISTS (select 1 from "public"."sys_menu") + THEN + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (2, ''资源中心'', 0, 2, ''/resource'', NULL, NULL, 1, 0, ''M'', ''0'', ''0'', NULL, ''resource'', ''admin'', ''2022-10-10 22:16:07.030737'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, ''Resources''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (5, ''插件管理'', 0, 5, ''/plugin/manage'', ''plugin/manage/index'', NULL, 1, 0, ''C'', ''0'', ''0'', NULL, ''plugin'', ''admin'', ''2022-10-10 22:16:07.030737'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, ''Plugins''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (6, ''安全中心'', 0, 6, ''/security'', NULL, NULL, 1, 0, ''M'', ''0'', ''0'', NULL, ''security'', ''admin'', ''2022-10-10 22:16:07.030737'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, ''Security''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (7, ''日志中心'', 0, 7, ''/logs'', NULL, NULL, 1, 0, ''M'', ''0'', ''0'', NULL, ''logs'', ''admin'', ''2022-11-23 20:54:36.439'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, ''Logs''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (201, ''数据库资源'', 2, 1, ''/resource/database'', ''resource/database/index'', NULL, 1, 0, ''C'', ''0'', ''0'', NULL, NULL, ''admin'', ''2022-10-10 22:16:07.030737'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, ''Database''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (202, ''协议资源'', 2, 2, ''/resource/protocol'', ''resource/protocol/index'', NULL, 1, 0, ''C'', ''0'', ''0'', NULL, NULL, ''admin'', ''2022-10-10 22:16:07.030737'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, ''Protocol''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (203, ''物理机资源'', 2, 3, ''/resource/physical'', ''resource/physical/index'', NULL, 1, 0, ''C'', ''0'', ''0'', NULL, NULL, ''admin'', ''2022-10-10 22:16:07.030737'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, ''Host''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (204, ''AZ管理'', 2, 4, ''/resource/az'', ''resource/az/index'', NULL, 1, 0, ''C'', ''0'', ''0'', NULL, NULL, ''admin'', ''2022-10-10 22:16:07.030737'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, ''Az Manager''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (601, ''账号管理'', 6, 1, ''/security/user'', ''security/user/index'', NULL, 1, 0, ''C'', ''0'', ''0'', NULL, NULL, ''admin'', ''2022-10-10 22:16:07.030737'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, ''Accounts''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (602, ''角色与权限'', 6, 2, ''/security/role'', ''security/role/index'', NULL, 1, 0, ''C'', ''0'', ''0'', NULL, NULL, ''admin'', ''2022-10-10 22:16:07.030737'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, ''Roles''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (603, ''菜单管理'', 6, 3, ''/security/menu'', ''security/menu/index'', NULL, 1, 0, ''C'', ''1'', ''0'', NULL, NULL, ''admin'', ''2022-10-10 22:16:07.030737'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, ''Menus''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (604, ''访问白名单'', 6, 4, ''/security/whitelist'', ''security/whitelist/index'', NULL, 1, 0, ''C'', ''0'', ''0'', NULL, NULL, ''admin'', ''2022-10-10 22:16:07.030737'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, ''Whitelist''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (605, ''个人中心'', 6, 5, ''/security/usercenter'', ''security/user/ucenter/index'', NULL, 1, 0, ''C'', ''1'', ''0'', NULL, NULL, ''admin'', NULL, NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, ''User Center''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (701, ''系统日志'', 7, 1, ''/logs/sys-log'', ''logs/sys-log/index'', NULL, 1, 0, ''C'', ''0'', ''0'', NULL, NULL, ''admin'', ''2022-11-23 20:54:36.439'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, ''System Log''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (702, ''操作日志'', 7, 2, ''/logs/oper-log'', ''logs/oper-log/index'', NULL, 1, 0, ''C'', ''0'', ''0'', NULL, NULL, ''admin'', ''2022-11-23 20:54:36.439'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 2, ''Operation Log''); + INSERT INTO "public"."sys_menu" ("menu_id", "menu_name", "parent_id", "order_num", "path", "component", "query", "is_frame", "is_cache", "menu_type", "visible", "status", "perms", "icon", "create_by", "create_time", "update_by", "update_time", "remark", "open_way", "plugin_id", "open_position", "query_template", "plugin_theme", "menu_classify", "menu_en_name") VALUES (208, ''安装包管理'', 2, 8, ''/resource/package'', ''resource/package/index'', NULL, 1, 0, ''C'', ''0'', ''0'', NULL, NULL, ''admin'', ''2022-11-23 20:54:36.439'', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, ''Package Management''); + END IF; + + IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema=''public'' and table_name=''sys_log_config'') AND + NOT EXISTS (select 1 from "public"."sys_log_config") + THEN + INSERT INTO "public"."sys_log_config" ("key", "value", "id") VALUES (''log_level'', ''info'', 1); + INSERT INTO "public"."sys_log_config" ("key", "value", "id") VALUES (''log_max_file_size'', ''5mb'', 3); + INSERT INTO "public"."sys_log_config" ("key", "value", "id") VALUES (''log_total_size_cap'', ''10gb'', 4); + INSERT INTO "public"."sys_log_config" ("key", "value", "id") VALUES (''log_max_history'', ''30'', 2); + END IF; + + RETURN 0; + END;' + LANGUAGE plpgsql; + +select init_data_fuc(); +DROP FUNCTION init_data_fuc; + + +CREATE TABLE IF NOT EXISTS "public"."ops_jdbcdb_cluster_node" +("cluster_node_id" varchar(25) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "cluster_id" varchar(255) COLLATE "pg_catalog"."default", + "name" varchar(255) COLLATE "pg_catalog"."default", + "ip" varchar(255) COLLATE "pg_catalog"."default", + "port" varchar(255) COLLATE "pg_catalog"."default", + "username" varchar(255) COLLATE "pg_catalog"."default", + "password" text COLLATE "pg_catalog"."default", + "url" varchar(255) COLLATE "pg_catalog"."default", + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ); + +CREATE TABLE IF NOT EXISTS "public"."ops_jdbcdb_cluster" +( + "cluster_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "name" varchar(255) COLLATE "pg_catalog"."default", + "deploy_type" varchar(255) COLLATE "pg_catalog"."default", + "db_type" varchar(255) COLLATE "pg_catalog"."default", + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ); + +-- ---------------------------- +-- Table structure for sys_setting +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."sys_setting"( + "id" int4 NOT NULL PRIMARY KEY DEFAULT nextval('sq_sys_setting_id'::regclass), + "user_id" int8 NOT NULL, + "upload_path" text COLLATE "pg_catalog"."default" NOT NULL +); + +CREATE OR REPLACE FUNCTION add_sys_setting_field_func() RETURNS integer AS 'BEGIN +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''sys_setting'' AND COLUMN_NAME = ''portal_pkg_download_url'' ) = 0 +THEN +ALTER TABLE sys_setting ADD COLUMN portal_pkg_download_url text COLLATE "pg_catalog"."default"; +COMMENT ON COLUMN "public"."sys_setting"."portal_pkg_download_url" IS ''portal的在线下载地址''; +END IF; +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''sys_setting'' AND COLUMN_NAME = ''portal_pkg_name'' ) = 0 +THEN +ALTER TABLE sys_setting ADD COLUMN portal_pkg_name text COLLATE "pg_catalog"."default"; +COMMENT ON COLUMN "public"."sys_setting"."portal_pkg_name" IS ''portal的安装包名称''; +END IF; +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''sys_setting'' AND COLUMN_NAME = ''portal_jar_name'' ) = 0 +THEN +ALTER TABLE sys_setting ADD COLUMN portal_jar_name text COLLATE "pg_catalog"."default"; +COMMENT ON COLUMN "public"."sys_setting"."portal_jar_name" IS ''portal的jar名称''; +UPDATE "public"."sys_setting" SET portal_pkg_download_url = ''https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/''; +UPDATE "public"."sys_setting" SET portal_pkg_name = ''PortalControl-7.0.0rc3.tar.gz''; +UPDATE "public"."sys_setting" SET portal_jar_name = ''portalControl-7.0.0rc3-exec.jar''; +END IF; +RETURN 0; +END;' +LANGUAGE plpgsql; + +SELECT add_sys_setting_field_func(); + +DROP FUNCTION add_sys_setting_field_func; + + +COMMENT +ON COLUMN "public"."sys_setting"."id" IS 'ID'; +COMMENT +ON COLUMN "public"."sys_setting"."user_id" IS '关联的用户ID'; +COMMENT +ON COLUMN "public"."sys_setting"."upload_path" IS '文件上传目录'; + +-- ---------------------------- +-- Records of sys_setting +-- ---------------------------- +INSERT INTO "public"."sys_setting" VALUES (1, 1, '/ops/files/', 'https://opengauss.obs.cn-south-1.myhuaweicloud.com/latest/tools/', 'PortalControl-7.0.0rc3.tar.gz', 'portalControl-7.0.0rc3-exec.jar') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."sys_menu" VALUES (209, '创建服务器', 2, 9, '/resource/physical/createserver', 'resource/physical/components/CreateServerPage', NULL, 1, 0, 'C', '1', '0', NULL, NULL, 'admin', '2025-07-15 20:11:27.003', NULL, NULL, NULL, 1, NULL, 1, NULL, NULL, 1, 'Create Server') ON DUPLICATE KEY UPDATE NOTHING; +CREATE OR REPLACE FUNCTION add_user_field_func() RETURNS integer AS 'BEGIN +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''sys_user'' AND COLUMN_NAME = ''update_pwd'' ) = 0 +THEN +ALTER TABLE sys_user ADD COLUMN update_pwd int2 DEFAULT 0; +COMMENT ON COLUMN "public"."sys_user"."update_pwd" IS ''是否修改密码;1:是;0:否''; +END IF; +RETURN 0; +END;' +LANGUAGE plpgsql; + +SELECT add_user_field_func(); + +DROP FUNCTION add_user_field_func; + +CREATE OR REPLACE FUNCTION add_cluster_field_func() RETURNS integer AS 'BEGIN +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_cluster'' AND COLUMN_NAME = ''env_path'' ) = 0 +THEN +ALTER TABLE ops_cluster ADD COLUMN env_path varchar(255); +COMMENT ON COLUMN "public"."ops_cluster"."env_path" IS ''环境变量文件路径''; +END IF; +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_cluster'' AND COLUMN_NAME = ''xml_config_path'' ) = 0 +THEN +ALTER TABLE ops_cluster ADD COLUMN xml_config_path varchar(255); +COMMENT ON COLUMN "public"."ops_cluster"."xml_config_path" IS ''xml配置路径''; +END IF; +RETURN 0; +END;' +LANGUAGE plpgsql; + +SELECT add_cluster_field_func(); + +DROP FUNCTION add_cluster_field_func; + +CREATE OR REPLACE FUNCTION add_host_field_func() RETURNS integer AS 'BEGIN +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_host'' AND COLUMN_NAME = ''os'' ) = 0 +THEN +ALTER TABLE ops_host ADD COLUMN os varchar(255); +COMMENT ON COLUMN "public"."ops_host"."os" IS ''操作系统''; +END IF; +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_host'' AND COLUMN_NAME = ''cpu_arch'' ) = 0 +THEN +ALTER TABLE ops_host ADD COLUMN cpu_arch varchar(255); +COMMENT ON COLUMN "public"."ops_host"."cpu_arch" IS ''CPU架构''; +END IF; +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_host'' AND COLUMN_NAME = ''name'' ) = 0 +THEN +ALTER TABLE ops_host ADD COLUMN name varchar(255); +COMMENT ON COLUMN "public"."ops_host"."name" IS ''名称''; +END IF; +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_host'' AND COLUMN_NAME = ''os_build'' ) = 0 +THEN +ALTER TABLE ops_host ADD COLUMN os_build varchar(255); +COMMENT ON COLUMN "public"."ops_host"."os_build" IS ''OS Build''; +END IF; +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_host'' AND COLUMN_NAME = ''cpu_model'' ) = 0 +THEN +ALTER TABLE ops_host ADD COLUMN cpu_model varchar(255); +COMMENT ON COLUMN "public"."ops_host"."cpu_model" IS ''CPU model''; +END IF; +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_host'' AND COLUMN_NAME = ''cpu_freq'' ) = 0 +THEN +ALTER TABLE ops_host ADD COLUMN cpu_freq varchar(64); +COMMENT ON COLUMN "public"."ops_host"."cpu_freq" IS ''CPU主频''; +END IF; +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_host'' AND COLUMN_NAME = ''physical_cores'' ) = 0 +THEN +ALTER TABLE ops_host ADD COLUMN physical_cores int4; +COMMENT ON COLUMN "public"."ops_host"."physical_cores" IS ''CPU 物理核数''; +END IF; +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_host'' AND COLUMN_NAME = ''logical_cores'' ) = 0 +THEN +ALTER TABLE ops_host ADD COLUMN logical_cores int4; +COMMENT ON COLUMN "public"."ops_host"."logical_cores" IS ''CPU逻辑核数''; +END IF; +RETURN 0; +END;' +LANGUAGE plpgsql; + +SELECT add_host_field_func(); + +DROP FUNCTION add_host_field_func; + +CREATE OR REPLACE FUNCTION add_sys_plugin_desc_en_field_func() RETURNS integer AS 'BEGIN +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''sys_plugins'' AND COLUMN_NAME = ''plugin_desc_en'' ) = 0 +THEN +ALTER TABLE sys_plugins ADD COLUMN plugin_desc_en text COLLATE "pg_catalog"."default"; +COMMENT ON COLUMN "public"."sys_plugins"."plugin_desc_en" IS ''插件英文描述''; +END IF; +RETURN 0; +END;' +LANGUAGE plpgsql; + +SELECT add_sys_plugin_desc_en_field_func(); + +DROP FUNCTION add_sys_plugin_desc_en_field_func; + +delete from "public"."sys_menu" where menu_id = 202; +update "public"."sys_menu" set menu_name = '实例管理', order_num = 1 where menu_id = 201; +update "public"."sys_menu" set menu_name = '设备管理', order_num = 2 where menu_id = 203; +update "public"."sys_menu" set order_num = 4 where menu_id = 204; + +ALTER TABLE "public"."sys_oper_log" ALTER COLUMN "oper_param" type text; + +CREATE TABLE IF NOT EXISTS "public"."sys_setting"( + "id" int4 NOT NULL PRIMARY KEY DEFAULT nextval('sq_sys_setting_id'::regclass), + "user_id" int8 NOT NULL, + "upload_path" text COLLATE "pg_catalog"."default" NOT NULL + ); +COMMENT +ON COLUMN "public"."sys_setting"."id" IS 'ID'; +COMMENT +ON COLUMN "public"."sys_setting"."user_id" IS '关联的用户ID'; +COMMENT +ON COLUMN "public"."sys_setting"."upload_path" IS '文件上传目录'; + +CREATE TABLE IF NOT EXISTS "public"."sys_plugin_logo" ( + "id" int8 NOT NULL DEFAULT nextval('sq_sys_plugin_logo_id'::regclass), + "plugin_id" varchar(100) COLLATE "pg_catalog"."default", + "logo_path" varchar(255) COLLATE "pg_catalog"."default", + CONSTRAINT "tb_plugin_logo_pkey" PRIMARY KEY ("id") +); + +COMMENT ON COLUMN "public"."sys_plugin_logo"."id" IS '主键ID'; + +COMMENT ON COLUMN "public"."sys_plugin_logo"."plugin_id" IS '插件ID'; + +COMMENT ON COLUMN "public"."sys_plugin_logo"."logo_path" IS 'logo路径'; + +update "public"."sys_menu" set menu_name = '服务器管理', menu_en_name = 'server', order_num = 2 where menu_id = 203; + +CREATE OR REPLACE FUNCTION add_host_field_func() RETURNS integer AS 'BEGIN + IF + ( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''ops_host'' AND COLUMN_NAME = ''os_version'' ) = 0 + THEN + ALTER TABLE ops_host ADD COLUMN os_version varchar(255); + COMMENT ON COLUMN "public"."ops_host"."os_version" IS ''操作系统版本''; + END IF; + RETURN 0; +END;' +LANGUAGE plpgsql; + +SELECT add_host_field_func(); + +DROP FUNCTION add_host_field_func; + +CREATE TABLE IF NOT EXISTS "public"."agent_install" ( + "agent_id" varchar(100) NOT NULL PRIMARY KEY , + "agent_ip" varchar(100) COLLATE "pg_catalog"."default", + "agent_name" varchar(100) COLLATE "pg_catalog"."default", + "agent_port" int4 DEFAULT 1025, + "install_path" varchar(255) COLLATE "pg_catalog"."default", + "install_user" varchar(100) COLLATE "pg_catalog"."default", + "install_user_id" varchar(100) , + "status" varchar(100) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_time" timestamp(6) +); + +CREATE TABLE IF NOT EXISTS "public"."agent_task_template_definition" ( + "name" varchar(100) NOT NULL PRIMARY KEY , + "type" varchar(100) COLLATE "pg_catalog"."default", + "group_tag" varchar(100) COLLATE "pg_catalog"."default", + "plugins_tag" varchar(100) COLLATE "pg_catalog"."default", + "operate_obj_type" varchar(100) COLLATE "pg_catalog"."default", + "operate_obj" text COLLATE "pg_catalog"."default", + "period" varchar(100) COLLATE "pg_catalog"."default", + "collect_metric" varchar(100) COLLATE "pg_catalog"."default", + "storage_policy" varchar(100) COLLATE "pg_catalog"."default", + "keep_period" varchar(100) COLLATE "pg_catalog"."default", + "receive_api" varchar(100) COLLATE "pg_catalog"."default", + "create_time" timestamp(6) +); +COMMENT ON COLUMN "public"."agent_task_template_definition"."name" IS '主键ID 采集任务名称'; +COMMENT ON COLUMN "public"."agent_task_template_definition"."type" IS '采集任务类型 实时任务 统计历史任务'; +COMMENT ON COLUMN "public"."agent_task_template_definition"."group_tag" IS '采集任务所属分组-用户自定义标签'; +COMMENT ON COLUMN "public"."agent_task_template_definition"."plugins_tag" IS '采集任务所属插件-插件标签'; +COMMENT ON COLUMN "public"."agent_task_template_definition"."operate_obj_type" IS '采集任务操作对象类型OS,DB,OSHI,HTTP'; +COMMENT ON COLUMN "public"."agent_task_template_definition"."operate_obj" IS '采集任务操作对象类'; +COMMENT ON COLUMN "public"."agent_task_template_definition"."period" IS '采集任务周期'; +COMMENT ON COLUMN "public"."agent_task_template_definition"."collect_metric" IS '采集任务收集指标'; +COMMENT ON COLUMN "public"."agent_task_template_definition"."storage_policy" IS '采集任务收集数据存储策略'; +COMMENT ON COLUMN "public"."agent_task_template_definition"."keep_period" IS '采集任务收集数据保存周期'; +COMMENT ON COLUMN "public"."agent_task_template_definition"."receive_api" IS '采集任务收集数据接收地址'; +COMMENT ON TABLE "public"."agent_task_template_definition" IS '采集任务模版定义表'; + +INSERT INTO "public"."agent_task_template_definition" VALUES ('base_host_info', 'OSHI_FIXED_METRIC', 'datakit', NULL, 'OSHI', NULL, 'PT0S', 'base_host_info', 'CUSTOM', NULL, '/receive/fixed/host/info', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_template_definition" VALUES ('host_history_dynamic_metrics', 'OSHI_DYNAMIC_METRIC', 'datakit', NULL, 'OSHI', NULL, 'PT10S', 'host_dynamic_metrics', 'HISTORY', 'PT1H', '/receive/metrics', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_template_definition" VALUES ('opengauss_instance_monitor_history', 'DB_METRIC', 'datakit', NULL, 'DB', NULL, 'PT10S', 'opengauss_instance_monitor', 'HISTORY', 'PT1H', '/receive/metrics', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_template_definition" VALUES ('mysql_instance_monitor_history', 'DB_METRIC', 'datakit', NULL, 'DB', NULL, 'PT10S', 'mysql_instance_monitor', 'HISTORY', 'PT1H', '/receive/metrics', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_template_definition" VALUES ('opengauss_instance_memory_top10', 'DB_PIPE', 'datakit', NULL, 'DB', 'SELECT * FROM pv_session_memory_detail() ORDER BY usedsize desc limit 10;', 'PT10S', 'opengauss_instance_memory_top10', 'HISTORY', 'PT1H', '/receive/pipeline', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_template_definition" VALUES ('opengauss_sql_trace', 'DB_PIPE', 'datakit', NULL, 'HTTP', NULL, 'PT10S', 'opengauss_sql_trace', 'TREE', 'P7D', '/receive/pipeline', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_template_definition" VALUES ('opengauss_alarm_notification', 'DB_PIPE', 'datakit', NULL, 'HTTP', NULL, 'PT10S', 'opengauss_alarm_notification', 'HISTORY', 'P7D', '/receive/pipeline', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_template_definition" VALUES ('opengauss_slow_sql_query', 'DB_PIPE', 'datakit', NULL, 'DB', NULL, 'PT10S', 'opengauss_slow_sql_query', 'FINGERPRINT', 'P7D', '/receive/pipeline', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_template_definition" VALUES ('host_dynamic_metrics', 'OSHI_DYNAMIC_METRIC', 'datakit', NULL, 'OSHI', NULL, 'PT10S', 'host_dynamic_metrics', 'REAL_TIME', NULL, '/receive/metrics', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_template_definition" VALUES ('opengauss_instance_monitor', 'DB_METRIC', 'datakit', NULL, 'DB', NULL, 'PT10S', 'opengauss_instance_monitor', 'REAL_TIME', NULL, '/receive/metrics', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_template_definition" VALUES ('mysql_instance_monitor', 'DB_METRIC', 'datakit', NULL, 'DB', NULL, 'PT10S', 'mysql_instance_monitor', 'REAL_TIME', NULL, '/receive/metrics', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; + + +CREATE TABLE IF NOT EXISTS "public"."agent_task_schema_definition" ( + "id" BIGSERIAL PRIMARY KEY, + "name" varchar(100) NOT NULL COLLATE "pg_catalog"."default", + "metric" varchar(100) COLLATE "pg_catalog"."default", + "create_time" timestamp(6) + ); +COMMENT ON COLUMN "public"."agent_task_schema_definition"."name" IS '采集任务名称'; +COMMENT ON COLUMN "public"."agent_task_schema_definition"."metric" IS '采集任务绑定指标名称'; +COMMENT ON TABLE "public"."agent_task_schema_definition" IS '采集任务数据结构定义表'; + +INSERT INTO "public"."agent_task_schema_definition" VALUES (1, 'base_host_info', 'system.hostname', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (2, 'base_host_info', 'system.os.name', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (3, 'base_host_info', 'system.os.version', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (4, 'base_host_info', 'system.cpu.name', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (5, 'base_host_info', 'system.cpu.max.freq', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (6, 'base_host_info', 'system.cpu.arch', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (7, 'base_host_info', 'system.cpu.core.count', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (8, 'host_dynamic_metrics', 'system.memory.total', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (9, 'host_dynamic_metrics', 'system.cpu.usage', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (10, 'host_dynamic_metrics', 'system.memory.available', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (11, 'host_dynamic_metrics', 'system.memory.usage', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (12, 'host_dynamic_metrics', 'system.net.bytes.received', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (13, 'host_dynamic_metrics', 'system.net.bytes.sent', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (14, 'host_dynamic_metrics', 'system.disk.total', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (15, 'host_dynamic_metrics', 'system.disk.bytes.write', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (16, 'host_dynamic_metrics', 'system.disk.bytes.read', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (17, 'host_dynamic_metrics', 'system.disk.fs.total', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (18, 'host_dynamic_metrics', 'system.disk.fs.used', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (19, 'host_dynamic_metrics', 'system.disk.fs.free', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (24, 'opengauss_instance_monitor', 'opengauss.pg.locks.count', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (25, 'opengauss_instance_monitor', 'opengauss.pg.session.count', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (26, 'opengauss_instance_monitor', 'opengauss.pg.connect.count', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (28, 'mysql_instance_monitor', 'mysql.jdbc.monitor.conn.num', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (29, 'mysql_instance_monitor', 'mysql.jdbc.monitor.qps', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (30, 'mysql_instance_monitor', 'mysql.jdbc.monitor.tps', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (31, 'mysql_instance_monitor', 'mysql.jdbc.monitor.memory.used', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (32, 'mysql_instance_monitor', 'mysql.jdbc.monitor.table.space.used', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (33, 'opengauss_instance_memory_top10', 'opengauss.pg.session.memory.top10.sessid', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (34, 'opengauss_instance_memory_top10', 'opengauss.pg.session.memory.top10.usedsize', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (35, 'opengauss_instance_memory_top10', 'opengauss.pg.session.memory.top10.freesize', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (36, 'opengauss_instance_memory_top10', 'opengauss.pg.session.memory.top10.totalsize', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_schema_definition" VALUES (37, 'opengauss_instance_memory_top10', 'opengauss.pg.session.memory.top10.contextname', '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; + +CREATE TABLE IF NOT EXISTS "public"."agent_task_metrics_definition" ( + "name" varchar(100) NOT NULL PRIMARY KEY, + "description" varchar(100) COLLATE "pg_catalog"."default", + "field_name" varchar(100) COLLATE "pg_catalog"."default", + "unit" varchar(100) COLLATE "pg_catalog"."default", + "data_type" varchar(100) COLLATE "pg_catalog"."default", + "prop" varchar(100) COLLATE "pg_catalog"."default", + "collect_cmd" varchar(1024) COLLATE "pg_catalog"."default", + "create_time" timestamp(6) +); +COMMENT ON COLUMN "public"."agent_task_metrics_definition"."name" IS '采集任务指标名称'; +COMMENT ON COLUMN "public"."agent_task_metrics_definition"."description" IS '采集任务指标描述'; +COMMENT ON COLUMN "public"."agent_task_metrics_definition"."field_name" IS '采集任务指标字段名称'; +COMMENT ON COLUMN "public"."agent_task_metrics_definition"."unit" IS '采集任务指标数据展示单位'; +COMMENT ON COLUMN "public"."agent_task_metrics_definition"."data_type" IS '采集任务指标数据类型'; +COMMENT ON COLUMN "public"."agent_task_metrics_definition"."prop" IS '采集任务指标属性'; +COMMENT ON COLUMN "public"."agent_task_metrics_definition"."collect_cmd" IS '指标采集指令-SQL'; +COMMENT ON TABLE "public"."agent_task_metrics_definition" IS '采集任务指标定义表'; + +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.hostname', '服务器Host名称', 'hostname', NULL, 'String', NULL, NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.os.name', '操作系统名称', 'osName', NULL, 'String', NULL, NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.os.version', '操作系统版本', 'osVersion', NULL, 'String', NULL, NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.cpu.name', 'cpu名称', 'cpuName', NULL, 'String', NULL, NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.cpu.max.freq', 'cpu最大主频', 'cpuMaxFreq', 'GHz', 'long', NULL, NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.cpu.arch', 'cpu架构', 'cpuArch', NULL, 'String', NULL, NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.cpu.core.count', 'cpu核数', 'cpuCoreCount', NULL, 'long', NULL, NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.memory.total', '内存总量', 'memoryTotal', 'GB', 'long', NULL, NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.cpu.usage', 'CPU使用率', 'cpuUsage', '%', 'double', NULL, NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.memory.available', '剩余内存', 'memoryAvailable', 'GB', 'long', NULL, NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.memory.usage', '内存使用率', 'memoryUsage', '%', 'double', NULL, NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.net.bytes.received', '网卡下行速率', 'netRecv', 'kb/s', 'long', 'faceName', NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.net.bytes.sent', '网卡上行速率', 'netSent', 'kb/s', 'long', 'faceName', NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.disk.total', '磁盘总大小', 'diskTotal', 'GB', 'long', 'diskName', NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.disk.bytes.write', '磁盘写入速率', 'diskWrite', 'GB/s', 'long', 'diskName', NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.disk.bytes.read', '磁盘读取速率', 'diskRead', 'GB/s', 'long', 'diskName', NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.disk.fs.total', '磁盘分区大小', 'diskFsTotal', 'GB', 'long', 'mountPoint', NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.disk.fs.used', '磁盘分区已使用大小', 'diskFsUsed', 'GB', 'long', 'mountPoint', NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('system.disk.fs.free', '磁盘分区剩余大小', 'diskFsFree', 'GB', 'long', 'mountPoint', NULL, '2025-04-16 16:34:16') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('opengauss.status.cm.state', 'openGauss集群CM状态', 'cmState', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('opengauss.status.cluster.state', 'openGauss集群Cluster状态', 'cluster_state', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('opengauss.status.node.state', 'openGauss集群节点状态', 'nodeState', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('opengauss.status.node.role', 'openGauss集群节点角色', 'nodeRole', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('opengauss.pg.locks.count', 'openGauss锁数量', 'count', NULL, 'Long', NULL, 'SELECT count(*) count FROM pg_locks', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('opengauss.pg.session.count', 'openGauss会话数量', 'count', NULL, 'Long', NULL, 'SELECT count(*) count FROM pg_stat_activity', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('opengauss.pg.connect.count', 'openGauss连接数量', 'count', NULL, 'Long', NULL, 'SELECT count(*) count FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS s', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('mysql.jdbc.monitor.role', 'mysql主备role', 'role', NULL, 'String', NULL, 'SHOW SLAVE STATUS', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('mysql.jdbc.monitor.conn.num', 'mysql连接数', 'value', NULL, 'Long', NULL, 'SHOW STATUS LIKE ''Threads_connected''', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('mysql.jdbc.monitor.qps', 'mysql QPS', 'qps', NULL, 'double', NULL, 'SELECT ROUND(CAST(questions AS UNSIGNED) / IF(CAST(uptime AS UNSIGNED) = 0, 1, CAST(uptime AS UNSIGNED)),2) AS qps FROM ( SELECT MAX(CASE WHEN VARIABLE_NAME = ''Questions'' THEN VARIABLE_VALUE END) AS questions, MAX(CASE WHEN VARIABLE_NAME = ''Uptime'' THEN VARIABLE_VALUE END) AS uptime FROM performance_schema.global_status WHERE VARIABLE_NAME IN (''Questions'', ''Uptime'')) AS status;', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('mysql.jdbc.monitor.tps', 'mysql TPS', 'tps', NULL, 'double', NULL, 'SELECT ROUND(((COALESCE(CAST(com_commit AS UNSIGNED), 0) + COALESCE(CAST(com_rollback AS UNSIGNED), 0)) / IF(COALESCE(CAST(uptime AS UNSIGNED), 0) = 0, 1, COALESCE(CAST(uptime AS UNSIGNED), 0))), 2) AS tps FROM (SELECT MAX(CASE WHEN VARIABLE_NAME = ''Com_commit'' THEN VARIABLE_VALUE END) AS com_commit,MAX(CASE WHEN VARIABLE_NAME = ''Com_rollback'' THEN VARIABLE_VALUE END) AS com_rollback,MAX(CASE WHEN VARIABLE_NAME = ''Uptime'' THEN VARIABLE_VALUE END) AS uptime FROM performance_schema.global_status WHERE VARIABLE_NAME IN (''Com_commit'', ''Com_rollback'', ''Uptime'')) AS status;', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('mysql.jdbc.monitor.memory.used', 'mysql 内存使用量', 'memoryUsed', NULL, 'double', NULL, 'SELECT (@@key_buffer_size + @@innodb_buffer_pool_size + @@innodb_log_buffer_size + @@max_connections * ( @@read_buffer_size + @@read_rnd_buffer_size + @@sort_buffer_size + @@join_buffer_size + @@binlog_cache_size + @@thread_stack + @@tmp_table_size )) AS ''memoryUsed''', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('mysql.jdbc.monitor.table.space.used', 'mysql 表空间使用量', 'tableSpaceUsed', NULL, 'double', NULL, 'SELECT SUM( table_schema_size.table_schema_size ) AS ''tableSpaceUsed'' FROM ( SELECT table_schema, SUM( data_length + index_length ) AS table_schema_size FROM information_schema.TABLES GROUP BY table_schema ) table_schema_size', '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('opengauss.pg.session.memory.top10.sessid', 'openGauss会话内存使用Top10', 'sessid', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('opengauss.pg.session.memory.top10.usedsize', 'openGauss会话内存使用Top10', 'usedsize', NULL, 'Long', NULL, NULL, '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('opengauss.pg.session.memory.top10.freesize', 'openGauss会话内存使用Top10', 'freesize', NULL, 'Long', NULL, NULL, '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('opengauss.pg.session.memory.top10.totalsize', 'openGauss会话内存使用Top10', 'totalsize', NULL, 'Long', NULL, NULL, '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."agent_task_metrics_definition" VALUES ('opengauss.pg.session.memory.top10.contextname', 'openGauss会话内存使用Top10', 'contextname', NULL, 'String', NULL, NULL, '2025-04-18 15:36:28') ON DUPLICATE KEY UPDATE NOTHING; + + +CREATE TABLE IF NOT EXISTS "public"."agent_task_instance" ( + "id" BIGSERIAL PRIMARY KEY, + "task_template_id" varchar(100) NOT NULL COLLATE "pg_catalog"."default", + "task_name" varchar(100) NOT NULL COLLATE "pg_catalog"."default", + "agent_id" varchar(100) NOT NULL COLLATE "pg_catalog"."default", + "cluster_node_id" varchar(100) COLLATE "pg_catalog"."default", + "cluster_original" varchar(100) COLLATE "pg_catalog"."default", + "task_status" varchar(100) NOT NULL COLLATE "pg_catalog"."default", + "start_time" timestamp(6), + "end_time" timestamp(6), + "error_msg" text COLLATE "pg_catalog"."default", + "task_params" text COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_time" timestamp(6) + ); +COMMENT ON COLUMN "public"."agent_task_instance"."id" IS '采集任务ID'; +COMMENT ON COLUMN "public"."agent_task_instance"."task_template_id" IS '采集任务绑定模版ID'; +COMMENT ON COLUMN "public"."agent_task_instance"."task_name" IS '采集任务名称'; +COMMENT ON COLUMN "public"."agent_task_instance"."agent_id" IS '采集任务执行AgentID'; +COMMENT ON COLUMN "public"."agent_task_instance"."cluster_node_id" IS '采集任务关联数据库集群实例ID'; +COMMENT ON COLUMN "public"."agent_task_instance"."cluster_original" IS '采集任务关联集群来源:OPS_CLUSTER;OPS_JDBC_CLUSTER'; +COMMENT ON COLUMN "public"."agent_task_instance"."task_status" IS '采集任务状态'; +COMMENT ON COLUMN "public"."agent_task_instance"."start_time" IS '采集任务开始时间'; +COMMENT ON COLUMN "public"."agent_task_instance"."end_time" IS '采集任务结束时间'; +COMMENT ON COLUMN "public"."agent_task_instance"."error_msg" IS '采集任务异常信息'; +COMMENT ON COLUMN "public"."agent_task_instance"."task_params" IS '采集任务执行参数'; +COMMENT ON TABLE "public"."agent_task_instance" IS 'Agent任务实例表'; + + +CREATE TABLE IF NOT EXISTS "public"."agent_cluster_relation" ( + "cluster_node_id" varchar(100) NOT NULL PRIMARY KEY, + "cluster_original" varchar(100) NOT NULL COLLATE "pg_catalog"."default" + ); +COMMENT ON COLUMN "public"."agent_cluster_relation"."cluster_node_id" IS '集群ID'; +COMMENT ON COLUMN "public"."agent_cluster_relation"."cluster_original" IS '采集任务关联集群来源:OPS_CLUSTER;OPS_JDBC_CLUSTER'; +COMMENT ON TABLE "public"."agent_cluster_relation" IS 'Agent任务集群关联表'; + + +CREATE TABLE IF NOT EXISTS "public"."agent_metric_real_time" ( + "multi_id" varchar(300) NOT NULL COLLATE "pg_catalog"."default", + "agent_id" int8 NOT NULL, + "cluster_node_id" varchar(100) COLLATE "pg_catalog"."default", + "metric" varchar(100) NOT NULL COLLATE "pg_catalog"."default", + "property" varchar(100) COLLATE "pg_catalog"."default", + "value" varchar(100) NOT NULL COLLATE "pg_catalog"."default", + "start_time" timestamp(6), + "end_time" timestamp(6), + "create_time" timestamp(6), + PRIMARY KEY ("agent_id","cluster_node_id","metric","property") +); +COMMENT ON COLUMN "public"."agent_metric_real_time"."multi_id" IS 'Agent 联合主键值'; +COMMENT ON COLUMN "public"."agent_metric_real_time"."agent_id" IS 'AgentID'; +COMMENT ON COLUMN "public"."agent_metric_real_time"."cluster_node_id" IS '集群ID'; +COMMENT ON COLUMN "public"."agent_metric_real_time"."metric" IS '指标ID,指标名称'; +COMMENT ON COLUMN "public"."agent_metric_real_time"."property" IS '指标属性对应值'; +COMMENT ON COLUMN "public"."agent_metric_real_time"."value" IS '指标值'; +COMMENT ON COLUMN "public"."agent_metric_real_time"."start_time" IS '指标采集开始时间'; +COMMENT ON COLUMN "public"."agent_metric_real_time"."end_time" IS '指标采集结束时间'; +COMMENT ON COLUMN "public"."agent_metric_real_time"."create_time" IS '指标保存时间'; +COMMENT ON TABLE "public"."agent_metric_real_time" IS 'Agent实时指标数据表'; + +CREATE TABLE IF NOT EXISTS "public"."agent_metric_historical" ( + "id" int8 NOT NULL PRIMARY KEY, + "task_id" int8 NOT NULL, + "agent_id" int8 NOT NULL, + "cluster_node_id" varchar(100) COLLATE "pg_catalog"."default", + "metric" varchar(100) NOT NULL COLLATE "pg_catalog"."default", + "property" varchar(100) COLLATE "pg_catalog"."default", + "value" varchar(100) NOT NULL COLLATE "pg_catalog"."default", + "start_time" timestamp(6), + "end_time" timestamp(6), + "create_time" timestamp(6) + ); +COMMENT ON COLUMN "public"."agent_metric_historical"."id" IS 'Agent 主键值'; +COMMENT ON COLUMN "public"."agent_metric_historical"."task_id" IS '数据采集任务ID'; +COMMENT ON COLUMN "public"."agent_metric_historical"."agent_id" IS 'AgentID'; +COMMENT ON COLUMN "public"."agent_metric_historical"."cluster_node_id" IS '集群ID'; +COMMENT ON COLUMN "public"."agent_metric_historical"."metric" IS '指标ID,指标名称'; +COMMENT ON COLUMN "public"."agent_metric_historical"."property" IS '指标属性对应值'; +COMMENT ON COLUMN "public"."agent_metric_historical"."value" IS '指标值'; +COMMENT ON COLUMN "public"."agent_metric_historical"."start_time" IS '指标采集开始时间'; +COMMENT ON COLUMN "public"."agent_metric_historical"."end_time" IS '指标采集结束时间'; +COMMENT ON COLUMN "public"."agent_metric_historical"."create_time" IS '指标保存时间'; +COMMENT ON TABLE "public"."agent_metric_historical" IS 'Agent历史指标数据表'; + +CREATE TABLE IF NOT EXISTS "public"."agent_task_table_relation" ( + "task_id" varchar(100) NOT NULL PRIMARY KEY, + "task_table_name" varchar(100) NOT NULL COLLATE "pg_catalog"."default" + ); +COMMENT ON COLUMN "public"."agent_task_table_relation"."task_id" IS 'Agent任务集群关联关系-任务ID'; +COMMENT ON COLUMN "public"."agent_task_table_relation"."task_table_name" IS '集群ID'; +COMMENT ON TABLE "public"."agent_task_table_relation" IS 'Agent任务集群关联表'; + +CREATE OR REPLACE FUNCTION change_password_field_to_text() RETURNS integer AS 'BEGIN + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = ''public'' + AND table_name = ''ops_encryption'' + AND column_name = ''public_key'' + AND data_type != ''text'' + ) THEN + ALTER TABLE public.ops_encryption + ALTER COLUMN public_key TYPE text USING public_key::text; + END IF; + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = ''public'' + AND table_name = ''ops_encryption'' + AND column_name = ''encryption_key'' + AND data_type != ''text'' + ) THEN + ALTER TABLE public.ops_encryption + ALTER COLUMN encryption_key TYPE text USING encryption_key::text; + END IF; + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = ''public'' + AND table_name = ''ops_encryption'' + AND column_name = ''private_key'' + AND data_type != ''text'' + ) THEN + ALTER TABLE public.ops_encryption + ALTER COLUMN private_key TYPE text USING private_key::text; + END IF; + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = ''public'' + AND table_name = ''ops_jdbcdb_cluster_node'' + AND column_name = ''password'' + AND data_type != ''text'' + ) THEN + ALTER TABLE public.ops_jdbcdb_cluster_node + ALTER COLUMN password TYPE text USING password::text; + END IF; + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = ''public'' + AND table_name = ''ops_host_user'' + AND column_name = ''password'' + AND data_type != ''text'' + ) THEN + ALTER TABLE public.ops_host_user + ALTER COLUMN password TYPE text USING password::text; + END IF; + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = ''public'' + AND table_name = ''ops_cluster_task'' + AND column_name = ''database_password'' + AND data_type != ''text'' + ) THEN + ALTER TABLE public.ops_cluster_task + ALTER COLUMN database_password TYPE text USING database_password::text; + END IF; + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = ''public'' + AND table_name = ''ops_cluster'' + AND column_name = ''database_password'' + AND data_type != ''text'' + ) THEN + ALTER TABLE public.ops_cluster + ALTER COLUMN database_password TYPE text USING database_password::text; + END IF; + RETURN 0; +END;' +LANGUAGE plpgsql; + +SELECT change_password_field_to_text(); + +DROP FUNCTION change_password_field_to_text; + + +CREATE TABLE IF NOT EXISTS "public"."sys_setting_ext"( + "id" BIGSERIAL PRIMARY KEY, + "user_id" int8 NOT NULL, + "param_name" varchar(64) NOT NULL COLLATE "pg_catalog"."default", + "param_value" varchar(64) COLLATE "pg_catalog"."default", + "remark" varchar(128) COLLATE "pg_catalog"."default" +); +insert into "public"."sys_setting_ext" values(1,1,'datakit_server_host','','') ON DUPLICATE KEY UPDATE NOTHING; \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/i18n/messages.properties b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/i18n/messages.properties new file mode 100644 index 000000000..2f5a5fabb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/i18n/messages.properties @@ -0,0 +1,3 @@ +user.password.not.match=user does not exist/wrong password +upload.exceed.maxSize=The uploaded file size exceeds the file size limit!
The maximum allowed file size is: {0}MB! +upload.filename.exceed.length=The maximum length of the uploaded file name is {0} characters diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/log4j2.xml b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/log4j2.xml new file mode 100644 index 000000000..7075ee5be --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/log4j2.xml @@ -0,0 +1,72 @@ + + + + $${sys:logging.file.path:-./logs} + %enc{%msg}{CRLF}%n + %style{%d{yyyy-MM-dd HH:mm:ss}}{blue} [%style{%t}{cyan}] [%style{%c{32}}{yellow}] - [%highlight{%p}] ${SAFE_MSG} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/mybatis/mybatis-config.xml b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 000000000..ea13e2840 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/AgentHeartbeatServiceImplTest.java b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/AgentHeartbeatServiceImplTest.java new file mode 100644 index 000000000..75d8165ab --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/AgentHeartbeatServiceImplTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.service.agent.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opengauss.admin.common.core.domain.model.agent.HeartbeatReport; +import org.opengauss.agent.event.AgentStatusEvent; +import org.opengauss.agent.impl.AgentHeartbeatServiceImpl; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationEventPublisher; + +import java.time.Instant; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +/** + * AgentHeartbeatServiceImplTest + * + * @author: wangchao + * @Date: 2025/4/15 11:45 + * @Description: AgentHeartbeatServiceImplTest + * @since 7.0.0-RC2 + **/ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ExtendWith(MockitoExtension.class) +class AgentHeartbeatServiceImplTest { + @Mock + private ApplicationEventPublisher eventPublisher; + + @InjectMocks + private AgentHeartbeatServiceImpl service = new AgentHeartbeatServiceImpl(); + + @BeforeEach + void start() { + service.startHeartbeatService(); + } + + // 心跳接收逻辑测试 + @DisplayName("心跳接收逻辑测试 1") + @Test + void whenReceiveFirstHeartbeat_thenRegisterNewAgent() { + String agentId = "101"; + // 首次心跳 + HeartbeatReport report = new HeartbeatReport(agentId, Instant.now()); + service.receiveHeartbeat(report); + assertTrue(service.isAgentOnline(agentId)); + } + + @DisplayName("心跳接收逻辑测试 2") + @Test + void whenReceiveExpiredHeartbeat_thenMarkOffline() { + String agentId = "102"; + // 过期时间戳(假设超时阈值30秒) + HeartbeatReport report = new HeartbeatReport(agentId, Instant.now().minusSeconds(31)); + service.receiveHeartbeat(report); + service.checkAgentsStatus(); + assertFalse(service.isAgentOnline(agentId)); + } + + @DisplayName("心跳接收逻辑测试 3") + @Test + void whenStatusChanged_thenPublishEvent() { + String agentId = "103"; + // 初始在线状态 + service.receiveHeartbeat(new HeartbeatReport(agentId, Instant.now())); + // 触发离线 + service.receiveHeartbeat(new HeartbeatReport(agentId, Instant.now().minusSeconds(35))); + ArgumentCaptor captor = ArgumentCaptor.forClass(AgentStatusEvent.class); + verify(eventPublisher, times(2)).publishEvent(captor.capture()); + List events = captor.getAllValues(); + assertEquals(true, events.get(0).isOnline()); // 初始上线事件 + assertEquals(false, events.get(1).isOnline()); // 后续离线事件 + } + + @DisplayName("状态检查任务测试1") + @Test + void whenCheckStatus_thenUpdateOnlineState() throws InterruptedException { + String agentId = "104"; + // 注册过期Agent + service.receiveHeartbeat(new HeartbeatReport(agentId, Instant.now().minusSeconds(31))); + // 手动触发状态检查 + service.checkAgentsStatus(); + assertFalse(service.isAgentOnline(agentId)); + } + + @DisplayName("边界条件测试1") + @Test + void whenQueryNonExistAgent_thenReturnOffline() { + String agentId = "105"; + assertFalse(service.isAgentOnline(agentId)); + } + + @DisplayName("边界条件测试2") + @Test + void whenConcurrentUpdates_thenMaintainConsistency() throws Exception { + String agentId = "106"; + ExecutorService pool = Executors.newFixedThreadPool(10); + IntStream.range(0, 100) + .forEach(i -> pool.submit(() -> service.receiveHeartbeat(new HeartbeatReport(agentId, Instant.now())))); + pool.shutdown(); + assertTrue(pool.awaitTermination(3, TimeUnit.SECONDS)); + assertTrue(service.isAgentOnline(agentId)); + } + + @DisplayName("事件验证:通过ArgumentCaptor捕获事件细节") + @Test + void verifyEventPayloadDetails() { + String agentId = "107"; + service.receiveHeartbeat(new HeartbeatReport(agentId, Instant.now())); + ArgumentCaptor captor = ArgumentCaptor.forClass(AgentStatusEvent.class); + verify(eventPublisher).publishEvent(captor.capture()); + AgentStatusEvent event = captor.getValue(); + assertEquals("agent-07", event.getAgentId()); + assertEquals(true, event.isOnline()); + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/CalculateCleanIntervalTest.java b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/CalculateCleanIntervalTest.java new file mode 100644 index 000000000..38a2cbbfd --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/CalculateCleanIntervalTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.service.agent.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import lombok.extern.slf4j.Slf4j; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +/** + * CalculateCleanIntervalTest + * + * @author: wangchao + * @Date: 2025/5/19 16:06 + * @since 7.0.0-RC2 + **/ +@Slf4j +public class CalculateCleanIntervalTest { + @DisplayName("keep period PT5M") + @Test + public void testKeepPeriod5M() { + String keepPeriod = "PT5M"; + long interval = calculateCleanInterval(keepPeriod); + log.info("Clean interval for keepPeriod {}: {} seconds", keepPeriod, interval); + assertEquals(60, interval); + } + + @DisplayName("keep period PT10M") + @Test + public void testKeepPeriod10M() { + String keepPeriod = "PT10M"; + long interval = calculateCleanInterval(keepPeriod); + log.info("Clean interval for keepPeriod {}: {} seconds", keepPeriod, interval); + assertEquals(60, interval); + } + + @DisplayName("keep period PT5M") + @Test + public void testKeepPeriod15M() { + String keepPeriod = "PT15M"; + long interval = calculateCleanInterval(keepPeriod); + log.info("Clean interval for keepPeriod {}: {} seconds", keepPeriod, interval); + assertEquals(90, interval); + } + + @DisplayName("keep period PT30M") + @Test + public void testKeepPeriod30M() { + String keepPeriod = "PT30M"; + long interval = calculateCleanInterval(keepPeriod); + log.info("Clean interval for keepPeriod {}: {} seconds", keepPeriod, interval); + assertEquals(180, interval); + } + + @DisplayName("keep period PT1H") + @Test + public void testKeepPeriod1H() { + String keepPeriod = "PT1H"; + long interval = calculateCleanInterval(keepPeriod); + log.info("Clean interval for keepPeriod {}: {} seconds", keepPeriod, interval); + assertEquals(360, interval); + } + + @DisplayName("keep period PT2H") + @Test + public void testKeepPeriod2H() { + String keepPeriod = "PT2H"; + long interval = calculateCleanInterval(keepPeriod); + log.info("Clean interval for keepPeriod {}: {} seconds", keepPeriod, interval); + assertEquals(3600, interval); + } + + @DisplayName("keep period PT10H") + @Test + public void testKeepPeriod10H() { + String keepPeriod = "PT10H"; + long interval = calculateCleanInterval(keepPeriod); + log.info("Clean interval for keepPeriod {}: {} seconds", keepPeriod, interval); + assertEquals(3600, interval); + } + + @DisplayName("keep period P1D") + @Test + public void testKeepPeriod1D() { + String keepPeriod = "P1D"; + long interval = calculateCleanInterval(keepPeriod); + log.info("Clean interval for keepPeriod {}: {} seconds", keepPeriod, interval); + assertEquals(8640, interval); + } + + @DisplayName("keep period P7D") + @Test + public void testKeepPeriod7D() { + String keepPeriod = "P7D"; + long interval = calculateCleanInterval(keepPeriod); + log.info("Clean interval for keepPeriod {}: {} seconds", keepPeriod, interval); + assertEquals(60480, interval); + } + + @DisplayName("calc keep period PT1H") + @Test + public void test1() { + Instant currentCleanTime = Instant.now(); + ZonedDateTime zonedCutoffTime = currentCleanTime.atZone(ZoneId.of("GMT+8")) + .withZoneSameInstant(ZoneId.of("Asia/Shanghai")); // 或 ZoneId.of("UTC+8") + log.info("calc keep period PT1H {}: {} seconds", zonedCutoffTime, zonedCutoffTime.toInstant()); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"); + log.info("calc keep period PT1H format {}", zonedCutoffTime.format(formatter)); + } + + private long calculateCleanInterval(String keepPeriod) { + Duration duration = Duration.parse(keepPeriod); + long seconds = duration.toSeconds(); + long interval = seconds / 10; // 默认按存储周期的1/10设置清理间隔 + // 短周期任务(<=1小时)的最小清理间隔为1分钟,长周期任务保持原逻辑 + return Math.max(interval, seconds <= 3600 ? 60 : 3600); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/JavaVersionParseTest.java b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/JavaVersionParseTest.java new file mode 100644 index 000000000..8fd62363f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/JavaVersionParseTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.service.agent.impl; + +import static org.junit.Assert.assertEquals; + +import junit.framework.AssertionFailedError; +import lombok.extern.slf4j.Slf4j; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.opengauss.admin.common.utils.CommonUtils; + +import java.util.Map; + +/** + * org.opengauss.admin.plugin.service.impl.MigrationTaskHostRefServiceImpl + * #checkJavaEnv(org.opengauss.admin.system.plugin.beans.SshLogin) + * + * @author: wangchao + * @Date: 2025/6/17 17:31 + * @since 7.0.0-RC2 + **/ +@Slf4j +public class JavaVersionParseTest { + @Test + public void checkVersionList() { + Map versionMap = Map.of("1.8.0_301", 8, "11.0.12", 11, "17.0.14", 17, "21", 21, "21.0.1", 21, + "23-ea", 23); + versionMap.forEach((versionInfo, version) -> { + try { + Assertions.assertEquals(version, CommonUtils.getJavaVersionMajor(versionInfo)); + log.info("parse java version success, version: {}", versionInfo); + } catch (AssertionFailedError e) { + log.error("parse java version error, version: {} {}", versionInfo, e.getMessage()); + } + }); + } + + @Test + public void checkBishengJDKVersion() { + String jdk8 = "openjdk version \"1.8.0_382\"\n" + + "OpenJDK Runtime Environment (Harmony-based)(build 1.8.0_382-b05)\n" + + "OpenJDK 64-Bit Server VM (build 25.382-b05, mixed mode)"; + String jdk11 = "openjdk version \"11.0.20\" 2024-07-16\n" + + "OpenJDK Runtime Environment (build 11.0.20+8-Bisheng)\n" + + "OpenJDK 64-Bit Server VM (build 11.0.20+8-Bisheng, mixed mode)"; + String jdk17 = "openjdk version \"17.0.9\" 2024-10-17\n" + + "OpenJDK Runtime Environment (build 17.0.9+9-Bisheng)\n" + + "OpenJDK 64-Bit Server VM (build 17.0.9+9-Bisheng, mixed mode)"; + String jdk21 = "openjdk version \"21\" 2023-09-19\n" + "OpenJDK Runtime Environment (build 21+35-Bisheng)\n" + + "OpenJDK 64-Bit Server VM (build 21+35-Bisheng, mixed mode, sharing)"; + Map versionMap = Map.of(jdk8, 8, jdk11, 11, jdk17, 17, jdk21, 21); + versionMap.forEach((versionInfo, version) -> { + try { + Assertions.assertEquals(version, CommonUtils.getJavaVersionMajor(versionInfo)); + log.info("parse java version success, version: {}", versionInfo); + } catch (AssertionFailedError e) { + log.error("parse java version error, version: {} {}", versionInfo, e.getMessage()); + } + }); + } + + @Test + public void checkOracleJDKVersion() { + String jdk8 = "java version \"1.8.0_381\"\n" + "Java(TM) SE Runtime Environment (build 1.8.0_381-b13)\n" + + "Java HotSpot(TM) 64-Bit Server VM (build 25.381-b13, mixed mode)"; + String jdk11 = "java version \"11.0.20\" 2024-07-16 LTS\n" + + "Java(TM) SE Runtime Environment 18.9 (build 11.0.20+8-LTS-111)\n" + + "Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.20+8-LTS-111, mixed mode)"; + String jdk17 = "java version \"17.0.9\" 2024-10-17 LTS\n" + + "Java(TM) SE Runtime Environment (build 17.0.9+9-LTS-224)\n" + + "Java HotSpot(TM) 64-Bit Server VM (build 17.0.9+9-LTS-224, mixed mode)"; + String jdk21 = "java version \"21\" 2023-09-19\n" + "Java(TM) SE Runtime Environment (build 21+35-2513)\n" + + "Java HotSpot(TM) 64-Bit Server VM (build 21+35-2513, mixed mode, sharing)"; + Map versionMap = Map.of(jdk8, 8, jdk11, 11, jdk17, 17, jdk21, 21); + versionMap.forEach((versionInfo, version) -> { + try { + Assertions.assertEquals(version, CommonUtils.getJavaVersionMajor(versionInfo)); + log.info("parse java version success, version: {}", versionInfo); + } catch (AssertionFailedError e) { + log.error("parse java version error, version: {} {}", versionInfo, e.getMessage()); + } + }); + } + + @Test + public void checkOpenJDKVersion() { + String jdk8 = "openjdk version \"1.8.0_382\"\n" + "OpenJDK Runtime Environment (build 1.8.0_382-b05)\n" + + "OpenJDK 64-Bit Server VM (build 25.382-b05, mixed mode)"; + String jdk11 = "java version \"11.0.20\" 2024-07-16 LTS\n" + + "Java(TM) SE Runtime Environment 18.9 (build 11.0.20+8-LTS-111)\n" + + "Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.20+8-LTS-111, mixed mode)"; + String jdk17 = "openjdk version \"17.0.9\" 2024-10-17\n" + + "OpenJDK Runtime Environment Temurin-17.0.9+9 (build 17.0.9+9)\n" + + "OpenJDK 64-Bit Server VM Temurin-17.0.9+9 (build 17.0.9+9, mixed mode)"; + String jdk21 = "openjdk version \"21\" 2023-09-19\n" + + "OpenJDK Runtime Environment Temurin-21+35 (build 21+35)\n" + + "OpenJDK 64-Bit Server VM Temurin-21+35 (build 21+35, mixed mode, sharing)"; + Map versionMap = Map.of(jdk8, 8, jdk11, 11, jdk17, 17, jdk21, 21); + versionMap.forEach((versionInfo, version) -> { + try { + Assertions.assertEquals(version, CommonUtils.getJavaVersionMajor(versionInfo)); + log.info("parse java version success, version: {}", versionInfo); + } catch (AssertionFailedError e) { + log.error("parse java version error, version: {} {}", versionInfo, e.getMessage()); + } + }); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/MathUtilsTest.java b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/MathUtilsTest.java new file mode 100644 index 000000000..1a9042635 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/MathUtilsTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.service.agent.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import lombok.extern.slf4j.Slf4j; + +import org.junit.jupiter.api.Test; +import org.opengauss.admin.common.utils.MathUtils; + +/** + * MathUtilsTest + * + * @author: wangchao + * @Date: 2025/6/21 18:45 + * @since 7.0.0-RC2 + **/ +@Slf4j +public class MathUtilsTest { + @Test + public void testAdd() { + String s = "1"; + String s2 = "2"; + String result = MathUtils.add(s, s2); + log.info("{} add {} result: {}", s, s2, result); + assertEquals("3.00", result); + } + + @Test + public void testFormatPrecisionFour() { + String s = "1"; + String result = MathUtils.formatScaleTwo(s); + log.info("{} formatScaleTwo result: {}", s, result); + assertEquals("1.00", result); + } + + @Test + public void testPercent() { + String s = "1"; + String s2 = "10"; + String result = MathUtils.percent(s, s2); + log.info("{}/{} percent result: {}", s, s2, result); + assertEquals("10.00", result); + } + + @Test + public void testPercent_0() { + String s = "1"; + String s2 = "0"; + String result = MathUtils.percent(s, s2); + log.info("{}/{} percent result: {}", s, s2, result); + assertEquals("0.00", result); + } + + @Test + public void testPercent_3() { + String s = "1"; + String s2 = "3"; + String result = MathUtils.percent(s, s2); + log.info("{}/{} percent result: {}", s, s2, result); + assertEquals("33.33", result); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/MurmurHash3Test.java b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/MurmurHash3Test.java new file mode 100644 index 000000000..8c5261f57 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/MurmurHash3Test.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.service.agent.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.commons.codec.digest.MurmurHash3; +import org.junit.Test; + +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.Set; + +/** + * MurmurHash3Test + * + * @author: wangchao + * @Date: 2025/5/28 17:17 + * @since 7.0.0-RC2 + **/ +public class MurmurHash3Test { + @Test + public void testGenerateTableName() { + Set ids = new HashSet<>(); + Long taskId = 123L; + Long agentId = 456L; + String clusterId = "cluster1"; + for (int i = 0; i < 10; i++) { + ids.add(calcRowId(taskId, agentId, clusterId, i)); + } + assertEquals(10, ids.size()); + } + + private long calcRowId(Long taskId, Long agentId, String clusterId, int rowIdx) { + return MurmurHash3.hash32x86((taskId + agentId + clusterId + rowIdx).getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/PathValidateTest.java b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/PathValidateTest.java new file mode 100644 index 000000000..733ad98fb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/PathValidateTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.service.agent.impl; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.CommonUtils; + +/** + * PathValidateTest + * + * @author: wangchao + * @Date: 2025/8/15 11:52 + * @since 7.0.0-RC2 + **/ +public class PathValidateTest { + @BeforeAll + static void initEnv() { + System.setProperty("os.name", "Linux"); + } + + @Test + void testPathValidate_ValidPath() { + // Test with valid path + assertDoesNotThrow(() -> CommonUtils.pathValidate("/valid/path")); + } + + @Test + void testPathValidate_NullPath() { + // Test with null path + OpsException exception = assertThrows(OpsException.class, () -> { + CommonUtils.pathValidate(null); + }); + assertNotNull(exception.getMessage()); + } + + @Test + void testPathValidate_EmptyPath() { + // Test with empty path + OpsException exception = assertThrows(OpsException.class, () -> { + CommonUtils.pathValidate(""); + }); + assertNotNull(exception.getMessage()); + } + + @Test + void testPathValidate_InvalidCharacters() { + // Test with path containing invalid characters + OpsException exception = assertThrows(OpsException.class, () -> { + CommonUtils.pathValidate("/invalid/|path"); + }); + assertNotNull(exception.getMessage()); + } + + @Test + void testPathValidate_RelativePath() { + // Test with relative path + OpsException exception = assertThrows(OpsException.class, () -> { + CommonUtils.pathValidate("../relative/path"); + }); + assertNotNull(exception.getMessage()); + } + + @Test + void testPathValidate_TooLongPath() { + // Test with path exceeding maximum allowed length + String longPath = "/" + "a".repeat(255); // Assuming max length is 255 + OpsException exception = assertThrows(OpsException.class, () -> { + CommonUtils.pathValidate(longPath); + }); + assertNotNull(exception.getMessage()); + } + + @Test + void testPathValidate_LongPath() { + // Test with path exceeding maximum allowed length + String longPath = "/" + "a".repeat(254); // Assuming max length is 255 + assertDoesNotThrow(() -> CommonUtils.pathValidate(longPath)); + } + + @Test + void testPathValidate_TooDeepPath() { + // Test with path exceeding maximum allowed deep + String deepPath = "/a/b/c/d/e/f"; // Assuming max deep is 6 + OpsException exception = assertThrows(OpsException.class, () -> { + CommonUtils.pathValidate(deepPath); + }); + assertNotNull(exception.getMessage()); + } + + @Test + void testPathValidate_DeepPath() { + // Test with path exceeding maximum allowed deep + String deepPath = "/a/b/c/d/e"; // Assuming max deep is 5 + assertDoesNotThrow(() -> CommonUtils.pathValidate(deepPath)); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/RandomStringTest.java b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/RandomStringTest.java new file mode 100644 index 000000000..b0fb0a9c1 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/RandomStringTest.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +package org.opengauss.admin.system.service.agent.impl; + +import lombok.extern.slf4j.Slf4j; + +import org.junit.Test; +import org.opengauss.admin.common.utils.RandomCharGenerator; + +/** + * RandomStringTest + * + * @author: wangchao + * @Date: 2025/9/25 09:39 + * @since 7.0.0-RC2 + **/ +@Slf4j +public class RandomStringTest { + @Test + public void testGeneratorRandomString() { + log.info(RandomCharGenerator.generateRandomBased128String()); + log.info(RandomCharGenerator.generateRandomBased128String()); + log.info(RandomCharGenerator.generateRandomBased128String()); + log.info(RandomCharGenerator.generateRandomBased128String()); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/TableNameGeneratorTest.java b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/TableNameGeneratorTest.java new file mode 100644 index 000000000..a74c219f9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-api/src/test/java/org/opengauss/admin/system/service/agent/impl/TableNameGeneratorTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.service.agent.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.Test; +import org.opengauss.agent.repository.TableNameGenerator; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * TableNameGeneratorTest + * + * @author: wangchao + * @Date: 2025/5/28 09:46 + * @since 7.0.0-RC2 + **/ +public class TableNameGeneratorTest { + @Test + public void testGenerateTableName() { + List taskIds = List.of("test", "datakit_agent_pip_test", "opengauss_instance_memory_top10", + "modeling_visualization_custom_dimensions", "ops_az", "base_host_info", "host_dynamic_metrics", + "opengauss_instance_monitor", "opengauss_alarm_notification", "opengauss_slow_sql_query"); + Set tableNameSet = new HashSet<>(); + for (String taskId : taskIds) { + for (int i = 0; i < 1000000; i++) { + String realTimeTableName = TableNameGenerator.getRealTimeTableName(taskId); + tableNameSet.add(realTimeTableName); + } + } + assertEquals(taskIds.size(), tableNameSet.size()); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/pom.xml b/ER-Model/openGauss-datakit/visualtool-common/pom.xml new file mode 100644 index 000000000..8fcd5d04e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/pom.xml @@ -0,0 +1,218 @@ + + + + org.opengauss + visualtool-parent + ${admin.version} + ../../pom.xml + + 4.0.0 + + visualtool-common + + + visualtool-common + + + + + + org.springframework + spring-context-support + + + + org.springframework + spring-web + + + org.springframework.boot + spring-boot-starter-log4j2 + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-websocket + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + org.mybatis + mybatis + + + + + org.springframework.boot + spring-boot-starter-validation + + + org.apache.commons + commons-lang3 + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.alibaba + fastjson + + + + commons-io + commons-io + + + + commons-fileupload + commons-fileupload + + + org.apache.commons + commons-pool2 + + + commons-codec + commons-codec + + + commons-validator + commons-validator + + + commons-digester + commons-digester + + + commons-logging + commons-logging + + + + + commons-net + commons-net + + + org.yaml + snakeyaml + + + io.jsonwebtoken + jjwt-api + + + io.jsonwebtoken + jjwt-impl + runtime + + + io.jsonwebtoken + jjwt-jackson + runtime + + + com.google.guava + guava + + + com.github.ben-manes.caffeine + caffeine + + + eu.bitwalker + UserAgentUtils + + + jakarta.servlet + jakarta.servlet-api + + + + org.projectlombok + lombok + + + + cn.hutool + hutool-all + + + + com.alibaba + easyexcel + + + + org.springframework + spring-test + + + com.github.mwiede + jsch + + + + + + org.apache.maven.plugins + maven-clean-plugin + ${maven-clean-plugin.version} + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + default-prepare-agent + + prepare-agent + + + + report + prepare-package + + report + + + target/jacoco.exec + + + + junit-test + test + + report + + + target/jacoco.exec + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/annotation/Log.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/annotation/Log.java new file mode 100644 index 000000000..c45fcfd37 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/annotation/Log.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * Log.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/annotation/Log.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.annotation; + + +import org.opengauss.admin.common.enums.BusinessType; +import org.opengauss.admin.common.enums.OperatorType; + +import java.lang.annotation.*; + +/** + * log annotation + * + * @author xielibo + */ +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log { + /** + * title + */ + public String title() default ""; + + /** + * businessType + */ + public BusinessType businessType() default BusinessType.OTHER; + + /** + * operator type + */ + public OperatorType operatorType() default OperatorType.OTHER; + + /** + * is save request data + */ + public boolean isSaveRequestData() default true; + + /** + * is save response data + */ + public boolean isSaveResponseData() default true; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/annotation/RepeatSubmit.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/annotation/RepeatSubmit.java new file mode 100644 index 000000000..f0daa94f8 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/annotation/RepeatSubmit.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * RepeatSubmit.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/annotation/RepeatSubmit.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.annotation; + +import java.lang.annotation.*; + +/** + * Custom annotations prevent repeated form submissions + * + * @author xielibo + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit { + /** + * Interval time (ms), less than this time is regarded as repeated submission + */ + public int interval() default 5000; + + /** + * tips + */ + public String message() default "Duplicate submission not allowed, please try again later"; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/config/SystemConfig.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/config/SystemConfig.java new file mode 100644 index 000000000..4d0f1ee64 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/config/SystemConfig.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SystemConfig.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/config/SystemConfig.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.nio.file.Paths; + +/** + * system config + * + * @author xielibo + */ +@Component +@ConfigurationProperties(prefix = "system") +public class SystemConfig { + + /** + * files storage path + */ + private static String defaultStoragePath; + + + public static String getStoragePath() { + return defaultStoragePath; + } + + public void setDefaultStoragePath(String storagePath) { + SystemConfig.defaultStoragePath = storagePath; + } + + /** + * plugin path + */ + public static String getPluginPath() { + return Paths.get(getStoragePath(), "../visualtool-plugin").normalize().toString(); + } + + /** + * import path + */ + public static String getImportPath() { + return getStoragePath() + "/import"; + } + + /** + * avatar files path + */ + public static String getAvatarPath() { + return getStoragePath() + "/avatar"; + } + + /** + * download path + */ + public static String getDownloadPath() { + return getStoragePath() + "/download/"; + } + + /** + * upload path + */ + public static String getUploadPath() { + return getStoragePath() + "/upload/"; + } + + /** + * icon files path + */ + public static String getIconsPath() { + return getStoragePath() + "/icons"; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/AgentConstants.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/AgentConstants.java new file mode 100644 index 000000000..4eab7670e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/AgentConstants.java @@ -0,0 +1,287 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.constant; + +import java.util.List; + +/** + * AgentConstants + * + * @author: wangchao + * @date: 2025/4/18 11:40 + * @since 7.0.0-RC2 + **/ +public interface AgentConstants { + /** + * 状态相关常量 + */ + interface Status { + /** + * heartbeat status up + */ + String HEARTBEAT_STATUS_UP = "UP"; + + /** + * heartbeat status down + */ + String HEARTBEAT_STATUS_DOWN = "DOWN"; + } + + /** + * 空值与默认值相关常量 + */ + interface Default { + /** + * empty string + */ + String EMPTY = ""; + + /** + * none + */ + String NONE = "none"; + } + + /** + * 字段与列表相关常量 + */ + interface Field { + /** + * id + */ + String ID = "id"; + + /** + * batch id + */ + String BATCH_ID = "batch_id"; + + /** + * task id + */ + String TASK_ID = "task_id"; + + /** + * agent id + */ + String AGENT_ID = "agent_id"; + + /** + * cluster node id + */ + String CLUSTER_NODE_ID = "cluster_node_id"; + + /** + * cluster id + */ + String CREATE_TIME = "create_time"; + + /** + * common field list + */ + List COMMON_FIELD_LIST = List.of(ID, TASK_ID, AGENT_ID, CLUSTER_NODE_ID, CREATE_TIME); + + /** + * fingerprint table required field + */ + String FINGERPRINT_TABLE_REQUIRED_FIELD = "fingerprint_id"; + + /** + * row id + */ + String ROW_ID = "row_id"; + } + + /** + * 监控任务模板相关常量 + */ + interface TaskTemplate { + /** + * host dynamic metrics collect_metric + */ + String HOST_DYNAMIC_METRICS = "host_dynamic_metrics"; + + /** + * mysql monitor task template + */ + String MYSQL_MONITOR_TASK_TEMPLATE = "mysql_instance_monitor"; + + /** + * opengauss monitor task template + */ + String OPENGAUSS_MONITOR_TASK_TEMPLATE = "opengauss_instance_monitor"; + } + + /** + * OpenGauss 监控指标相关常量 + */ + interface OpenGaussMetric { + /** + * opengauss monitor metric lock count + */ + String OPENGAUSS_LOCK_COUNT = "opengauss.pg.locks.count"; + + /** + * opengauss monitor metric session count + */ + String OPENGAUSS_SESSION_COUNT = "opengauss.pg.session.count"; + + /** + * opengauss monitor metric connection count + */ + String OPENGAUSS_CONNECTION_COUNT = "opengauss.pg.connect.count"; + } + + /** + * MySQL 监控指标相关常量 + */ + interface MySqlMetric { + /** + * mysql monitor metric role master or slave or cascade + */ + String MYSQL_ROLE = "mysql.jdbc.monitor.role"; + + /** + * mysql monitor metric connection num + */ + String MYSQL_CONNECTION_NUM = "mysql.jdbc.monitor.conn.num"; + + /** + * mysql monitor metric qps + */ + String MYSQL_QPS = "mysql.jdbc.monitor.qps"; + + /** + * mysql monitor metric tps + */ + String MYSQL_TPS = "mysql.jdbc.monitor.tps"; + + /** + * mysql monitor metric memory used + */ + String MYSQL_MEMORY_USED = "mysql.jdbc.monitor.memory.used"; + + /** + * mysql monitor metric table space used + */ + String MYSQL_TABLE_SPACE_USED = "mysql.jdbc.monitor.table.space.used"; + } + + /** + * host metric 监控指标相关常量 + */ + interface HostMetric { + /** + * system cpu usage + */ + String SYSTEM_CPU_USAGE = "system.cpu.usage"; + + /** + * system memory usage + */ + String SYSTEM_MEMORY_USAGE = "system.memory.usage"; + + /** + * system memory total + */ + String SYSTEM_MEMORY_TOTAL = "system.memory.total"; + + /** + * system memory available + */ + String SYSTEM_MEMORY_AVAILABLE = "system.memory.available"; + + /** + * system network bytes received + */ + String SYSTEM_NETWORK_RECEIVED = "system.net.bytes.received"; + + /** + * system network bytes sent + */ + String SYSTEM_NETWORK_SENT = "system.net.bytes.sent"; + + /** + * system disk fs total + */ + String SYSTEM_DISK_TOTAL = "system.disk.fs.total"; + + /** + * system disk fs used + */ + String SYSTEM_DISK_USED = "system.disk.fs.used"; + + /** + * system disk fs free + */ + String SYSTEM_DISK_FREE = "system.disk.fs.free"; + + /** + * system disk fs usage + */ + String SYSTEM_DISK_USAGE = "system.disk.fs.usage"; + + /** + * system cpu info + */ + String CPU_NAME = "system.cpu.name"; + + /** + * system cpu arch + */ + String CPU_ARCH = "system.cpu.arch"; + + /** + * system cpu core num + */ + String CPU_CORE_NUM = "system.cpu.core.count"; + + /** + * system cpu max frequency + */ + String CPU_FREQUENCY = "system.cpu.max.freq"; + + /** + * system cpu min frequency regex + */ + String CPU_FREQ_REGEX = "-?\\d+(\\.\\d+)?"; + + /** + * system os name + */ + String OS_NAME = "system.os.name"; + + /** + * system os version + */ + String OS_VERSION = "system.os.version"; + + /** + * system cpu usage + */ + String CPU_USING = "system.cpu.usage"; + + /** + * migration Host + */ + String MIGRATION_HOST = "migrationHost"; + + /** + * host network tx | rx + */ + String NET_MONITOR = "netMonitor"; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/CommonConstants.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/CommonConstants.java new file mode 100644 index 000000000..6b8542492 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/CommonConstants.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CommonConstants.java + * + * IDENTIFICATION + * openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/CommonConstants.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.constant; + +/** + * CommonConstants + * + * @author wangchao + * @since 2024/10/29 09:26 + */ +public interface CommonConstants { + + /** + * host root username + */ + String ROOT_USER = "root"; + + /** + * line split \n + */ + String LINE_SPLITTER = "\n"; + + /** + * datakit sys_setting ext param :datakit server host name + */ + String SETTING_SERVER_HOST_NAME = "datakit_server_host"; + + /** + * admin user id + */ + Integer ADMIN_USER_ID = 1; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/Constants.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/Constants.java new file mode 100644 index 000000000..b743de004 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/Constants.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * Constants.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/constant/Constants.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.constant; + + +/** + * Common Constant Information + * + * @author xielibo + */ +public class Constants { + + /** + * UTF-8 encoding + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK encoding + */ + public static final String GBK = "GBK"; + + /** + * http request + */ + public static final String HTTP = "http://"; + + /** + * https request + */ + public static final String HTTPS = "https://"; + + /** + * Login user cache key + */ + public static final String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * duplicate submission cache key + */ + public static final String REPEAT_SUBMIT_KEY = "repeat_submit:"; + + /** + * limit cache key + */ + public static final String RATE_LIMIT_KEY = "rate_limit:"; + + + /** + * token + */ + public static final String TOKEN = "token"; + + /** + * token header + */ + public static final String TOKEN_HEADER = "Authorization"; + + /** + * token expire time + */ + public static final int TOKEN_EXPIRE_TIME = 60; + + /** + * token prefix + */ + public static final String TOKEN_PREFIX = "Bearer "; + + /** + * login user key + */ + public static final String LOGIN_USER_KEY = "login_user_key"; + + + /** + * Resource Mapping Path Prefix + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * Plugin Web Resource Prefix + */ + public static final String PLUGIN_PATH_PREFIX = "/static-plugin"; + +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/LogConstants.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/LogConstants.java new file mode 100644 index 000000000..113d7bf9f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/LogConstants.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * LogConstants.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/constant/LogConstants.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.constant; + +/** + * System log constants + */ +public class LogConstants { + + /** + * The log level key + */ + public static final String SYS_LOG_CONFIG_KEY_LEVEL = "log_level"; + + /** + * The MaxFileSize key + */ + public static final String SYS_LOG_CONFIG_KEY_MAX_FILE_SIZE = "log_max_file_size"; + + /** + * The TotalSizeCap key + */ + public static final String SYS_LOG_CONFIG_KEY_TOTAL_SIZE_CAP = "log_total_size_cap"; + + /** + * The MaxHistory key + */ + public static final String SYS_LOG_CONFIG_KEY_MAX_HISTORY = "log_max_history"; + + public static final Integer DEFAULT_MAX_HISTORY = 60; + + public static final String DEFAULT_TOTAL_SIZE_CAP = "10gb"; + + public static final String DEFAULT_MAX_FILE_SIZE = "20mb"; + + public static final String DEFAULT_LEVEL = "info"; + + public static final String DEFAULT_APPENDER = "file_info"; + +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/SqlConstants.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/SqlConstants.java new file mode 100644 index 000000000..240842f36 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/SqlConstants.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SqlConstants.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/constant/SqlConstants.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.constant; + +/** + * SQL Constants + * + * @author fansd + */ +public class SqlConstants { + /** + * order by asc + */ + public static final String ASC = "ASC"; + + /** + * order by desc + */ + public static final String DESC = "DESC"; + + /** + * begin time + */ + public static final String FIELD_NAME_BEGIN_TIME = "beginTime"; + + /** + * end time + */ + public static final String FIELD_NAME_END_TIME = "endTime"; + +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/UserConstants.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/UserConstants.java new file mode 100644 index 000000000..e42d98084 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/UserConstants.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UserConstants.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/constant/UserConstants.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.constant; + +/** + * User Constants + * + * @author xielibo + */ +public class UserConstants { + + /** + * normal status + */ + public static final String NORMAL = "0"; + + /** + * Abnormal state + */ + public static final String EXCEPTION = "1"; + + + /** + * is menu frame + */ + public static final String NO_FRAME = "1"; + + /** + * Menu Type Directory + */ + public static final String TYPE_DIR = "M"; + + /** + * Menu Type Menu + */ + public static final String TYPE_MENU = "C"; + + /** + * Layout + */ + public final static String LAYOUT = "DEFAULT_LAYOUT"; + + /** + * ParentView + */ + public final static String PARENT_VIEW = "ParentView"; + + /** + * check result code + */ + public final static String UNIQUE = "0"; + public final static String NOT_UNIQUE = "1"; + + /** + * delete mark + */ + public final static String UN_DELETE = "0"; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/ops/SshCommandConstants.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/ops/SshCommandConstants.java new file mode 100644 index 000000000..955d30b44 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/constant/ops/SshCommandConstants.java @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SshCommandConstants.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/constant/ops/SshCommandConstants.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.constant.ops; + +/** + * @author lhf + * @date 2022/8/10 14:51 + **/ +public interface SshCommandConstants { + /** + * query operating system + */ + String OS = "cat /etc/os-release | grep ID= | head -n 1 | awk -F '=' '{print $2}' | sed 's/\\\"//g'"; + /** + * Query the operating system version + */ + String OS_VERSION = "cat /etc/os-release | grep VERSION_ID= | head -n 1|awk -F '=' '{print $2}' | sed 's/\\\"//g'"; + + /** + * get memory info + */ + String MEMORY = "free -g |head -n 2| tail -n 1 | tr -s \" \""; + + /** + * CPU usage + */ + String CPU_USING + = "awk 'NR==1 {u=$2; n=$3; s=$4; i=$5; w=$6; x=$7; y=$8; z=$9} END {total=u+n+s+i+w+x+y+z; idle=i; printf \"%" + + ".1f\\n\", 100 - (idle*100/total)}' /proc/stat"; + /** + * rely + */ + String DEPENDENCY + = "yum list installed | egrep 'libaio-devel|flex|bison|ncurses-devel|glibc-devel|patch|readline-devel'"; + + /** + * base dependencies + */ + String BASE_DEPENDENCY = "yum list installed | egrep 'coreutils|procps-ng|openssh-clients|unzip|lsof|grep|tar'"; + /** + * base dependencies + */ + String OPENEULER_BASE_DEPENDENCY = "yum list installed | egrep 'coreutils|procps-ng|openssh|unzip|lsof|grep|tar'"; + + /** + * firewall + */ + String FIREWALL = "systemctl status firewalld | head -n 3 | tail -n 1 | awk '{print $2}'"; + + /** + * Install user + */ + String OMM_USER = "cat /etc/passwd | awk -F \":\" \"{print $1}\"|grep omm | wc -l"; + + /** + * Modify user password + */ + String CHANGE_OMM_PASSWORD_TEMPLATE = "LC_ALL=C passwd {0}"; + /** + * Modify OMM password + */ + String CHANGE_OMM_PASSWORD = "passwd omm"; + + String LIMITS_CHECK = " cat /etc/security/limits.conf | grep 1048576 "; + + /** + * linux limits config + */ + String LIMITS = + "echo -e \"* hard nofile 1048576\\n* soft nproc 1048576\\n* hard nproc 1048576\\n* soft nproc 1048576\" >> " + + "/etc/security/limits.conf"; + + /** + * hostname + */ + String HOSTNAME = "hostname"; + /** + * decompress + */ + String DECOMPRESS = "tar {0} {1} -C {2}"; + /** + * SEMMNI + */ + String SEM = "sysctl -w kernel.sem=\"250 85000 250 330\""; + /** + * Minimalist single node installation + */ + String MINIMAL_LIST_SINGLE_INSTALL = "sh {0}/install.sh -w {1} -p {2} &&source ~/.bashrc"; + /** + * Minimalist cluster installation + */ + String MINIMAL_LIST_CLUSTER_INSTALL = "sh {0}install.sh -w {1} -p {2} --multinode &&source ~/.bashrc"; + /** + * Minimalist Single Node Uninstall + */ + String MINIMAL_LIST_SINGLE_UNINSTALL = "gs_ctl stop -D {0}/single_node"; + /** + * Minimalist single node restart + */ + String MINIMAL_LIST_SINGLE_RESTART = "gs_ctl restart -D {0}/single_node"; + /** + * Minimalist single node startup + */ + String MINIMAL_LIST_SINGLE_START = "gs_ctl start -D {0}/single_node"; + /** + * Minimalist single node stop + */ + String MINIMAL_LIST_SINGLE_STOP = "gs_ctl stop -D {0}/single_node"; + /** + * Minimalist master node uninstall + */ + String MINIMAL_LIST_MASTER_UNINSTALL = "gs_ctl stop -D {0}/master"; + /** + * minimalist uninstall from node + */ + String MINIMAL_LIST_SLAVE_UNINSTALL = "gs_ctl stop -D {0}/slave"; + /** + * Minimalist master node restart + */ + String MINIMAL_LIST_MASTER_RESTART = "gs_ctl restart -D {0}/master"; + /** + * Minimalist masternode startup + */ + String MINIMAL_LIST_MASTER_START = "gs_ctl start -D {0}/master"; + /** + * Minimalist masternode stopped + */ + String MINIMAL_LIST_MASTER_STOP = "gs_ctl stop -D {0}/master"; + /** + * Minimalist restart from node + */ + String MINIMAL_LIST_SLAVE_RESTART = "gs_ctl restart -D {0}/slave"; + /** + * The minimalist version starts from node + */ + String MINIMAL_LIST_SLAVE_START = "gs_ctl start -D {0}/slave"; + /** + * minimalist stop from node + */ + String MINIMAL_LIST_SLAVE_STOP = "gs_ctl stop -D {0}/slave"; + /** + * Lite reboot + */ + String LITE_RESTART = "gs_ctl restart -D {0}"; + /** + * Lite launch + */ + String LITE_START = "gs_ctl start -D {0}"; + /** + * Lite version discontinued + */ + String LITE_STOP = "gs_ctl stop -D {0}"; + /** + * Create a directory + */ + String MK_DIR = "mkdir -p {0}"; + /** + * Remote copy from current host to host + */ + String SCP = "scp {0} root@{1}:{2}"; + /** + * move files + */ + String MV = "mv -rf {0} ./old/"; + /** + * grant permission + */ + String CHMOD = "chmod 755 -R {0}"; + + String CHMOD_DATA_PATH = "chmod 700 -R {0}"; + String CHOWN = "chown {0} -R {1}"; + /** + * switch user + */ + String SU = "su {0}"; + /** + * Lite single node installation + */ + String LITE_SINGLE_INSTALL = "echo {0} | sh {1}/install.sh --mode single -D {2} -R {3} --start"; + /** + * Lite masternode installation + */ + String LITE_MASTER_INSTALL = + "echo {0} | sh {1}/install.sh --mode primary -D {2} -R {3} -C \"replconninfo1=''localhost={4} localport={5}" + + " remotehost={6} remoteport={7}''\" --start"; + /** + * Lite install from node + */ + String LITE_SLAVE_INSTALL = + "echo {0} | sh {1}/install.sh --mode standby -D {2} -R {3} -C \"replconninfo1=''localhost={4} localport={5}" + + " remotehost={6} remoteport={7}''\" --start"; + /** + * light version uninstall + */ + String LITE_UNINSTALL = "sh {0}/uninstall.sh --delete-data"; + /** + * Create a file + */ + String FILE_CREATE = "echo ''{0}'' > {1}"; + /** + * Enterprise Edition preInstall + */ + String GS_PREINSTALL_INTERACTIVE = "cd {0} && ./gs_preinstall -U {1} -G {2} -X {3} --non-interactive"; + String GS_PREINSTALL = "cd {0} && ./gs_preinstall -U {1} -G {2} -X {3}"; + /** + * Enterprise Edition Installation + */ + String ENTERPRISE_INSTALL = "cd ~ && gs_install -X {0}"; + /** + * Modify the listening address + */ + String LISTENER = "gs_guc set -D {0} -c \" listen_addresses = ''*''\""; + /** + * Modify hba.conf + */ + String HBA = "gs_guc set -D {0} -h \"host all all 0.0.0.0/0 sha256\" " + + "&& gs_guc set -D {0} -h \"host all all ::/0 sha256\""; + /** + * Login to openGauss + */ + String LOGIN = "gsql -d postgres -p {0}"; + /** + * network usage + */ + String NET = "cat /proc/net/dev"; + String DEL_FILE = "rm -rf {0}"; + + /** + * cpu arch + */ + String CPU_ARCH = "LC_ALL=C lscpu | grep Architecture: | head -n 1 | awk -F ':' '{print $2}'"; + /** + * check os user exist + */ + String CHECK_OS_USER_EXIST = "export PATH=$PATH:/sbin:/usr/sbin && id -u %s"; + /** + * message constants no such user + */ + String NO_SUCH_USER = "no such user"; + /** + * create os user + */ + String CREATE_OS_USER = "useradd %s"; + + /** + * check os user sudo no password permission + */ + String CREATE_OS_USER_SUDO = "sudo -n true && echo \"%s\" || echo \"%s\""; + + /** + * check os disk info + */ + String DISK_TOTAL_MONITOR = "df -T --total | tail -n +2| tr -s \" \" | grep total"; + + /** + * check os net card name + */ + String NET_CARD_NAME = "export PATH=$PATH:/sbin:/usr/sbin && ip -o addr show | grep %s | awk '{print $2}'"; + /** + * check os net card rx tx + */ + String NET_MONITOR = "export PATH=$PATH:/sbin:/usr/sbin net_card_name=\"%s\"\n" + + " rx_net1=$(ip -s link show $net_card_name | awk '/RX:/{getline; print $1}')\n" + + " tx_net1=$(ip -s link show $net_card_name | awk '/TX:/{getline; print $1}')\n" + " sleep 1\n" + + " rx_net2=$(ip -s link show $net_card_name | awk '/RX:/{getline; print $1}')\n" + + " tx_net2=$(ip -s link show $net_card_name | awk '/TX:/{getline; print $1}')\n" + + " rx_net=$((rx_net2 - rx_net1))\n" + " tx_net=$((tx_net2 - tx_net1))\n" + + " echo \"$rx_net|$tx_net\""; + /** + * check os port conflict + */ + String CHECK_OS_PORT_CONFLICT = "export PATH=$PATH:/sbin:/usr/sbin && ss -tuln | grep -E \":%d\\s+\" | wc -l"; + /** + * check os openGauss Version + */ + String OPENGAUSS_MAIN_VERSION_NUM = "gsql -V| awk '{print $3}'"; + /** + * check os user java version + */ + String JAVA_VERSION = "java -version"; + /** + * base host info: contains cpu core number, remaining memory, available disk space + */ + String HOST_BASE_INFO = "cat /proc/cpuinfo |grep \"processor\"|wc -l && grep MemFree /proc/meminfo " + + "| awk '{val=$2/1024}END{print val}' && df -Th | egrep -v \"(tmpfs|sr0)\" | tail -n +2| " + + "tr -s \" \" | cut -d \" \" -f5 | tr -d \"G\" | awk '{sum+=$1}END{print sum}'"; + /** + * check path is empty + */ + String CHECK_PATH_EMPTY = "if [ $(ls -A %s | wc -l) -eq 0 ]; then\n echo dir_empty\n else\n echo dir_no_empty\n fi"; + /** + * check file is exist + */ + String CHECK_FILE_EXIST = "if [ -e %s ]; then echo file_exist; else echo file_not_exist; fi"; + /** + * check result + * file_exist + */ + String CHECK_RESULT_FILE_EXIST = "file_exist"; + /** + * check result + * dir_empty: + */ + String CHECK_RESULT_PATH_EMPTY = "dir_empty"; + + /** + * get host cpu info + */ + String CPU = "lscpu | grep \"CPU(s):\\|Architecture\\|MHz\""; + /** + * check openGauss version + * ENTERPRISE: + */ + String CHECK_ENTERPRISE_VERSION = "source %s;gs_om -t view&&[ -f %s ]"; + /** + * check openGauss version + * LITE: + */ + String CHECK_LITE_VERSION = "source %s;gsql -V|grep -i \"openGauss-lite\"&&[ -f %s ]"; + /** + * check openGauss version + * MINILIST: + */ + String CHECK_MINILIST_VERSION = "source %s;gsql -V&&[ -f %s ]"; + + /** + * check environment is online by curl -I https://opengauss.org/zh/ + */ + String ONLINE = "curl -I https://opengauss.org/zh/"; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/controller/BaseController.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/controller/BaseController.java new file mode 100644 index 000000000..60e298d14 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/controller/BaseController.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BaseController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/controller/BaseController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.opengauss.admin.common.constant.SqlConstants; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.core.page.PageDomain; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.core.page.TableSupport; +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.common.utils.DateUtils; +import org.opengauss.admin.common.utils.SecurityUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; + +import java.beans.PropertyEditorSupport; +import java.util.Date; + +/** + * base controller + * + * @author xielibo + */ +public class BaseController { + + /** + * Automatically convert the date format string passed from the front desk to Date type + */ + @InitBinder + public void initBinder(WebDataBinder binder) { + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() { + @Override + public void setAsText(String text) { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * Set request pagination params + */ + protected Page startPage() { + Page page = new Page<>(); + page.setSize(15); + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + String orderBy = pageDomain.getOrderBy(); + String orderColumn = pageDomain.getOrderByColumn(); + String isAsc = pageDomain.getIsAsc(); + + if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize)) { + page.setCurrent(pageNum); + page.setSize(pageSize); + page.setOptimizeCountSql(false); + page.setMaxLimit(500L); + } + + if (StringUtils.isNotBlank(orderColumn)) { + if (SqlConstants.ASC.equalsIgnoreCase(isAsc)) { + page.addOrder(OrderItem.asc(orderColumn)); + } else { + page.addOrder(OrderItem.desc(orderColumn)); + } + } + + return page; + } + + /** + * Set request pagination params + */ + protected Page startPage(Integer pageNum, Integer size, String orderColumn, String order) { + Page page = new Page(); + page.setCurrent(pageNum); + page.setSize(size); + page.setOptimizeCountSql(false); + page.setMaxLimit(500L); + + if (StringUtils.isNotBlank(orderColumn)) { + if (SqlConstants.ASC.equalsIgnoreCase(order)) { + page.addOrder(OrderItem.asc(orderColumn)); + } else { + page.addOrder(OrderItem.desc(orderColumn)); + } + } + return page; + } + + /** + * Set response pagination params + * + * @param page + */ + protected TableDataInfo getDataTable(IPage page) { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(ResponseCode.SUCCESS.code()); + rspData.setMsg(ResponseCode.SUCCESS.msg()); + rspData.setRows(page.getRecords()); + rspData.setTotal(page.getTotal()); + return rspData; + } + + /** + * response result + */ + protected AjaxResult toAjax(int rows) { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * response result + */ + protected AjaxResult toAjax(boolean result) { + return result ? success() : error(); + } + + /** + * response success result + */ + public AjaxResult success() { + return AjaxResult.success(); + } + + /** + * response error result + */ + public AjaxResult error() { + return AjaxResult.error(); + } + + /** + * response success result + */ + public AjaxResult success(String message) { + return AjaxResult.success(message); + } + + /** + * response error result + */ + public AjaxResult error(String message) { + return AjaxResult.error(message); + } + + + /** + * Get the currently logged in user + */ + public LoginUser getLoginUser() { + return SecurityUtils.getLoginUser(); + } + + /** + * Get the currently logged in userId + */ + public Integer getUserId() { + return getLoginUser().getUser().getUserId(); + } + + /** + * Get the currently logged in username + */ + public String getUsername() { + return getLoginUser().getUsername(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/AjaxResult.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/AjaxResult.java new file mode 100644 index 000000000..6944b3606 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/AjaxResult.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AjaxResult.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/AjaxResult.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain; + +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.common.utils.StringUtils; + +import java.util.HashMap; + +/** + * Ajax Result Response + * + * @author xielibo + */ +public class AjaxResult extends HashMap { + private static final long serialVersionUID = 1L; + + /** + * code + */ + public static final String CODE_TAG = "code"; + + /** + * message + */ + public static final String MSG_TAG = "msg"; + + /** + * data + */ + public static final String DATA_TAG = "data"; + + public AjaxResult() { + } + public AjaxResult(int code, String msg) { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + public AjaxResult(int code, String msg, Object data) { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (StringUtils.isNotNull(data)) { + super.put(DATA_TAG, data); + } + } + public static AjaxResult success() { + return AjaxResult.success("success"); + } + + public static AjaxResult success(Object data) { + return AjaxResult.success("success", data); + } + + public static AjaxResult success(String msg) { + return AjaxResult.success(msg, null); + } + + public static AjaxResult success(String msg, Object data) { + return new AjaxResult(ResponseCode.SUCCESS.code(), msg, data); + } + + public static AjaxResult error() { + return AjaxResult.error("fail"); + } + + public static AjaxResult error(String msg) { + return AjaxResult.error(msg, null); + } + + public static AjaxResult error(Integer code) { + return AjaxResult.error(code, ResponseCode.getInstance(code).msg()); + } + + /** + * error response encapsulation + * + * @param code error code + * @param msg error message + * @param data data + * @return Response Result + */ + public static AjaxResult errorAttachedData(Integer code, String msg, Object data) { + return new AjaxResult(code, msg, data); + } + + public static AjaxResult error(String msg, Object data) { + return new AjaxResult(ResponseCode.ERROR.code(), msg, data); + } + + public static AjaxResult error(int code, String msg) { + return new AjaxResult(code, msg, null); + } + + public boolean isOk() { + Object code = get(CODE_TAG); + return code != null && code.equals(ResponseCode.SUCCESS.code()); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/BaseEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/BaseEntity.java new file mode 100644 index 000000000..312daf72d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/BaseEntity.java @@ -0,0 +1,82 @@ +/** + Copyright laker. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ + +package org.opengauss.admin.common.core.domain; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Base Model + * + * @author xielibo + */ +@Data +public class BaseEntity implements Serializable { + private static final long serialVersionUID = 1L; + + + /** + * createBy + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * createTime + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + /** + * updateBy + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * updateTime + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * remark + */ + private String remark; + + /** + * params + */ + @TableField(exist = false) + private Map params; + + public Map getParams() { + if (params == null) { + params = new HashMap<>(4); + } + return params; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/TreeSelect.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/TreeSelect.java new file mode 100644 index 000000000..856a32e0d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/TreeSelect.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * TreeSelect.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/TreeSelect.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain; + +import com.fasterxml.jackson.annotation.JsonInclude; +import org.opengauss.admin.common.core.domain.entity.SysMenu; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Treeselect Bean + * + * @author xielibo + */ +@Data +public class TreeSelect implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * id + */ + private Integer id; + + /** + * label + */ + private String label; + + /** + * children + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + public TreeSelect() { + + } + + public TreeSelect(SysMenu menu) { + this.id = menu.getMenuId(); + this.label = menu.getMenuName(); + this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/UploadInfo.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/UploadInfo.java new file mode 100644 index 000000000..059fa1501 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/UploadInfo.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UploadInfo.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/UploadInfo.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain; + +import lombok.Data; +import org.opengauss.admin.common.core.vo.UploadInfoVO; + +import jakarta.validation.constraints.NotEmpty; + +/** + * File upload common info + * + * @author wangyl + */ +@Data +public class UploadInfo { + /** + * file upload path + */ + @NotEmpty(message = "file path cannot be empty") + private String realPath; + /** + * file name for UI + */ + private String name; + + @Override + public String toString() { + return String.format("Real path: %s, File name: %s", realPath, name); + } + + public UploadInfoVO toVO() { + UploadInfoVO vo = new UploadInfoVO(); + vo.setName(name); + vo.setRealPath(realPath); + return vo; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysMenu.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysMenu.java new file mode 100644 index 000000000..823b205f0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysMenu.java @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysMenu.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysMenu.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.opengauss.admin.common.core.vo.MenuVo; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.util.*; + +/** + * menu permissions sys_menu + * + * @author xielibo + */ +@Data +public class SysMenu { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(value = "menu_id", type = IdType.AUTO) + private Integer menuId; + + /** + * menu name + */ + private String menuName; + + /** + * menu en name + */ + private String menuEnName; + + /** + * parent name + */ + @TableField(exist = false) + private String parentName; + + /** + * parent id + */ + private Integer parentId; + + /** + * order + */ + private Integer orderNum; + + /** + * route path + */ + private String path; + + /** + * component + */ + private String component; + + /** + * is frame(0 yes 1 no) + */ + private String isFrame; + + /** + * is cache(0 yes 1 no) + */ + private String isCache; + + /** + * menu type(M directory C menu) + */ + private String menuType; + + /** + * visible(0:display 1:hide) + */ + private String visible; + + /** + * status(0:display 1:hide) + */ + private String status; + + /** + * perms + */ + private String perms; + + /** + * icon + */ + private String icon; + + /** + * open way 1:page;2:dialog;default:1 + */ + private Integer openWay; + + /** + * Plugin ID + */ + private String pluginId; + + /** + * plugin menu theme + */ + private String pluginTheme; + + /** + * openPosition 1:left menu bar ;2:Home Instance Block, default 1. + */ + private Integer openPosition; + + /** + * query parameter template + */ + private String queryTemplate; + + + /** + * creater + */ + private String createBy; + + /** + * create time + */ + private Date createTime; + + /** + * updater + */ + private String updateBy; + + /** + * update time + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * remark + */ + private String remark; + + /** + * menu classify + */ + private Integer menuClassify; + + /** + * route query + */ + @TableField(exist = false) + private Map query; + + /** + * request params + */ + @TableField(exist = false) + private Map params; + + public Map getParams() { + if (params == null) { + params = new HashMap<>(4); + } + return params; + } + + /** + * children + */ + @TableField(exist = false) + private List children = new ArrayList(); + + + @NotBlank(message = "menuName cannot be empty") + @Size(min = 0, max = 50, message = "menuName length cannot exceed 50 characters") + public String getMenuName() { + return menuName; + } + + + @NotNull(message = "orderNum cannot be empty") + public Integer getOrderNum() { + return orderNum; + } + + @NotBlank(message = "path cannot be empty") + @Size(min = 0, max = 200, message = "path cannot exceed 200 characters") + public String getPath() { + return path; + } + + + @Size(min = 0, max = 200, message = "component cannot exceed 255 characters") + public String getComponent() { + return component; + } + + @NotBlank(message = "menuType cannot be empty") + public String getMenuType() { + return menuType; + } + + @Size(min = 0, max = 100, message = "perms cannot exceed 100 characters") + public String getPerms() { + return perms; + } + + public MenuVo toMenuVo() { + MenuVo vo = MenuVo.builder().menuId(menuId).menuName(menuName) + .parentId(parentId).build(); + return vo; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysRole.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysRole.java new file mode 100644 index 000000000..e954bd693 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysRole.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysRole.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysRole.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import org.opengauss.admin.common.core.domain.BaseEntity; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +/** + * Role Model + * + * @author xielibo + */ +@Data +public class SysRole extends BaseEntity { + private static final long serialVersionUID = 1L; + + + public SysRole() { + } + + public SysRole(Integer roleId) { + this.roleId = roleId; + } + + /** + * roleId + */ + @TableId(value = "role_id", type = IdType.AUTO) + private Integer roleId; + + /** + * roleName + */ + @NotBlank(message = "roleName cannot be empty") + @Size(min = 0, max = 25, message = "roleName length cannot exceed 25 characters") + private String roleName; + + /** + * roleKey + */ + private String roleKey; + + /** + * roleSort + */ + private String roleSort; + + /** + * dataScope + */ + private String dataScope; + + /** + * menuCheckStrictly + */ + private boolean menuCheckStrictly; + + /** + * deptCheckStrictly + */ + private boolean deptCheckStrictly; + + /** + * status + */ + private String status; + + /** + * delFlag + */ + @TableLogic + private String delFlag; + + @TableField(exist = false) + private boolean flag = false; + + @TableField(exist = false) + private Integer[] menuIds; + + + public boolean isAdmin() { + return isAdmin(this.roleId); + } + + public static boolean isAdmin(Integer roleId) { + return roleId != null && 1 == roleId; + } + + public String getRoleName() { + return roleName; + } + + + public String getRoleKey() { + return roleKey; + } + + + public String getRoleSort() { + return roleSort; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysSettingEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysSettingEntity.java new file mode 100644 index 000000000..875e92d6e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysSettingEntity.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysSettingEntity.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysSettingEntity.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +/** + * System Setting Model + * + * @author wangyl + */ +@Data +@TableName("sys_setting") +public class SysSettingEntity { + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + @NotBlank(message = "uploadPath cannot be empty") + private String uploadPath; + @NotNull(message = "userId cannot be empty") + private Integer userId; + @NotBlank(message = "portalPkgDownloadUrl cannot be empty") + private String portalPkgDownloadUrl; + @NotBlank(message = "portalPkgName cannot be empty") + private String portalPkgName; + @NotBlank(message = "portalJarName cannot be empty") + private String portalJarName; + @TableField(exist = false) + private String serverHost; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysSettingExtEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysSettingExtEntity.java new file mode 100644 index 000000000..e4280a479 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysSettingExtEntity.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import lombok.Data; + +/** + * SysSettingExtEntity + * + * @author: wangchao + * @Date: 2025/9/8 15:48 + * @since 7.0.0-RC2 + **/ +@Data +@TableName("sys_setting_ext") +public class SysSettingExtEntity { + @TableId(value = "id", type = IdType.AUTO) + private Long id; + private Long userId; + private String paramName; + private String paramValue; + private String remark; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysUser.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysUser.java new file mode 100644 index 000000000..44c5a6cdd --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysUser.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysUser.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/SysUser.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; +import org.opengauss.admin.common.core.domain.BaseEntity; + +import java.util.List; + +/** + * SysUser Model + * + * @author xielibo + */ +@Data +public class SysUser extends BaseEntity { + + private static final long serialVersionUID = 1L; + /** + * userId + */ + @TableId(value = "user_id", type = IdType.AUTO) + private Integer userId; + /** + * userName + */ + private String userName; + /** + * nickName + */ + private String nickName; + /** + * email + */ + private String email; + /** + * phonenumber + */ + private String phonenumber; + /** + * sex + */ + private String sex; + /** + * avatar + */ + private String avatar; + /** + * password + */ + @TableField(updateStrategy = FieldStrategy.NOT_EMPTY) + private String password; + /** + * salt + */ + @TableField(exist = false) + private String salt; + /** + * status(0:normal 1:disabled) + */ + private String status; + /** + * delFlag(0 No 1 Yes) + */ + @TableLogic + private String delFlag; + /** + * updatePwd (0 No 1 Yes) + */ + private String updatePwd; + /** + * loginIp + */ + @TableField(fill = FieldFill.INSERT) + private String loginIp; + /** + * roles + */ + @TableField(exist = false) + private List roles; + /** + * roleIds + */ + @TableField(exist = false) + private Integer roleIds; + @TableField(exist = false) + private String roleName; + + public SysUser() { + + } + + public SysUser(Integer userId) { + this.userId = userId; + } + + public static boolean isAdmin(Integer userId) { + return userId != null && userId.equals(1); + } + + public static Integer getAdminUserId() { + return 1; + } + + public boolean isAdmin() { + return isAdmin(userId); + } + + @Size(min = 0, max = 30, message = "User nickname length cannot exceed 30 characters") + public String getNickName() { + return nickName; + } + + + @NotBlank(message = "User account cannot be empty") + @Size(min = 0, max = 30, message = "User account length cannot exceed 30 characters") + public String getUserName() { + return userName; + } + + + @Email(message = "E-mail format is incorrect") + @Size(min = 0, max = 50, message = "E-mail length cannot exceed 50 characters") + public String getEmail() { + return email; + } + + + @Size(min = 0, max = 11, message = "Mobile phone number length cannot exceed 11 characters") + public String getPhonenumber() { + return phonenumber; + } + + public void setPhonenumber(String phonenumber) { + this.phonenumber = phonenumber; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/AgentInstallEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/AgentInstallEntity.java new file mode 100644 index 000000000..be92f48f5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/AgentInstallEntity.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.entity.agent; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import org.opengauss.admin.common.enums.agent.AgentStatus; + +import java.time.Instant; + +/** + * AgentInstallEntity + * + * @author: wangchao + * @Date: 2025/4/10 09:31 + * @Description: AgentInstallEntiry + * @since 7.0.0-RC2 + **/ +@Data +@TableName("agent_install") +@EqualsAndHashCode +public class AgentInstallEntity { + @TableId(value = "agent_id", type = IdType.INPUT) + private String agentId; + private String agentIp; + private String agentName; + private Integer agentPort; + private String installPath; + private String installUser; + private String installUserId; + private AgentStatus status; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant createTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant updateTime; + @TableField(exist = false) + private String publicKey; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/GenericCompositeKey.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/GenericCompositeKey.java new file mode 100644 index 000000000..de6aa5651 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/GenericCompositeKey.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.entity.agent; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * GenericCompositeKey + * + * @author: wangchao + * @Date: 2025/4/17 16:01 + * @Description: GenericCompositeKey + * @since 7.0.0-RC2 + **/ +@EqualsAndHashCode +@AllArgsConstructor +@NoArgsConstructor +public class GenericCompositeKey implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private T1 t1; + private T2 t2; + private T3 t3; + private T4 t4; + + @Override + public String toString() { + return t1 + "," + t2 + "," + t3 + "," + t4; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/MetricHistorical.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/MetricHistorical.java new file mode 100644 index 000000000..a114cbc63 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/MetricHistorical.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.entity.agent; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; + +import lombok.Builder; +import lombok.Data; + +import java.time.Instant; + +/** + * MetricHistorical + * + * @author: wangchao + * @Date: 2025/4/17 19:15 + * @Description: MetricHistorical + * @since 7.0.0-RC2 + **/ +@Data +@Builder +@TableName("agent_metric_historical") +public class MetricHistorical { + @TableId(value = "id", type = IdType.INPUT) + private Long id; + private Long taskId; + private Long agentId; + private String clusterNodeId; + private String metric; + private String property; + private String value; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant startTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant endTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant createTime; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/MetricRealTime.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/MetricRealTime.java new file mode 100644 index 000000000..a141a0f38 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/MetricRealTime.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.entity.agent; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import com.fasterxml.jackson.annotation.JsonFormat; + +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.Instant; + +/** + * MetricRealTime + * + * @author: wangchao + * @Date: 2025/4/17 19:15 + * @Description: MetricRealTime + * @since 7.0.0-RC2 + **/ +@Data +@Builder +@EqualsAndHashCode(callSuper = false) +@TableName("agent_metric_real_time") +public class MetricRealTime extends Model { + @TableId(value = "multi_id", type = IdType.INPUT) + private Integer multiId; + private Long agentId; + private String clusterNodeId; + private String metric; + private String property; + private String value; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant startTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant endTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant createTime; + + @Override + public Serializable pkVal() { + return new GenericCompositeKey<>(agentId, clusterNodeId, metric, property); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskInstanceEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskInstanceEntity.java new file mode 100644 index 000000000..5ebd1a018 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskInstanceEntity.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.entity.agent; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskStatus; + +import java.time.Instant; + +/** + * TaskInstanceEntity + * + * @author: wangchao + * @Date: 2025/4/17 16:12 + * @Description: TaskInstanceEntity + * @since 7.0.0-RC2 + **/ +@Data +@EqualsAndHashCode +@TableName("agent_task_instance") +public class TaskInstanceEntity { + @TableId(value = "id", type = IdType.AUTO) + private Long id; + private String taskTemplateId; + private String taskName; + private String agentId; + private String clusterNodeId; + private String clusterOriginal; + private AgentTaskStatus taskStatus; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant startTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant endTime; + private String errorMsg; + private String taskParams; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant createTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant updateTime; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskMetricsDefinition.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskMetricsDefinition.java new file mode 100644 index 000000000..4d92d49a4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskMetricsDefinition.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.entity.agent; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.Instant; + +/** + * TaskMetricsDefinition + * + * @author: wangchao + * @Date: 2025/4/16 09:34 + * @Description: TaskMetricsDefinition + * @since 7.0.0-RC2 + **/ +@Data +@EqualsAndHashCode +@TableName("agent_task_metrics_definition") +public class TaskMetricsDefinition { + @TableId(value = "name", type = IdType.INPUT) + private String name; + private String description; + private String fieldName; + private String unit; + private String dataType; + private String prop; + private String collectCmd; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant createTime; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskSchemaDefinition.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskSchemaDefinition.java new file mode 100644 index 000000000..556b51fdb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskSchemaDefinition.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.entity.agent; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.Instant; + +/** + * TaskSchemaDefinition + * + * @author: wangchao + * @Date: 2025/4/16 09:34 + * @Description: TaskSchemaDefinition + * @since 7.0.0-RC2 + **/ +@Data +@EqualsAndHashCode +@TableName(value = "agent_task_schema_definition") +public class TaskSchemaDefinition { + @TableId(value = "id", type = IdType.INPUT) + private long id; + private String name; + private String metric; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant createTime; +} + diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskTableRelation.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskTableRelation.java new file mode 100644 index 000000000..0548abd87 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskTableRelation.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.entity.agent; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * TaskTableRelation + * + * @author: wangchao + * @Date: 2025/4/17 19:15 + * @Description: TaskTableRelation + * @since 7.0.0-RC2 + **/ +@Data +@EqualsAndHashCode +@TableName("agent_task_table_relation") +public class TaskTableRelation { + @TableId(value = "task_id", type = IdType.INPUT) + private String taskId; + private String taskTableName; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskTemplateDefinition.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskTemplateDefinition.java new file mode 100644 index 000000000..cd02a9b12 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/agent/TaskTemplateDefinition.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.entity.agent; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import org.opengauss.admin.common.enums.agent.ObjectType; +import org.opengauss.admin.common.enums.agent.StoragePolicy; +import org.opengauss.admin.common.enums.agent.TaskType; + +import java.time.Instant; + +/** + * TaskTemplateDefinition + * + * @author: wangchao + * @Date: 2025/4/16 09:34 + * @Description: TaskTemplateDefinition + * @since 7.0.0-RC2 + **/ +@Data +@EqualsAndHashCode +@TableName("agent_task_template_definition") +public class TaskTemplateDefinition { + @TableId(value = "name", type = IdType.INPUT) + private String name; + private TaskType type; + private String groupTag; + private String pluginsTag; + private ObjectType operateObjType; + private String operateObj; + + /** + *
+     * Obtains a Duration from a text string such as PnDTnHnMn.nS.
+     * time period and duration ,1year 2month 3week 4day 5hour 6minutes 7.5second eg. "P1Y2M3W4DT5H6M7.5S"
+     *
+     * The formats accepted are based on the ISO-8601 duration format PnDTnHnMn.nS with days considered to be exactly
+     * 24 hours.
+     * For example, P1Y2M3W4DT5H6M7S is a valid duration.
+     * A date-based amount of time in the ISO-8601 calendar system, such as '2 years, 3 months and 4 days'.
+     * 
+ */ + private String period; + private String collectMetric; + private StoragePolicy storagePolicy; + + /** + *
+     * Obtains a Duration from a text string such as PnDTnHnMn.nS.
+     * time period and duration,1year 2month 3week 4day 5hour 6minutes 7.5second eg.
+     * time period and duration,1year 2month 3week 4day 5hour 6minutes 7.5second eg. "P1Y2M3W4DT5H6M7.5S"
+     *
+     * The formats accepted are based on the ISO-8601 duration format PnDTnHnMn.nS with days considered to be exactly
+     * 24 hours.
+     * For example,      * For example, P1Y2M3W4DT5H6M7S is a valid duration.
+     * A date-based amount of time in the ISO-8601 calendar system, such as '2 years, 3 months and 4 days'.
+     * 
+ */ + private String keepPeriod; + private String receiveApi; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Instant createTime; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsAzEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsAzEntity.java new file mode 100644 index 000000000..b07110dc3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsAzEntity.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsAzEntity.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsAzEntity.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import jakarta.validation.constraints.NotBlank; + +/** + * @author lhf + * @date 2022/8/6 15:53 + **/ +@Data +@TableName("ops_az") +@EqualsAndHashCode(callSuper = true) +public class OpsAzEntity extends BaseEntity { + @TableId + private String azId; + @NotBlank(message = "Name is required") + private String name; + private Integer priority; + @NotBlank(message = "Actual location cannot be empty") + private String address; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsCheckEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsCheckEntity.java new file mode 100644 index 000000000..388eea0e9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsCheckEntity.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsCheckEntity.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsCheckEntity.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author lhf + * @date 2022/11/13 17:15 + **/ +@Data +@TableName("ops_check") +@EqualsAndHashCode(callSuper = true) +public class OpsCheckEntity extends BaseEntity { + @TableId + private String checkId; + private String clusterId; + private String checkRes; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsClusterEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsClusterEntity.java new file mode 100644 index 000000000..884c2237d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsClusterEntity.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterEntity.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsClusterEntity.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.common.core.domain.BaseEntity; +import org.opengauss.admin.common.enums.ops.DeployTypeEnum; +import org.opengauss.admin.common.enums.ops.InstallModeEnum; +import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author lhf + * @date 2022/8/12 09:01 + **/ +@Data +@TableName("ops_cluster") +@EqualsAndHashCode(callSuper = true) +public class OpsClusterEntity extends BaseEntity { + @TableId + private String clusterId; + private OpenGaussVersionEnum version; + private String versionNum; + private InstallModeEnum installMode; + private DeployTypeEnum deployType; + private String clusterName; + private String installPackagePath; + private String databaseUsername; + private String databasePassword; + + private String installPath; + private String logPath; + private String tmpPath; + private String omToolsPath; + private String corePath; + private Integer port; + private Boolean enableDcf; + private Integer dcfPort; + private String envPath; + private String xmlConfigPath; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsClusterNodeEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsClusterNodeEntity.java new file mode 100644 index 000000000..7dee75204 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsClusterNodeEntity.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterNodeEntity.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsClusterNodeEntity.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.common.core.domain.BaseEntity; +import org.opengauss.admin.common.enums.ops.ClusterRoleEnum; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author lhf + * @date 2022/8/18 09:12 + **/ +@Data +@TableName("ops_cluster_node") +@EqualsAndHashCode(callSuper = true) +public class OpsClusterNodeEntity extends BaseEntity { + @TableId + private String clusterNodeId; + private ClusterRoleEnum clusterRole; + private String hostId; + private String installUserId; + private String installPath; + private String dataPath; + private String pkgPath; + private Boolean installDemoDatabase; + private String clusterId; + /** + * Whether to install CM + */ + @TableField("is_install_cm") + private Boolean isInstallCM; + /** + * Whether it is the CM master node + */ + @TableField("is_cm_master") + private Boolean isCMMaster; + /** + * CM data path + */ + private String cmDataPath; + /** + * CM port + */ + private Integer cmPort; + /** + * data node xlog path + */ + private String xlogPath; + + private String dssDataLunLinkPath; + + private String xlogLunLinkPath; + + private String cmVotingLunLinkPath; + + private String cmSharingLunLinkPath; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsEncryptionEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsEncryptionEntity.java new file mode 100644 index 000000000..f2e71411a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsEncryptionEntity.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsEncryptionEntity.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsEncryptionEntity.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author lhf + * @date 2022/11/30 08:56 + **/ +@Data +@TableName("ops_encryption") +@EqualsAndHashCode() +public class OpsEncryptionEntity { + @TableId + private String encryptionId; + private String publicKey; + private String privateKey; + private String keySecurity; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostEntity.java new file mode 100644 index 000000000..745dee6b9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostEntity.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsHostEntity.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops + * /OpsHostEntity.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import org.opengauss.admin.common.core.domain.BaseEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author lhf + * @date 2022/8/6 16:08 + **/ +@Data +@TableName("ops_host") +@EqualsAndHashCode(callSuper = true) +public class OpsHostEntity extends BaseEntity { + @TableId + private String hostId; + private String hostname; + private String privateIp; + private String publicIp; + private Integer port; + private String os; + private String osVersion; + private String osBuild; + private String cpuArch; + private String cpuModel; + private String cpuFreq; + private int physicalCores; + private int logicalCores; + @TableField(exist = false) + private Boolean isRemember; + private String name; + @TableField(exist = false) + private String displayIp; + + /** + * Get the display IP address. + * + * @return display info + */ + public String getDisplayIp() { + return publicIp != null && !publicIp.isEmpty() ? publicIp + "(" + privateIp + ")" : publicIp; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostTagEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostTagEntity.java new file mode 100644 index 000000000..1da5270a6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostTagEntity.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsHostTagEntity.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostTagEntity.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.common.core.domain.BaseEntity; + +/** + * @author lhf + * @date 2023/3/14 23:33 + **/ +@Data +@TableName("ops_host_tag") +@EqualsAndHashCode(callSuper = true) +public class OpsHostTagEntity extends BaseEntity { + @TableId + private String hostTagId; + private String name; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostTagRel.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostTagRel.java new file mode 100644 index 000000000..cc86cfe44 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostTagRel.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsHostTagRel.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostTagRel.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.common.core.domain.BaseEntity; + +/** + * @author lhf + * @date 2023/3/14 23:34 + **/ +@Data +@TableName("ops_host_tag_rel") +@EqualsAndHashCode(callSuper = true) +public class OpsHostTagRel extends BaseEntity { + @TableId + private String id; + private String hostId; + private String tagId; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostUserEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostUserEntity.java new file mode 100644 index 000000000..ddd7aca67 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsHostUserEntity.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsHostUserEntity.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops + * /OpsHostUserEntity.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import org.opengauss.admin.common.constant.CommonConstants; +import org.opengauss.admin.common.core.domain.BaseEntity; + +import cn.hutool.core.util.StrUtil; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author lhf + * @date 2022/8/6 16:09 + **/ +@Data +@TableName("ops_host_user") +@EqualsAndHashCode(callSuper = true) +public class OpsHostUserEntity extends BaseEntity { + @TableId + private String hostUserId; + private String username; + private String password; + private String hostId; + private Boolean sudo; + + /** + * Check if the user is the root user. + * + * @return boolean + */ + public boolean isRootUser() { + return StrUtil.equalsIgnoreCase(username, CommonConstants.ROOT_USER); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsJdbcDbClusterEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsJdbcDbClusterEntity.java new file mode 100644 index 000000000..b4cb65aee --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsJdbcDbClusterEntity.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsJdbcDbClusterEntity.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsJdbcDbClusterEntity.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.common.core.domain.BaseEntity; +import org.opengauss.admin.common.enums.ops.DbTypeEnum; +import org.opengauss.admin.common.enums.ops.DeployTypeEnum; + +/** + * @author lhf + * @date 2023/1/13 10:59 + **/ +@Data +@TableName("ops_jdbcdb_cluster") +@EqualsAndHashCode(callSuper = true) +public class OpsJdbcDbClusterEntity extends BaseEntity { + @TableId + private String clusterId; + private String name; + private DbTypeEnum dbType; + private DeployTypeEnum deployType; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsJdbcDbClusterNodeEntity.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsJdbcDbClusterNodeEntity.java new file mode 100644 index 000000000..149cb371c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsJdbcDbClusterNodeEntity.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsJdbcDbClusterNodeEntity.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/entity/ops/OpsJdbcDbClusterNodeEntity.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.common.core.domain.BaseEntity; + +/** + * @author lhf + * @date 2023/1/13 11:02 + **/ +@Data +@TableName("ops_jdbcdb_cluster_node") +@EqualsAndHashCode(callSuper = true) +public class OpsJdbcDbClusterNodeEntity extends BaseEntity { + @TableId + private String clusterNodeId; + private String clusterId; + private String name; + private String ip; + private String port; + private String username; + private String password; + private String url; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/LoginBody.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/LoginBody.java new file mode 100644 index 000000000..4d5292ecf --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/LoginBody.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * LoginBody.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/LoginBody.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model; + +import lombok.Data; + +/** + * User Login DTO + * + * @author xielibo + */ +@Data +public class LoginBody { + /** + * username + */ + private String username; + + /** + * password + */ + private String password; + + /** + * code + */ + private String code; + +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/LoginUser.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/LoginUser.java new file mode 100644 index 000000000..6fd576b9d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/LoginUser.java @@ -0,0 +1,144 @@ +/** + Copyright ruoyi. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.common.core.domain.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.opengauss.admin.common.core.domain.entity.SysUser; +import lombok.Data; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.Set; + +/** + * Login user VO + * + * @author xielibo + */ +@Data +public class LoginUser implements UserDetails { + private static final long serialVersionUID = 1L; + + /** + * User ID + */ + private Integer userId; + + /** + * Token + */ + private String token; + + /** + * Login IP + */ + private String ipaddr; + + /** + * Login Location + */ + private String loginLocation; + + /** + * Permission List + */ + private Set permissions; + + /** + * User + */ + private SysUser user; + private Collection authorities; + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public LoginUser() { + } + + public LoginUser(SysUser user, Set permissions) { + this.user = user; + this.permissions = permissions; + } + + @Override + public Collection getAuthorities() { + return authorities; + } + + // 添加权限设置方法 + public void setAuthorities(Collection authorities) { + this.authorities = authorities; + } + @JsonIgnore + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public String getUsername() { + return user.getUserName(); + } + + /** + * Whether the account has not expired, expired cannot be verified + */ + @JsonIgnore + @Override + public boolean isAccountNonExpired() { + return true; + } + + /** + * Specifies whether the user is unlocked, and locked users cannot be authenticated + * + * @return + */ + @JsonIgnore + @Override + public boolean isAccountNonLocked() { + return true; + } + + /** + * Indicates whether the user's credentials (password) have expired, expired credentials prevent authentication + * + * @return + */ + @JsonIgnore + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + /** + * Whether it is available, disabled users cannot authenticate + * + * @return + */ + @JsonIgnore + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentClusterVo.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentClusterVo.java new file mode 100644 index 000000000..c39b6290e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentClusterVo.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.core.domain.model.agent; + +import lombok.Data; + +/** + * AgentClusterVo + * + * @author: wangchao + * @date: 2025/4/18 11:40 + * @since 7.0.0-RC2 + **/ +@Data +public class AgentClusterVo { + private String clusterNodeId; + private String dataBaseType; + private String hostIp; + private String port; + private String username; + private String dbPassword; + private String url; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentHeartbeatStatus.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentHeartbeatStatus.java new file mode 100644 index 000000000..b010673fc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentHeartbeatStatus.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.model.agent; + +import java.time.Duration; +import java.time.Instant; + +/** + * AgentHeartbeatStatus + * + * @author: wangchao + * @Date: 2025/3/31 11:48 + * @Description: AgentHeartbeatStatus + * @since 7.0.0-RC2 + **/ +public class AgentHeartbeatStatus { + private static final long TIMEOUT_SEC = 3L; + + private volatile Instant lastHeartbeat = Instant.now(); + private volatile boolean isOnline; + private volatile boolean isStatusChanged; + + /** + * update the last heartbeat time + * and mark the agent as online + * + * @param heartbeatTime the time of the last heartbeat + */ + public void updateFromHeartbeat(Instant heartbeatTime) { + this.lastHeartbeat = heartbeatTime; + if (!isOnline) { + isOnline = true; + isStatusChanged = true; + } + } + + @Override + public String toString() { + return "[isOnline=" + isOnline + ", isChanged=" + isStatusChanged + " lastHeartbeat=" + lastHeartbeat + ']'; + } + + /** + * get the last heartbeat time + * + * @return the last heartbeat time + */ + public Instant getLastHeartbeat() { + return lastHeartbeat; + } + + /** + * get the status duration + * + * @return the status duration + */ + public Duration getStatusDuration() { + if (lastHeartbeat == null) { + return Duration.ZERO; + } + return Duration.between(lastHeartbeat, Instant.now()); + } + + /** + * check if the status has changed + * + * @return true if the status has changed, false otherwise + */ + public boolean checkAndResetStatusChanged() { + if (isStatusChanged) { + isStatusChanged = false; + return true; + } + return false; + } + + /** + * update the last heartbeat time + * and mark the agent as online + * + * @param time the time of the last heartbeat + */ + public void updateLastHeartbeatTime(Instant time) { + this.lastHeartbeat = time; + this.isOnline = true; // 收到心跳即标记为Up + } + + /** + * mark the agent as down + */ + public void markAsDown() { + this.isOnline = false; + } + + /** + * check if the agent is online (i.e. last heartbeat received within the timeout period) + * + * @return true if online, false otherwise + */ + public boolean isOnline() { + return isOnline; + } + + /** + * check if the agent is expired (i.e. no heartbeat received for a while) + * + * @return true if expired, false otherwise + */ + public boolean isExpired() { + if (lastHeartbeat == null) { + return true; + } + return Duration.between(lastHeartbeat, Instant.now()).getSeconds() > TIMEOUT_SEC; + } + + /** + * update the online status based on the last heartbeat time + * + * @return true if the status changed, false otherwise + */ + public boolean updateOnlineStatus() { + boolean shouldBeOnline = !isExpired(); + if (isOnline != shouldBeOnline) { + isOnline = shouldBeOnline; + isStatusChanged = true; + return true; + } + return false; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentStartResult.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentStartResult.java new file mode 100644 index 000000000..75e7796c2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentStartResult.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.model.agent; + +/** + * Agent start result + * + * @param agentId agent id + * @param isSuccess start success or not + * @param message start message + *

+ * Agent start result is non-changed. + * @author wangchao + * @since 7.0.0-RC2 + */ +public record AgentStartResult(String agentId, Boolean isSuccess, String message) { +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentTaskConfig.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentTaskConfig.java new file mode 100644 index 000000000..07b497b1d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentTaskConfig.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.model.agent; + +import lombok.Data; + +import org.opengauss.admin.common.core.domain.entity.agent.TaskMetricsDefinition; +import org.opengauss.admin.common.core.domain.entity.agent.TaskTemplateDefinition; +import org.opengauss.admin.common.enums.agent.ObjectType; + +import java.util.List; +import java.util.Objects; + +/** + * AgentTaskConfig + * + * @author: wangchao + * @Date: 2025/4/21 16:34 + * @Description: AgentTaskConfig + * @since 7.0.0-RC2 + **/ +@Data +public class AgentTaskConfig { + private Long taskId; + private String taskName; + private String agentId; + private TaskTemplateDefinition templateDefinition; + private List collectorMetricDetails; + private List metricsDefinitionList; + private AgentClusterVo clusterConfig; + + /** + * AgentTaskConfig + * + * @param templateDefinition templateDefinition + */ + public AgentTaskConfig(TaskTemplateDefinition templateDefinition) { + this.templateDefinition = templateDefinition; + } + + /** + * check if the task template is db template + * + * @return boolean + */ + public boolean isDbTemplate() { + return Objects.equals(templateDefinition.getOperateObjType(), ObjectType.DB); + } + + /** + * get collect metric name + * + * @return collect metric name + */ + public String getCollectMetric() { + return templateDefinition.getCollectMetric(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentTaskParam.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentTaskParam.java new file mode 100644 index 000000000..3252e7295 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentTaskParam.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.model.agent; + +import lombok.Builder; +import lombok.Data; + +import org.opengauss.admin.common.core.domain.entity.agent.TaskInstanceEntity; +import org.springframework.beans.BeanUtils; + +import java.time.Instant; + +/** + * AgentTaskParam + * + * @author: wangchao + * @Date: 2025/4/17 16:01 + * @Description: AgentTaskParam + * @since 7.0.0-RC2 + **/ +@Builder +@Data +public class AgentTaskParam { + private String taskName; + private String taskTemplateId; + private String agentId; + private String clusterNodeId; + private String clusterOriginal; + + /** + * Convert to TaskInstanceEntity + * + * @return TaskInstanceEntity + */ + public TaskInstanceEntity toTaskInstanceEntity() { + TaskInstanceEntity taskInstanceEntity = new TaskInstanceEntity(); + BeanUtils.copyProperties(this, taskInstanceEntity); + taskInstanceEntity.setTaskStatus(AgentTaskStatus.NO_START); + taskInstanceEntity.setCreateTime(Instant.now()); + taskInstanceEntity.setUpdateTime(Instant.now()); + return taskInstanceEntity; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentTaskStatus.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentTaskStatus.java new file mode 100644 index 000000000..4a34bcf88 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/AgentTaskStatus.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.model.agent; + +import com.baomidou.mybatisplus.annotation.IEnum; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * AgentTaskStatus + * + * @author: wangchao + * @Date: 2025/4/17 16:01 + * @Description: AgentTaskStatus + * @since 7.0.0-RC2 + **/ +@Getter +@AllArgsConstructor +public enum AgentTaskStatus implements IEnum { + NO_START("no_start"), + RUNNING("running"), + SUCCESS("success"), + FAILED("failed"), + CANCEL("cancel"), + STOP("stop"); + + private final String value; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/HeartbeatHeader.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/HeartbeatHeader.java new file mode 100644 index 000000000..9554a4332 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/HeartbeatHeader.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.model.agent; + +import lombok.Data; + +/** + * HeartbeatHeader + * + * @author: wangchao + * @Date: 2025/4/17 16:01 + * @Description: HeartbeatHeader + * @since 7.0.0-RC2 + **/ +@Data +public class HeartbeatHeader { + private String agentName; + private String instanceId; + private String agentAddress; + private String target; + + public HeartbeatHeader(String agentName, String instanceId, String agentAddress, String target) { + this.agentName = agentName; + this.instanceId = instanceId; + this.agentAddress = agentAddress; + this.target = target; + } + + /** + * Convert to heartbeat header string. + * + * @return heartbeat header string + */ + public String toHeartbeatHeader() { + return agentName + ":" + agentAddress + " : " + instanceId + "->" + target; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/HeartbeatReport.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/HeartbeatReport.java new file mode 100644 index 000000000..0ff0bc933 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/HeartbeatReport.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.model.agent; + +import cn.hutool.core.util.StrUtil; +import lombok.Data; +import lombok.NoArgsConstructor; + +import org.opengauss.admin.common.constant.AgentConstants; + +import java.time.Instant; + +/** + * HeartbeatReport + * + * @author: wangchao + * @Date: 2025/4/17 16:01 + * @Description: HeartbeatReport + * @since 7.0.0-RC2 + **/ +@Data +@NoArgsConstructor +public class HeartbeatReport { + private String agentId; + private Instant timestamps; + private String status; + private String additionalInfo; + + /** + * Constructor + * + * @param agentId agent id + * @param timestamps timestamp + */ + public HeartbeatReport(String agentId, Instant timestamps) { + this(agentId, timestamps, AgentConstants.Status.HEARTBEAT_STATUS_UP); + } + + /** + * Constructor + * + * @param agentId agent id + * @param timestamps timestamp + * @param status status + */ + public HeartbeatReport(String agentId, Instant timestamps, String status) { + this.agentId = agentId; + this.timestamps = timestamps; + this.status = status; + } + + /** + * Convert to heartbeat report string. + * + * @return heartbeat report string + */ + public String toHeartbeat() { + if (StrUtil.isEmpty(additionalInfo)) { + return "send heartbeat report " + status + ", at " + timestamps; + } + return "send heartbeat report " + status + ", at " + timestamps + ", additionalInfo: " + additionalInfo; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/HostBaseInfo.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/HostBaseInfo.java new file mode 100644 index 000000000..c509b70c5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/HostBaseInfo.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.model.agent; + +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * HostBaseInfo + * + * @author: wangchao + * @Date: 2025/4/8 17:18 + * @Description: HostBaseInfo + * @since 7.0.0-RC2 + **/ +@Data +@Builder +@Accessors(chain = true) +public class HostBaseInfo { + private String agentId; + private String hostName; + private String cpuModel; + private String cpuArchitecture; + private String cpuFreq; + private int physicalCores; + private int logicalCores; + private String osName; + private String osVersion; + private String osBuild; +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/TaskTemplate.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/TaskTemplate.java new file mode 100644 index 000000000..9936529e4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/agent/TaskTemplate.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.domain.model.agent; + +import lombok.Data; + +import org.opengauss.admin.common.core.domain.entity.agent.TaskMetricsDefinition; +import org.opengauss.admin.common.core.domain.entity.agent.TaskSchemaDefinition; +import org.opengauss.admin.common.core.domain.entity.agent.TaskTemplateDefinition; + +import java.util.List; + +/** + * TaskTemplate + * + * @author: wangchao + * @Date: 2025/4/17 16:04 + * @Description: TaskTemplate + * @since 7.0.0-RC2 + **/ +@Data +public class TaskTemplate { + private String taskTemplateId; + private TaskTemplateDefinition templateDefinition; + private List schemaDefinitionList; + private List metricsDefinitionList; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ClusterSummaryVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ClusterSummaryVO.java new file mode 100644 index 000000000..f504dfa5b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ClusterSummaryVO.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterSummaryVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ClusterSummaryVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/23 23:21 + **/ +@Data +public class ClusterSummaryVO { + private Long clusterNum; + private Long hostNum; + private Long nodeNum; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ErrorHostRecord.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ErrorHostRecord.java new file mode 100644 index 000000000..e0df3df6e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ErrorHostRecord.java @@ -0,0 +1,50 @@ +package org.opengauss.admin.common.core.domain.model.ops; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.opengauss.admin.common.utils.excel.TagsConverter; + +import java.util.List; + +/** + * Error Report Class. + * + * @author zzh + * @version 1.0 + * @data 2024/6/25 10:35 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@ColumnWidth(15) +public class ErrorHostRecord { + @ExcelProperty("序号") + private Integer id; + @ExcelProperty("服务器名称") + private String name; + @ExcelProperty("内网IP") + private String privateIp; + @ExcelProperty("公网IP") + private String publicIp; + @ExcelProperty("端口号") + private Integer port; + @ExcelProperty("用户名称") + private String userName; + @ExcelProperty("用户密码") + @ToString.Exclude + private String password; + @ExcelProperty(value = "是否为管理员(是|否)") + private String isAdmin; + @ExcelProperty(value = "标签", converter = TagsConverter.class) + private List tags; + @ColumnWidth(25) + @ExcelProperty("备注") + private String remark; + @ColumnWidth(20) + @ExcelProperty("时间戳") + private String timestamp; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/Host.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/Host.java new file mode 100644 index 000000000..4f8ad2bb6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/Host.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * Host.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/Host.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import lombok.Data; + +import java.util.List; + +/** + * Host Information + * + * @author lhf + * @date 2022/8/4 22:12 + **/ +@Data +public class Host { + private String id; + /** + * The host address + */ + private String host; + /** + * Host SSH Port + */ + private Integer port; + /** + * Host authentication Certificate + */ + private List hostCredentials; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostBody.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostBody.java new file mode 100644 index 000000000..2e7837c03 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostBody.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostBody.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostBody.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import cn.hutool.core.util.StrUtil; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.constant.CommonConstants; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.core.vo.HostInfoVo; + +import jakarta.validation.constraints.NotEmpty; + +import java.util.List; +import java.util.Objects; + +/** + * @author lhf + * @date 2022/8/11 20:07 + **/ +@Slf4j +@Data +public class HostBody { + @NotEmpty(message = "The IP address cannot be empty") + private String publicIp; + @NotEmpty(message = "The Intranet IP address cannot be empty") + private String privateIp; + private String password; + private Boolean isRemember; + private String azId; + private String remark; + private Integer port; + private String name; + private List tags; + private String username; + + public OpsHostEntity toHostEntity(HostInfoVo hostInfoVo) { + OpsHostEntity hostEntity = new OpsHostEntity(); + hostEntity.setPublicIp(publicIp); + hostEntity.setPrivateIp(privateIp); + hostEntity.setPort(port); + hostEntity.setHostname(hostInfoVo.getHostname()); + hostEntity.setRemark(remark); + hostEntity.setOs(hostInfoVo.getOs()); + hostEntity.setOsVersion(hostInfoVo.getOsVersion()); + hostEntity.setCpuArch(hostInfoVo.getCpuArch()); + hostEntity.setName(name); + return hostEntity; + } + + /** + * Convert to user entity + * + * @param hostId hostId + * @return User + */ + public OpsHostUserEntity toUser(String hostId) { + OpsHostUserEntity hostUserEntity = new OpsHostUserEntity(); + hostUserEntity.setUsername(username); + hostUserEntity.setHostId(hostId); + if (hostUserEntity.isRootUser()) { + hostUserEntity.setSudo(Boolean.TRUE); + } + if (Objects.nonNull(isRemember) && isRemember) { + hostUserEntity.setPassword(password); + } + return hostUserEntity; + } + + /** + * Convert to user body + * + * @param hostId hostId + * @return User + */ + public HostUserBody toUserBody(String hostId) { + HostUserBody hostUserBody = new HostUserBody(); + hostUserBody.setUsername(username); + hostUserBody.setHostId(hostId); + if (StrUtil.equalsIgnoreCase(username, CommonConstants.ROOT_USER)) { + hostUserBody.setSudo(Boolean.TRUE); + } + hostUserBody.setPassword(password); + return hostUserBody; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostCredentials.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostCredentials.java new file mode 100644 index 000000000..fe66c5cad --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostCredentials.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostCredentials.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostCredentials.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import org.opengauss.admin.common.enums.ops.HostAuthenticationTypeEnum; +import lombok.Data; + +/** + * Host credential + * + * @author lhf + * @date 2022/8/4 22:17 + **/ +@Data +public class HostCredentials { + + private String principal; + + private String credentials; + + private HostAuthenticationTypeEnum authenticationType; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostFile.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostFile.java new file mode 100644 index 000000000..77d45c186 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostFile.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostFile.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostFile.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import lombok.Data; +import org.opengauss.admin.common.enums.ops.HostFileTypeEnum; + +import java.io.File; + +/** + * @author lhf + * @date 2022/8/7 22:37 + **/ +@Data +public class HostFile { + + private String name; + + private HostFileTypeEnum type; + /** + * size(byte) + */ + private Long size; + + public static HostFile build(File file) { + HostFile hostFile = new HostFile(); + String fileName = file.getName(); + HostFileTypeEnum fileType = file.isFile() ? HostFileTypeEnum.FILE : HostFileTypeEnum.DIRECTORY; + long length = file.length(); + + hostFile.setName(fileName); + hostFile.setType(fileType); + hostFile.setSize(length); + + return hostFile; + } + + public static HostFile of(String filename, HostFileTypeEnum hostFileTypeEnum, long size) { + HostFile hostFile = new HostFile(); + hostFile.setName(filename); + hostFile.setType(hostFileTypeEnum); + hostFile.setSize(size); + return hostFile; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostInfoHolder.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostInfoHolder.java new file mode 100644 index 000000000..4e0eea05c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostInfoHolder.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostInfoHolder.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostInfoHolder.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author lhf + * @date 2022/8/13 11:50 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class HostInfoHolder { + private OpsHostEntity hostEntity; + private List hostUserEntities; +} + diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostRecord.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostRecord.java new file mode 100644 index 000000000..df88f2d07 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostRecord.java @@ -0,0 +1,97 @@ +package org.opengauss.admin.common.core.domain.model.ops; + +import cn.hutool.core.bean.BeanUtil; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.core.vo.HostInfoVo; +import org.opengauss.admin.common.utils.excel.TagsConverter; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +/** + * Excel Host Record Entity Class. + * + * @author zzh + * @version 1.0 + * @data 2024/6/25 10:35 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@ColumnWidth(15) +public class HostRecord { + @ExcelProperty(value = "序号", index = 0) + private Integer id; + @ExcelProperty(value = "服务器名称", index = 1) + private String name; + @ExcelProperty(value = "内网IP", index = 2) + @NotEmpty(message = "The Intranet IP address cannot be empty") + private String privateIp; + @ExcelProperty(value = "公网IP", index = 3) + @NotNull(message = "The IP address cannot be empty") + private String publicIp; + @NotNull(message = "The IP address cannot be empty") + @ExcelProperty(value = "端口号", index = 4) + private Integer port; + @ExcelProperty(value = "用户名称", index = 5) + private String userName; + @ExcelProperty(value = "用户密码", index = 6) + @ToString.Exclude + private String password; + @ExcelProperty(value = "是否为管理员(是|否)", index = 7) + private String isAdmin; + @ExcelProperty(value = "标签", converter = TagsConverter.class, index = 8) + private List tags; + @ExcelProperty(value = "备注", index = 9) + private String remark; + + public OpsHostEntity toHostEntity(HostInfoVo hostInfoVo) { + OpsHostEntity hostEntity = new OpsHostEntity(); + hostEntity.setPublicIp(publicIp); + hostEntity.setPrivateIp(privateIp); + hostEntity.setPort(port); + hostEntity.setHostname(hostInfoVo.getHostname()); + hostEntity.setRemark(remark); + hostEntity.setIsRemember(Boolean.TRUE); + hostEntity.setOs(hostInfoVo.getOs()); + hostEntity.setOsVersion(hostInfoVo.getOsVersion()); + hostEntity.setCpuArch(hostInfoVo.getCpuArch()); + hostEntity.setName(name); + return hostEntity; + } + + public OpsHostUserEntity toUser(String hostId) { + OpsHostUserEntity hostUserEntity = new OpsHostUserEntity(); + hostUserEntity.setUsername(userName); + hostUserEntity.setHostId(hostId); + hostUserEntity.setSudo(Boolean.TRUE); + return hostUserEntity; + } + + public HostBody toHostBody() { + HostBody hostBody = new HostBody(); + BeanUtil.copyProperties(this, hostBody); + hostBody.setIsRemember(true); + hostBody.setUsername(userName); + return hostBody; + } + + public ErrorHostRecord toErrorHostRecord() { + ErrorHostRecord errorHostRecord = new ErrorHostRecord(); + BeanUtil.copyProperties(this, errorHostRecord); + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss:dd:MM:yyyy"); + String formattedDate = sdf.format(new Date()); + errorHostRecord.setTimestamp(formattedDate); + return errorHostRecord; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostUserBody.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostUserBody.java new file mode 100644 index 000000000..a3f2c3ae4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostUserBody.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostUserBody.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/HostUserBody.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; + +/** + * @author lhf + * @date 2022/8/17 18:01 + **/ +@Data +public class HostUserBody { + @NotBlank(message = "The host ID cannot be empty") + private String hostId; + @NotBlank(message = "The user name cannot be empty") + private String username; + @NotBlank(message = "The password cannot be empty") + private String password; + private String rootPassword; + private Boolean sudo; + + public OpsHostUserEntity toEntity(Boolean sudo) { + OpsHostUserEntity hostUserEntity = new OpsHostUserEntity(); + hostUserEntity.setSudo(sudo); + hostUserEntity.setHostId(hostId); + hostUserEntity.setUsername(username); + hostUserEntity.setPassword(password); + return hostUserEntity; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ImportAsynInfo.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ImportAsynInfo.java new file mode 100644 index 000000000..c59d1ac56 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ImportAsynInfo.java @@ -0,0 +1,32 @@ +package org.opengauss.admin.common.core.domain.model.ops; + +import lombok.Data; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * Progress Information Class. + * + * @author zzh + * @version 1.0 + * @data 2024/6/25 10:35 + */ +@Data +public class ImportAsynInfo { + private String msg; + private Integer totality = 0; + private Integer doneSum = 0; + private Integer errorSum = 0; + private Integer successSum = 0; + public Boolean isEnd = false; + + public Boolean getEnd() { + return isEnd; + } + + public void setEnd(Boolean end) { + isEnd = end; + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/JschResult.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/JschResult.java new file mode 100644 index 000000000..19663764b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/JschResult.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JschResult.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/JschResult.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/8/10 14:08 + **/ +@Data +public class JschResult { + private Integer exitCode; + private String result; + + public boolean isOk() { + return exitCode != null && exitCode == 0; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ListDir.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ListDir.java new file mode 100644 index 000000000..c36f04b0a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ListDir.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ListDir.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/ListDir.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import lombok.Data; + +import java.util.List; + +/** + * @author lhf + * @date 2022/8/7 22:22 + **/ +@Data +public class ListDir { + private String path; + private List files; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/NodeMonitorVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/NodeMonitorVO.java new file mode 100644 index 000000000..64d1bc6b4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/NodeMonitorVO.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * NodeMonitorVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/NodeMonitorVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * @author lhf + * @date 2022/10/9 22:40 + **/ +@Data +public class NodeMonitorVO { + private String cpu; + private String memory; + private List net; + private String lock; + private String session; + private String connectNum; + private String kernel; + private String memorySize; + private List> sessionMemoryTop10; + private String state; + private Long time; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/NodeNetMonitor.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/NodeNetMonitor.java new file mode 100644 index 000000000..ddfaef0c5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/NodeNetMonitor.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * NodeNetMonitor.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/NodeNetMonitor.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/9 22:43 + **/ +@Data +public class NodeNetMonitor { + private String face; + private String receive; + private String transmit; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsClusterContext.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsClusterContext.java new file mode 100644 index 000000000..9d6bb0dd9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsClusterContext.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterContext.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsClusterContext.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity; +import lombok.Data; + +import java.util.List; + +/** + * @author lhf + * @date 2022/9/29 17:31 + **/ +@Data +public class OpsClusterContext implements Cloneable { + private WsSession retSession; + + private List opNodeIds; + + private List hostInfoHolders; + + private OpsClusterEntity opsClusterEntity; + + private List opsClusterNodeEntityList; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsClusterNodeVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsClusterNodeVO.java new file mode 100644 index 000000000..b568a9f19 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsClusterNodeVO.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterNodeVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsClusterNodeVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import lombok.Data; +import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity; + +/** + * @author lhf + * @date 2022/9/26 17:39 + **/ +@Data +public class OpsClusterNodeVO { + private String nodeId; + private String clusterRole; + private String publicIp; + private String privateIp; + private String hostname; + private String azName; + private String azAddress; + private String hostId; + private Integer dbPort; + private String dbName; + private String dbUser; + private String dbUserPassword; + private Integer hostPort; + //private String rootPassword; + private Boolean isRemember; + private String installUserName; + private String installPath; + private String dataPath; + private String hostCpuArch; + private String hostOs; + + public static OpsClusterNodeVO of(OpsClusterNodeEntity opsClusterNodeEntity) { + OpsClusterNodeVO opsClusterNodeVO = new OpsClusterNodeVO(); + opsClusterNodeVO.setNodeId(opsClusterNodeEntity.getClusterNodeId()); + opsClusterNodeVO.setClusterRole(opsClusterNodeEntity.getClusterRole().name()); + opsClusterNodeVO.setInstallPath(opsClusterNodeEntity.getInstallPath()); + opsClusterNodeVO.setDataPath(opsClusterNodeEntity.getDataPath()); + return opsClusterNodeVO; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsClusterVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsClusterVO.java new file mode 100644 index 000000000..cb132ceee --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsClusterVO.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsClusterVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import com.fasterxml.jackson.annotation.JsonFormat; +import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.common.enums.ops.DeployTypeEnum; +import lombok.Data; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author lhf + * @date 2022/9/26 17:34 + **/ +@Data +public class OpsClusterVO { + + private String clusterId; + private String clusterName; + private String version; + private String versionNum; + private String databasePassword; + private DeployTypeEnum deployType; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date lastCheckAt; + + private Map checkSummary; + + private List clusterNodes; + private String envPath; + private String clusterState; + + public static OpsClusterVO of(OpsClusterEntity opsClusterEntity) { + OpsClusterVO opsClusterVO = new OpsClusterVO(); + opsClusterVO.setClusterId(opsClusterEntity.getClusterId()); + opsClusterVO.setClusterName(opsClusterEntity.getClusterId()); + opsClusterVO.setVersion(opsClusterEntity.getVersion().name()); + opsClusterVO.setVersionNum(opsClusterEntity.getVersionNum()); + opsClusterVO.setDatabasePassword(opsClusterEntity.getDatabasePassword()); + opsClusterVO.setDeployType(opsClusterEntity.getDeployType()); + opsClusterVO.setEnvPath(opsClusterEntity.getEnvPath()); + return opsClusterVO; + } + + /** + * get other nodes in ops cluster + * + * @param exceptNodeId except node id + * @return List + */ + public List getOtherNodes(String exceptNodeId) { + return clusterNodes.stream() + .filter(node -> !exceptNodeId.equals(node.getNodeId())) + .collect(Collectors.toList()); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsNodeLogVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsNodeLogVO.java new file mode 100644 index 000000000..70be6a156 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsNodeLogVO.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsNodeLogVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/OpsNodeLogVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import lombok.Data; + +/** + * https://opengauss.org/zh/docs/3.0.0/docs/Administratorguide/%E6%97%A5%E5%BF%97%E5%8F%82%E8%80%83.html + * + * @author lhf + * @date 2022/10/12 17:08 + **/ +@Data +public class OpsNodeLogVO { + /** + * System Log path + */ + private String systemLogPath; + /** + * Path of operation logs. If the environment variable $GAUSSLOG does not exist or is empty, tool logs are not recorded in corresponding tool log files and are only printed on the screen + */ + private String opLogPath; + /** + * Black Box Log Path + */ + private String dumpLogPath; + /** + * Performance log path + */ + private String performanceLogPath; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/SessionVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/SessionVO.java new file mode 100644 index 000000000..7944f3503 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/SessionVO.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SessionVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/SessionVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 15:00 + **/ +@Data +public class SessionVO { + private String datid; + private String datname; + private String pid; + private String sessionid; + private String usesysid; + private String usename; + private String application_name; + private String client_addr; + private String client_hostname; + private String client_port; + private String backend_start; + private String xact_start; + private String query_start; + private String state_change; + private String waiting; + private String enqueue; + private String state; + private String resource_pool; + private String query_id; + private String query; + private String connection_info; + private String unique_sql_id; + private String trace_id; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/WsSession.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/WsSession.java new file mode 100644 index 000000000..4035e73b6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/WsSession.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WsSession.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/WsSession.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import jakarta.websocket.Session; +import java.io.IOException; + +/** + * @author lhf + * @date 2022/8/9 10:00 + **/ +@Slf4j +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WsSession { + private Session session; + private String businessId; + + public void close() { + try { + session.close(); + } catch (IOException e) { + log.error("Failed to close the ws connection. Procedure", e); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckClusterVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckClusterVO.java new file mode 100644 index 000000000..58a90f939 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckClusterVO.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckClusterVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckClusterVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.check; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 18:35 + **/ +@Data +public class CheckClusterVO { + private CheckItemVO checkClusterState; + private CheckItemVO checkDBParams; + private CheckItemVO checkDebugSwitch; + private CheckItemVO checkDirPermissions; + private CheckItemVO checkEnvProfile; + private CheckItemVO checkReadonlyMode; + private CheckItemVO checkDilateSysTab; + private CheckItemVO checkProStartTime; + private CheckItemVO checkMpprcFile; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckDbVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckDbVO.java new file mode 100644 index 000000000..399183ab1 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckDbVO.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckDbVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckDbVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.check; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 18:36 + **/ +@Data +public class CheckDbVO { + private CheckItemVO checkCurConnCount; + private CheckItemVO checkCursorNum; + private CheckItemVO checkPgxcgroup; + private CheckItemVO checkTableSpace; + private CheckItemVO checkSysadminUser; + private CheckItemVO checkHashIndex; + private CheckItemVO checkPgxcRedistb; + private CheckItemVO checkNodeGroupName; + private CheckItemVO checkTDDate; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckDeviceVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckDeviceVO.java new file mode 100644 index 000000000..5ad02fd7b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckDeviceVO.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckDeviceVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckDeviceVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.check; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 18:36 + **/ +@Data +public class CheckDeviceVO { + private CheckItemVO checkBlockdev; + private CheckItemVO checkDiskFormat; + private CheckItemVO checkSpaceUsage; + private CheckItemVO checkInodeUsage; + private CheckItemVO checkSwapMemory; + private CheckItemVO checkLogicalBlock; + private CheckItemVO checkIOrequestqueue; + private CheckItemVO checkMaxAsyIOrequests; + private CheckItemVO checkIOConfigure; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckItemVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckItemVO.java new file mode 100644 index 000000000..ca6465512 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckItemVO.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckItemVO.java + * + * IDENTIFICATION + * visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckItemVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.check; + +import lombok.Data; + +/** + * CheckItemVO + * + * @author lhf + * @date 2022/10/21 18:38 + **/ +@Data +public class CheckItemVO { + private String name = ""; + private String status = ""; + private String msg = ""; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckNetworkVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckNetworkVO.java new file mode 100644 index 000000000..2edeea332 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckNetworkVO.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckNetworkVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckNetworkVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.check; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 18:35 + **/ +@Data +public class CheckNetworkVO { + private CheckItemVO checkMTU; + private CheckItemVO checkPing; + private CheckItemVO checkRXTX; + private CheckItemVO checkNetWorkDrop; + private CheckItemVO checkMultiQueue; + private CheckItemVO checkRouting; + private CheckItemVO checkNICModel; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckOSVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckOSVO.java new file mode 100644 index 000000000..78d65d495 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckOSVO.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckOSVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckOSVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.check; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 18:35 + **/ +@Data +public class CheckOSVO { + private CheckItemVO checkEncoding; + private CheckItemVO checkFirewall; + private CheckItemVO checkKernelVer; + private CheckItemVO checkMaxHandle; + private CheckItemVO checkNTPD; + private CheckItemVO checkOSVer; + private CheckItemVO checkSysParams; + private CheckItemVO checkTHP; + private CheckItemVO checkTimeZone; + private CheckItemVO checkCPU; + private CheckItemVO checkSshdService; + private CheckItemVO checkSshdConfig; + private CheckItemVO checkCrondService; + private CheckItemVO checkStack; + private CheckItemVO checkSysPortRange; + private CheckItemVO checkMemInfo; + private CheckItemVO checkHyperThread; + private CheckItemVO checkMaxProcMemory; + private CheckItemVO checkBootItems; + private CheckItemVO checkKeyProAdj; + private CheckItemVO checkFilehandle; + private CheckItemVO checkDropCache; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckOtherVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckOtherVO.java new file mode 100644 index 000000000..d4dbff9bb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckOtherVO.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckOtherVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckOtherVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.check; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 18:36 + **/ +@Data +public class CheckOtherVO { + private CheckItemVO using; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckSummaryVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckSummaryVO.java new file mode 100644 index 000000000..0e4de4c15 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckSummaryVO.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckSummaryVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckSummaryVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.check; + +import lombok.Data; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * @author lhf + * @date 2022/11/13 17:22 + **/ +@Data +public class CheckSummaryVO { + private CheckVO checkVO; + private Map> summary; + + public static CheckSummaryVO of(CheckVO checkVO) { + CheckSummaryVO checkSummaryVO = new CheckSummaryVO(); + if (Objects.nonNull(checkVO)) { + checkSummaryVO.setCheckVO(checkVO); + checkSummaryVO.setSummary(checkVO.summary()); + } + return checkSummaryVO; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckVO.java new file mode 100644 index 000000000..cdb78ad5d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check/CheckVO.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/check + * /CheckVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.check; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.util.*; + +/** + * @author lhf + * @date 2022/10/21 18:34 + **/ +@Slf4j +@Data +public class CheckVO { + /** + * cluster + */ + private CheckClusterVO cluster; + /** + * database + */ + private CheckDbVO db; + /** + * os + */ + private CheckOSVO os; + /** + * device + */ + private CheckDeviceVO device; + /** + * network + */ + private CheckNetworkVO network; + + public Map> summary() { + Map> summary = new HashMap<>(); + Field[] clusterField = cluster.getClass().getDeclaredFields(); + for (Field declaredField : clusterField) { + declaredField.setAccessible(true); + try { + CheckItemVO checkItemVO = (CheckItemVO) declaredField.get(cluster); + if (Objects.isNull(checkItemVO)) { + continue; + } + String status = checkItemVO.getStatus(); + List checkItemVOS = summary.get(status); + if (Objects.isNull(checkItemVOS)) { + checkItemVOS = new ArrayList<>(); + summary.put(status, checkItemVOS); + } + checkItemVOS.add(checkItemVO); + } catch (Exception e) { + log.error("get fail", e); + } + } + Field[] dbField = db.getClass().getDeclaredFields(); + for (Field declaredField : dbField) { + declaredField.setAccessible(true); + try { + CheckItemVO checkItemVO = (CheckItemVO) declaredField.get(db); + if (Objects.isNull(checkItemVO)) { + continue; + } + String status = checkItemVO.getStatus(); + List checkItemVOS = summary.get(status); + if (Objects.isNull(checkItemVOS)) { + checkItemVOS = new ArrayList<>(); + summary.put(status, checkItemVOS); + } + checkItemVOS.add(checkItemVO); + } catch (Exception e) { + log.error("get fail", e); + } + } + Field[] osField = os.getClass().getDeclaredFields(); + for (Field declaredField : osField) { + declaredField.setAccessible(true); + try { + CheckItemVO checkItemVO = (CheckItemVO) declaredField.get(os); + if (Objects.isNull(checkItemVO)) { + continue; + } + String status = checkItemVO.getStatus(); + List checkItemVOS = summary.get(status); + if (Objects.isNull(checkItemVOS)) { + checkItemVOS = new ArrayList<>(); + summary.put(status, checkItemVOS); + } + checkItemVOS.add(checkItemVO); + } catch (Exception e) { + log.error("get fail", e); + } + } + Field[] deviceField = device.getClass().getDeclaredFields(); + for (Field declaredField : deviceField) { + declaredField.setAccessible(true); + try { + CheckItemVO checkItemVO = (CheckItemVO) declaredField.get(device); + if (Objects.isNull(checkItemVO)) { + continue; + } + String status = checkItemVO.getStatus(); + List checkItemVOS = summary.get(status); + if (Objects.isNull(checkItemVOS)) { + checkItemVOS = new ArrayList<>(); + summary.put(status, checkItemVOS); + } + checkItemVOS.add(checkItemVO); + } catch (Exception e) { + log.error("get fail", e); + } + } + Field[] networkField = network.getClass().getDeclaredFields(); + for (Field declaredField : networkField) { + declaredField.setAccessible(true); + try { + CheckItemVO checkItemVO = (CheckItemVO) declaredField.get(network); + if (Objects.isNull(checkItemVO)) { + continue; + } + String status = checkItemVO.getStatus(); + List checkItemVOS = summary.get(status); + if (Objects.isNull(checkItemVOS)) { + checkItemVOS = new ArrayList<>(); + summary.put(status, checkItemVOS); + } + checkItemVOS.add(checkItemVO); + } catch (Exception e) { + log.error("get fail", e); + } + } + return summary; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/HostMonitorVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/HostMonitorVO.java new file mode 100644 index 000000000..f1ca5455e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/HostMonitorVO.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostMonitorVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/HostMonitorVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.host; + +import lombok.Data; + +/** + * @author lhf + * @date 2023/3/14 10:31 + **/ +@Data +public class HostMonitorVO { + private String hostId; + private String upSpeed; + private String downSpeed; + private String cpu; + private String memory; + private String disk; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/OpsHostVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/OpsHostVO.java new file mode 100644 index 000000000..aa4fb787a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/OpsHostVO.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsHostVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/OpsHostVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.host; + +import lombok.Data; + +import org.opengauss.admin.common.enums.agent.AgentStatus; + +import java.util.Set; + +/** + * @author lhf + * @date 2022/10/23 08:22 + **/ +@Data +public class OpsHostVO { + private String hostId; + private String hostname; + private String privateIp; + private String publicIp; + private Integer port; + private String azId; + private String azName; + private String remark; + private Boolean isRemember; + private String os; + private String cpuArch; + private String name; + private String agentName; + private AgentStatus agentStatus; + private String agentInstallPath; + private String agentInstallUsername; + private Integer agentInstallPort; + private String osVersion; + private Set tags; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/SSHBody.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/SSHBody.java new file mode 100644 index 000000000..732a85a28 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/SSHBody.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SSHBody.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/SSHBody.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.host; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * @author lhf + * @date 2022/8/8 13:57 + **/ +@Data +@AllArgsConstructor +public class SSHBody { + private String ip; + + private Integer sshPort; + + private String sshUsername; + + private String sshPassword; + + private String businessId; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/tag/HostTagAddInputDto.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/tag/HostTagAddInputDto.java new file mode 100644 index 000000000..0ba8713ec --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/tag/HostTagAddInputDto.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostTagAddInputDto.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/tag/HostTagAddInputDto.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.host.tag; + +import lombok.Data; + +/** + * @author lhf + * @date 2023/3/18 00:05 + **/ +@Data +public class HostTagAddInputDto { + private String name; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/tag/HostTagInputDto.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/tag/HostTagInputDto.java new file mode 100644 index 000000000..4f5a85c1f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/tag/HostTagInputDto.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostTagInputDto.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/tag/HostTagInputDto.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.host.tag; + +import lombok.Data; + +import java.util.Arrays; +import java.util.List; + +/** + * @author lhf + * @date 2023/3/14 23:45 + **/ +@Data +public class HostTagInputDto { + private List names; + private List hostIds; + + public static HostTagInputDto of(List tags, String hostId) { + HostTagInputDto hostTagInputDto = new HostTagInputDto(); + hostTagInputDto.setNames(tags); + hostTagInputDto.setHostIds(Arrays.asList(hostId)); + return hostTagInputDto; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/tag/HostTagPageVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/tag/HostTagPageVO.java new file mode 100644 index 000000000..178540bd7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/tag/HostTagPageVO.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostTagPageVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/host/tag/HostTagPageVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.host.tag; + +import lombok.Data; + +/** + * @author lhf + * @date 2023/3/17 23:47 + **/ +@Data +public class HostTagPageVO { + private String id; + private String name; + private Integer relNum; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterImportAnalysisVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterImportAnalysisVO.java new file mode 100644 index 000000000..8d13a574b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterImportAnalysisVO.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JdbcDbClusterImportAnalysisVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterImportAnalysisVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.jdbc; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author lhf + * @date 2023/1/13 16:31 + **/ +@Data +public class JdbcDbClusterImportAnalysisVO { + private Integer total; + private Integer succNum; + private Integer failNum; + private List failDetail; + + public static JdbcDbClusterImportAnalysisVO of(List clusterInputDtoList, List errorCluster) { + JdbcDbClusterImportAnalysisVO jdbcDbClusterImportAnalysisVO = new JdbcDbClusterImportAnalysisVO(); + List failDetailList = new ArrayList<>(); + jdbcDbClusterImportAnalysisVO.setTotal(count(clusterInputDtoList)); + jdbcDbClusterImportAnalysisVO.setFailNum(count(errorCluster, failDetailList)); + jdbcDbClusterImportAnalysisVO.setSuccNum(jdbcDbClusterImportAnalysisVO.getTotal() - jdbcDbClusterImportAnalysisVO.getFailNum()); + jdbcDbClusterImportAnalysisVO.setFailDetail(failDetailList); + return jdbcDbClusterImportAnalysisVO; + } + + private static Integer count(List errorCluster, List failDetailList) { + Integer errNum = 0; + for (JdbcDbClusterInputDto inputDto : errorCluster) { + List nodes = inputDto.getNodes(); + if (CollUtil.isEmpty(nodes) && StrUtil.isNotEmpty(inputDto.getRemark())) { + errNum++; + failDetailList.add(JdbcErrorVO.of(inputDto.getClusterName(), null, inputDto.getRemark())); + } + + if (CollUtil.isNotEmpty(nodes)) { + for (JdbcDbClusterNodeInputDto node : nodes) { + if (StrUtil.isNotEmpty(node.getRemark())) { + errNum++; + failDetailList.add(JdbcErrorVO.of(inputDto.getClusterName(), node.getUrl(), node.getRemark())); + } + } + } + + } + return errNum; + } + + private static Integer count(List clusterInputDtoList) { + final Long count = clusterInputDtoList.stream().map(JdbcDbClusterInputDto::getNodes).flatMap(nodeList -> nodeList.stream()).collect(Collectors.counting()); + return count.intValue(); + } + + @Data + static class JdbcErrorVO { + private String clusterName; + private String url; + private String errMsg; + + public static JdbcErrorVO of(String clusterName, String url, String errMsg) { + JdbcErrorVO jdbcErrorVO = new JdbcErrorVO(); + jdbcErrorVO.setClusterName(clusterName); + jdbcErrorVO.setUrl(url); + jdbcErrorVO.setErrMsg(errMsg); + return jdbcErrorVO; + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterInputDto.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterInputDto.java new file mode 100644 index 000000000..a10b32508 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterInputDto.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JdbcDbClusterInputDto.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterInputDto.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.jdbc; + +import lombok.Data; +import org.opengauss.admin.common.enums.ops.DeployTypeEnum; + +import java.util.List; + +/** + * @author lhf + * @date 2023/1/13 13:27 + **/ +@Data +public class JdbcDbClusterInputDto { + private String clusterName; + private DeployTypeEnum deployType; + private List nodes; + private String remark; + + public static JdbcDbClusterInputDto of(String name, DeployTypeEnum deployType) { + JdbcDbClusterInputDto inputDto = new JdbcDbClusterInputDto(); + inputDto.setClusterName(name); + inputDto.setDeployType(deployType); + return inputDto; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterNodeInputDto.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterNodeInputDto.java new file mode 100644 index 000000000..f49b6d831 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterNodeInputDto.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JdbcDbClusterNodeInputDto.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterNodeInputDto.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.jdbc; + +import lombok.Data; +import org.opengauss.admin.common.enums.ops.DeployTypeEnum; + +/** + * @author lhf + * @date 2023/1/13 13:31 + **/ +@Data +public class JdbcDbClusterNodeInputDto { + private String name; + private String url; + private String username; + private String password; + private String remark; + private DeployTypeEnum deployType; + + public static JdbcDbClusterNodeInputDto of(String name, String url, String username, String password) { + JdbcDbClusterNodeInputDto jdbcDbClusterNodeInputDto = new JdbcDbClusterNodeInputDto(); + jdbcDbClusterNodeInputDto.setName(name); + jdbcDbClusterNodeInputDto.setUrl(url); + jdbcDbClusterNodeInputDto.setUsername(username); + jdbcDbClusterNodeInputDto.setPassword(password); + return jdbcDbClusterNodeInputDto; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterNodeVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterNodeVO.java new file mode 100644 index 000000000..9e964430e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterNodeVO.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JdbcDbClusterNodeVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterNodeVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.jdbc; + +import lombok.Data; +import org.opengauss.admin.common.core.domain.entity.ops.OpsJdbcDbClusterNodeEntity; + +/** + * @author lhf + * @date 2023/1/13 14:26 + **/ +@Data +public class JdbcDbClusterNodeVO { + private String clusterNodeId; + private String name; + private String ip; + private String port; + private String username; + private String password; + private String url; + private String os; + + public static JdbcDbClusterNodeVO of(OpsJdbcDbClusterNodeEntity clusterNodeEntity, String os) { + JdbcDbClusterNodeVO jdbcDbClusterNodeVO = new JdbcDbClusterNodeVO(); + jdbcDbClusterNodeVO.setClusterNodeId(clusterNodeEntity.getClusterNodeId()); + jdbcDbClusterNodeVO.setName(clusterNodeEntity.getName()); + jdbcDbClusterNodeVO.setIp(clusterNodeEntity.getIp()); + jdbcDbClusterNodeVO.setPort(clusterNodeEntity.getPort()); + jdbcDbClusterNodeVO.setUsername(clusterNodeEntity.getUsername()); + jdbcDbClusterNodeVO.setPassword(clusterNodeEntity.getPassword()); + jdbcDbClusterNodeVO.setUrl(clusterNodeEntity.getUrl()); + jdbcDbClusterNodeVO.setOs(os); + return jdbcDbClusterNodeVO; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterVO.java new file mode 100644 index 000000000..1900f3908 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterVO.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JdbcDbClusterVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcDbClusterVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.jdbc; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.opengauss.admin.common.core.domain.entity.ops.OpsJdbcDbClusterEntity; +import org.opengauss.admin.common.enums.ops.DbTypeEnum; +import org.opengauss.admin.common.enums.ops.DeployTypeEnum; + +import java.util.Date; +import java.util.List; + +/** + * @author lhf + * @date 2023/1/13 14:26 + **/ +@Data +public class JdbcDbClusterVO { + private String clusterId; + private String name; + private DeployTypeEnum deployType; + private DbTypeEnum dbType; + private List nodes; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + private String versionNum; + + public static JdbcDbClusterVO of(OpsJdbcDbClusterEntity record, List nodes) { + JdbcDbClusterVO jdbcDbClusterVO = new JdbcDbClusterVO(); + jdbcDbClusterVO.setClusterId(record.getClusterId()); + jdbcDbClusterVO.setName(record.getName()); + jdbcDbClusterVO.setDeployType(record.getDeployType()); + jdbcDbClusterVO.setDbType(record.getDbType()); + jdbcDbClusterVO.setUpdateTime(record.getUpdateTime()); + jdbcDbClusterVO.setNodes(nodes); + return jdbcDbClusterVO; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcInfo.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcInfo.java new file mode 100644 index 000000000..2df66721a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcInfo.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JdbcInfo.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcInfo.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.jdbc; + +import lombok.Data; +import org.opengauss.admin.common.enums.ops.DbTypeEnum; + +/** + * @author lhf + * @date 2023/1/13 13:45 + **/ +@Data +public class JdbcInfo { + private DbTypeEnum dbType; + private String ip; + private String port; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcMonitorVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcMonitorVO.java new file mode 100644 index 000000000..f42414064 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcMonitorVO.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JdbcMonitorVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/domain/model/ops/jdbc/JdbcMonitorVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.domain.model.ops.jdbc; + +import lombok.Data; +import org.opengauss.admin.common.enums.ops.ClusterRoleEnum; + +/** + * @author lhf + * @date 2023/2/23 21:53 + **/ +@Data +public class JdbcMonitorVO { + private ClusterRoleEnum role; + private String connNum; + private String qps; + private String tps; + private String memoryUsed; + private String tableSpaceUsed; + private String lockNum; + private String sessionNum; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ModifyPasswordDto.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ModifyPasswordDto.java new file mode 100644 index 000000000..a7f5fdc9b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ModifyPasswordDto.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2022-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.core.dto; + +import lombok.Data; + +/** + * ModifyPasswordDto + * + * @date: 2024/10/16 16:30 + * @description: ModifyPasswordDto + * @version: 1.0 + * @since 2024-10-16 + */ +@Data +public class ModifyPasswordDto { + /** + * oldPassword + */ + private String oldPassword; + + /** + * newPassword + */ + private String newPassword; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/OsSet.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/OsSet.java new file mode 100644 index 000000000..b31166f1b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/OsSet.java @@ -0,0 +1,41 @@ +package org.opengauss.admin.common.core.dto; + +import lombok.Data; +import lombok.AllArgsConstructor; + +import java.util.HashSet; + +/** + * used only for Json to Frontend + * + * @author chenyuchen2024 + * @date 24/02/22 21:12 + **/ +@Data +@AllArgsConstructor +public class OsSet { + + /** + * os set based on centos + **/ + private HashSet centosBasedSet; + + /** + * os set based on openEuler + **/ + private HashSet openEulerBasedSet; + + /** + * check whether os is based on centos + */ + public boolean isCentos(String os) { + return centosBasedSet.contains(os); + } + + /** + * check whether os is based on openEuler + */ + public boolean isOpenEuler(String os) { + return openEulerBasedSet.contains(os); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/PluginConfigDataDto.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/PluginConfigDataDto.java new file mode 100644 index 000000000..13f042458 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/PluginConfigDataDto.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * PluginConfigDataDto.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/PluginConfigDataDto.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.dto; + +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +/** + * @className: PluginConfigDataDto + * @description: PluginConfigDataDto + * @author: xielibo + * @date: 2022-09-25 3:14 PM + **/ +@Data +public class PluginConfigDataDto { + + @NotNull + private String pluginId; + @NotNull + private String configData; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/PluginDownloadDTO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/PluginDownloadDTO.java new file mode 100644 index 000000000..e3e951298 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/PluginDownloadDTO.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * PluginDownloadDTO.java + * + * IDENTIFICATION + * visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/PluginDownloadDTO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.core.dto; + +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +/** + * PluginDownloadDTO + * + * @author: zhaochen + * @since 2025-03-20 + * @version 7.0.0-RC1 + **/ +@Data +public class PluginDownloadDTO { + @NotNull(message = "plugin package support pluginId can not be empty") + private String pluginId; + + @NotNull(message = "plugin package support pluginUrl can not be empty") + private String pluginUrl; + + @NotNull(message = "plugin package support wsBusinessId can not be empty") + private String wsBusinessId; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/SysLogConfigDto.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/SysLogConfigDto.java new file mode 100644 index 000000000..ed1854e28 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/SysLogConfigDto.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysLogConfigDto.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/SysLogConfigDto.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.dto; + +import lombok.Data; + +/** + * System log config dto + */ +@Data +public class SysLogConfigDto { + private String totalSizeCap; + + private Integer maxHistory; + + private String level; + + private String maxFileSize; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/SysTaskDto.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/SysTaskDto.java new file mode 100644 index 000000000..753d0a3e8 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/SysTaskDto.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysTaskDto.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/SysTaskDto.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.dto; + +import lombok.Data; + +/** + * System task dto + */ +@Data +public class SysTaskDto { + /** + * task name + */ + private String taskName; + + /** + * task type + */ + private Integer taskType; + + /** + * exec status + */ + private Integer execStatus; + + private String execStartTime; + + private String execEndTime; + + private String finishStartTime; + + private String finishEndTime; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/ClusterNodeDto.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/ClusterNodeDto.java new file mode 100644 index 000000000..9863a6c00 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/ClusterNodeDto.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterNodeDto.java + * + * IDENTIFICATION + * data-migration/src/main/java/org/opengauss/admin/common/core/dto/ops/ClusterNodeDto.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.core.dto.ops; + +import lombok.Data; + +/** + * ClusterNodeDto Object + * + * @author: xielibo + * @date: 2023-05-17 17:01 + **/ +@Data +public class ClusterNodeDto { + + /** + * publicIp of ClusterNode + */ + private String publicIp; + /** + * port of ClusterNode + */ + private Integer port; + /** + * installUserId of ClusterNode + */ + private String installUserId; + /** + * installUserName of ClusterNode + */ + private String installUserName; + /** + * installPassword of ClusterNode + */ + private String installPassword; + /** + * installPath of ClusterNode + */ + private String installPath; + /** + * dataPath of ClusterNode + */ + private String dataPath; + /** + * is mini version + */ + private Boolean isMiniVersion; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/OpsPackageDownloadDTO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/OpsPackageDownloadDTO.java new file mode 100644 index 000000000..79dcaf17a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/OpsPackageDownloadDTO.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsPackageDownloadDTO.java + * + * IDENTIFICATION + * visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/OpsPackageDownloadDTO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.core.dto.ops; + +import lombok.Data; +import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.util.Objects; + +/** + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Data +public class OpsPackageDownloadDTO { + /** + * package name + */ + @NotBlank(message = "package name can not be empty") + private String name; + /** + * package support os + */ + @NotBlank(message = "package support os can not be empty") + private String os; + @NotBlank(message = "package support osVersion can not be empty") + private String osVersion; + /** + * package support cpu arch + */ + @NotBlank(message = "package support cpu arch can not be empty") + private String cpuArch; + /** + * package support openGauss version + */ + @NotNull(message = "package support openGauss version can not be empty") + private OpenGaussVersionEnum openGaussVersion; + /** + * package support openGauss version num + */ + @NotBlank(message = "package support openGauss version num can not be empty") + private String openGaussVersionNum; + /** + * package download url + */ + @NotBlank(message = "package download url can not be empty") + private String downloadUrl; + /** + * package download ws business id + */ + @NotBlank(message = "package download ws business id can not be empty") + private String wsBusinessId; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof OpsPackageDownloadDTO)) { + return false; + } + OpsPackageDownloadDTO that = (OpsPackageDownloadDTO) o; + return Objects.equals(name, that.name) + && Objects.equals(os, that.os) + && Objects.equals(cpuArch, that.cpuArch) + && openGaussVersion == that.openGaussVersion + && Objects.equals(openGaussVersionNum, that.openGaussVersionNum) + && Objects.equals(downloadUrl, that.downloadUrl) + && Objects.equals(wsBusinessId, that.wsBusinessId); + } + + @Override + public int hashCode() { + return Objects.hash(name, os, cpuArch, openGaussVersion, openGaussVersionNum, downloadUrl, wsBusinessId); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/OpsPackageUploadDTO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/OpsPackageUploadDTO.java new file mode 100644 index 000000000..0ba452fac --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/OpsPackageUploadDTO.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsPackageUploadDTO.java + * + * IDENTIFICATION + * visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/OpsPackageUploadDTO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.core.dto.ops; + +import lombok.Data; +import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.util.Objects; + +/** + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Data +public class OpsPackageUploadDTO { + /** + * package name + */ + @NotBlank(message = "package name can not be empty") + private String name; + /** + * package support os + */ + @NotBlank(message = "package support os can not be empty") + private String os; + @NotBlank(message = "package support osVersion can not be empty") + private String osVersion; + /** + * package support cpu arch + */ + @NotBlank(message = "package support cpu arch can not be empty") + private String cpuArch; + /** + * package support openGauss version + */ + @NotNull(message = "package support openGauss version can not be empty") + private OpenGaussVersionEnum packageVersion; + /** + * package support openGauss version num + */ + @NotBlank(message = "package support openGauss version num can not be empty") + private String packageVersionNum; + /** + * package download url + */ + @NotBlank(message = "package download url can not be empty") + private String packageUrl; + @NotNull(message = "package upload file can not be empty") + private MultipartFile uploadFile; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof OpsPackageUploadDTO)) { + return false; + } + OpsPackageUploadDTO that = (OpsPackageUploadDTO) o; + return Objects.equals(name, that.name) + && Objects.equals(os, that.os) + && Objects.equals(cpuArch, that.cpuArch) + && packageVersion == that.packageVersion + && Objects.equals(packageVersionNum, that.packageVersionNum) + && Objects.equals(packageUrl, that.packageUrl); + } + + @Override + public int hashCode() { + return Objects.hash(name, os, cpuArch, packageVersion, packageVersionNum, packageUrl); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/PackageDto.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/PackageDto.java new file mode 100644 index 000000000..e30e13bd7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/PackageDto.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * PackageDto.java + * + * IDENTIFICATION + * visualtool-common/src/main/java/org/opengauss/admin/common/core/dto/ops/PackageDto.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.core.dto.ops; + +import lombok.Data; +import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum; + +/** + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Data +public class PackageDto { + /** + * package name + */ + private String name; + /** + * package support os + */ + private String os; + private String osVersion; + /** + * package support cpu arch + */ + private String cpuArch; + /** + * package support openGauss version + */ + private OpenGaussVersionEnum openGaussVersion; + /** + * package support openGauss version num + */ + private String openGaussVersionNum; + + /** + * get simple info + * + * @return simple package info + */ + public String simple() { + return os + osVersion + " " + cpuArch + " " + openGaussVersion + openGaussVersionNum; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/handler/ops/cache/SSHChannelManager.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/handler/ops/cache/SSHChannelManager.java new file mode 100644 index 000000000..d2f1c8667 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/handler/ops/cache/SSHChannelManager.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SSHChannelManager.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/handler/ops/cache/SSHChannelManager.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.handler.ops.cache; + +import com.jcraft.jsch.ChannelShell; + +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author lhf + * @date 2022/8/11 20:37 + **/ +public class SSHChannelManager { + private static final ConcurrentHashMap CHANNEL_CONTEXT = new ConcurrentHashMap<>(); + + public static Optional getChannelShell(String businessId) { + return Optional.ofNullable(CHANNEL_CONTEXT.get(businessId)); + } + + public static void registerChannelShell(String businessId, ChannelShell channelShell) { + CHANNEL_CONTEXT.put(businessId,channelShell); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/handler/ops/cache/TaskManager.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/handler/ops/cache/TaskManager.java new file mode 100644 index 000000000..72bff46d4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/handler/ops/cache/TaskManager.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * TaskManager.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/handler/ops/cache/TaskManager + * .java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.core.handler.ops.cache; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.exception.ops.OpsException; + +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * @author lhf + * @date 2022/8/11 20:37 + **/ +@Slf4j +public class TaskManager { + private static final int MAX_RETRIES = 10; + private static final ConcurrentHashMap> TASK_CONTEXT = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap TASK_WORKER_CONTEXT = new ConcurrentHashMap<>(); + + /** + * registry task to context + * + * @param businessId businessId + * @param future future + */ + public static void registry(String businessId, Future future) { + TASK_CONTEXT.put(businessId, future); + } + + /** + * remove task from context, if the task is not finished, it will be cancelled. + * if the task is finished or cancelled, it will be removed from context. + * + * @param businessId businessId + * @return Optional> future + */ + public static Optional> remove(String businessId) { + Future future = TASK_CONTEXT.get(businessId); + if (future == null) { + log.info("task {} not found in context", businessId); + return Optional.empty(); + } + if (future.isDone() || future.isCancelled()) { + log.info("task {} already completed or cancelled", businessId); + TASK_WORKER_CONTEXT.remove(businessId); + return Optional.of(TASK_CONTEXT.remove(businessId)); + } + int retryCount = 0; + while (!future.isCancelled() && !future.isDone() && retryCount < MAX_RETRIES) { + future.cancel(true); + log.warn("attempting to cancel task: {}", businessId); + try { + TimeUnit.MILLISECONDS.sleep(200); + } catch (InterruptedException e) { + log.debug("thread interrupted while waiting for task cancellation", e); + Thread.currentThread().interrupt(); + break; + } + retryCount++; + } + if (future.isCancelled() || future.isDone()) { + log.info("task {} successfully cancelled or completed", businessId); + TASK_WORKER_CONTEXT.remove(businessId); + return Optional.of(TASK_CONTEXT.remove(businessId)); + } else { + log.error("failed to cancel task {} after {} attempts", businessId, MAX_RETRIES); + throw new OpsException("Failed to cancel task " + businessId); + } + } + + /** + * registry worker to context + * + * @param businessId businessId + * @param worker worker + */ + public static void registryWorker(String businessId, Runnable worker) { + TASK_WORKER_CONTEXT.put(businessId, worker); + } + + /** + * get worker from context + * + * @param businessId businessId + * @return Runnable + */ + public static Runnable getRegistryWorker(String businessId) { + return TASK_WORKER_CONTEXT.get(businessId); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/handler/ops/cache/WsConnectorManager.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/handler/ops/cache/WsConnectorManager.java new file mode 100644 index 000000000..68dadb431 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/handler/ops/cache/WsConnectorManager.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WsConnectorManager.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/handler/ops/cache/WsConnectorManager.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.handler.ops.cache; + +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.model.ops.WsSession; +import org.springframework.stereotype.Component; + +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author lhf + * @date 2022/8/11 20:37 + **/ +@Component +@Slf4j +public class WsConnectorManager { + private static final ConcurrentHashMap CONNECTOR_CONTEXT = new ConcurrentHashMap<>(); + + /** + * register session + * + * @param businessId business ID + * @param wsSession websocket session + */ + public void register(String businessId, WsSession wsSession) { + log.info("register-WsConnectorManager.java businessId: {}", businessId); + CONNECTOR_CONTEXT.put(businessId, wsSession); + } + + /** + * remove session + * + * @param businessId business ID + */ + public void remove(String businessId) { + log.info("remove1-WsConnectorManager.java businessId: {}", businessId); + CONNECTOR_CONTEXT.remove(businessId); + } + + /** + * get session + * + * @param businessId business ID + * @return websocket session + */ + public Optional getSession(String businessId) { + return Optional.ofNullable(CONNECTOR_CONTEXT.get(businessId)); + } + + public void removeByVal(WsSession wsSession) { + CONNECTOR_CONTEXT.forEach((k, v) -> { + if (v.equals(wsSession)) { + log.info("remove2-WsConnectorManager.java key {}", k); + CONNECTOR_CONTEXT.remove(k); + } + }); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/page/PageDomain.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/page/PageDomain.java new file mode 100644 index 000000000..79f1465be --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/page/PageDomain.java @@ -0,0 +1,107 @@ +/** + Copyright ruoyi. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.common.core.page; + +import org.opengauss.admin.common.utils.StringUtils; + +/** + * page bean + * + * @author xielibo + */ +public class PageDomain { + /** + * page Number + */ + private Integer pageNum; + + /** + * page Size + */ + private Integer pageSize; + + /** + * order by column + */ + private String orderByColumn; + + /** + * order type + */ + private String isAsc = "asc"; + + /** + * reasonable + */ + private Boolean reasonable = true; + + public String getOrderBy() { + if (StringUtils.isEmpty(orderByColumn)) { + return ""; + } + return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc; + } + + public Integer getPageNum() { + return pageNum; + } + + public void setPageNum(Integer pageNum) { + this.pageNum = pageNum; + } + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + public String getOrderByColumn() { + return orderByColumn; + } + + public void setOrderByColumn(String orderByColumn) { + this.orderByColumn = orderByColumn; + } + + public String getIsAsc() { + return isAsc; + } + + public void setIsAsc(String isAsc) { + if (StringUtils.isNotEmpty(isAsc)) { + if ("ascending".equals(isAsc)) { + isAsc = "asc"; + } else if ("descending".equals(isAsc)) { + isAsc = "desc"; + } + this.isAsc = isAsc; + } + } + + public Boolean getReasonable() { + if (StringUtils.isNull(reasonable)) { + return Boolean.TRUE; + } + return reasonable; + } + + public void setReasonable(Boolean reasonable) { + this.reasonable = reasonable; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/page/TableDataInfo.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/page/TableDataInfo.java new file mode 100644 index 000000000..d1bfa048c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/page/TableDataInfo.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * TableDataInfo.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/page/TableDataInfo.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.page; + +import java.io.Serializable; +import java.util.List; + +/** + * table data bean + * + * @author xielibo + */ +public class TableDataInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * total + */ + private long total; + + /** + * rows + */ + private List rows; + + /** + * code + */ + private int code; + + /** + * msg + */ + private String msg; + + /** + * table data + */ + public TableDataInfo() { + } + + public TableDataInfo(List list, int total) { + this.rows = list; + this.total = total; + } + + public long getTotal() { + return total; + } + + public void setTotal(long total) { + this.total = total; + } + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/page/TableSupport.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/page/TableSupport.java new file mode 100644 index 000000000..1edd55288 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/page/TableSupport.java @@ -0,0 +1,64 @@ +/** + Copyright ruoyi. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.common.core.page; + +import org.opengauss.admin.common.utils.ServletUtils; + +/** + * table support + * + * @author xielibo + */ +public class TableSupport { + /** + * pageNum + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * pageSize + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * orderByColumn + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * order type + */ + public static final String IS_ASC = "isAsc"; + + /** + * reasonable + */ + public static final String REASONABLE = "reasonable"; + + public static PageDomain getPageDomain() { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(ServletUtils.getParameterToInt(PAGE_NUM)); + pageDomain.setPageSize(ServletUtils.getParameterToInt(PAGE_SIZE)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + return pageDomain; + } + + public static PageDomain buildPageRequest() { + return getPageDomain(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/text/Convert.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/text/Convert.java new file mode 100644 index 000000000..2ea46303e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/text/Convert.java @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * Convert.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/text/Convert.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.text; + +import org.opengauss.admin.common.utils.StringUtils; + +import java.math.BigDecimal; +import java.text.NumberFormat; + +/** + * type converter + * + * @author xielibo + */ +public class Convert { + /** + * convert to string
+ * If the given value is null, or the conversion fails, return the default value
+ * + * @param value value + * @param defaultValue defaultValue + * @return result + */ + public static String toStr(Object value, String defaultValue) { + if (null == value) { + return defaultValue; + } + if (value instanceof String) { + return (String) value; + } + return value.toString(); + } + + /** + * convert to string
+ * If the given value is null, or the conversion fails, return null
+ * + * @param value value + * @return result + */ + public static String toStr(Object value) { + return toStr(value, null); + } + + /** + * convert to string
+ * If the given value is null, or the conversion fails, return the default value
+ * + * @param value value + * @param defaultValue defaultValue + * @return result + */ + public static Character toChar(Object value, Character defaultValue) { + if (null == value) { + return defaultValue; + } + if (value instanceof Character) { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * convert to char
+ * If the given value is null, or the conversion fails, return the null
+ * + * @param value value + * @return result + */ + public static Character toChar(Object value) { + return toChar(value, null); + } + + /** + * convert to byte
+ * If the given value is null, or the conversion fails, return the default value
+ * + * @param value value + * @param defaultValue defaultValue + * @return result + */ + public static Byte toByte(Object value, Byte defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Byte) { + return (Byte) value; + } + if (value instanceof Number) { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Byte.parseByte(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * convert to byte
+ * If the given value is null, or the conversion fails, return the default value
+ * + */ + public static Byte toByte(Object value) { + return toByte(value, null); + } + + /** + * convert to short
+ * If the given value is null, or the conversion fails, return the default value
+ * + */ + public static Short toShort(Object value, Short defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Short) { + return (Short) value; + } + if (value instanceof Number) { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Short.parseShort(valueStr.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * convert to byte
+ * If the given value is null, or the conversion fails, return null
+ * + * @param value value + */ + public static Short toShort(Object value) { + return toShort(value, null); + } + + /** + * convert to number
+ * If the given value is null, or the conversion fails, return the default value
+ * + * @param value value + * @param defaultValue defaultValue + * @return result + */ + public static Number toNumber(Object value, Number defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Number) { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return NumberFormat.getInstance().parse(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * convert to number
+ * If the given value is null, or the conversion fails, return null
+ * + * @param value value + * @return result + */ + public static Number toNumber(Object value) { + return toNumber(value, null); + } + + /** + * convert to int
+ * If the given value is null, or the conversion fails, return the default value
+ * + * @param value value + * @param defaultValue defaultValue + * @return result + */ + public static Integer toInt(Object value, Integer defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Integer) { + return (Integer) value; + } + if (value instanceof Number) { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Integer.parseInt(valueStr.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * convert to number
+ * If the given value is null, or the conversion fails, return null
+ * + * @param value value + * @return result + */ + public static Integer toInt(Object value) { + if (value != null) { + return Integer.parseInt(value.toString()); + } + return null; + } + + + + /** + * convert to long
+ * If the given value is null, or the conversion fails, return the default value
+ * + * @param value value + * @param defaultValue defaultValue + * @return result + */ + public static Long toLong(Object value, Long defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Long) { + return (Long) value; + } + if (value instanceof Number) { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return new BigDecimal(valueStr.trim()).longValue(); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * convert to long
+ * If the given value is null, or the conversion fails, return null
+ * + * @param value value + * @return result + */ + public static Long toLong(Object value) { + return toLong(value, null); + } + + /** + * convert to double
+ * If the given value is null, or the conversion fails, return the default value
+ * + * @param value value + * @param defaultValue defaultValue + * @return result + */ + public static Double toDouble(Object value, Double defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Double) { + return (Double) value; + } + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return new BigDecimal(valueStr.trim()).doubleValue(); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * convert to number
+ * If the given value is null, or the conversion fails, return null
+ * + * @param value value + * @return result + */ + public static Double toDouble(Object value) { + return toDouble(value, null); + } + + /** + * convert to float
+ * If the given value is null, or the conversion fails, return the default value
+ * + * @param value value + * @param defaultValue defaultValue + * @return result + */ + public static Float toFloat(Object value, Float defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Float) { + return (Float) value; + } + if (value instanceof Number) { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Float.parseFloat(valueStr.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * convert to float
+ * If the given value is null, or the conversion fails, return the default value
+ * + * @param value value + * @return result + */ + public static Float toFloat(Object value) { + return toFloat(value, null); + } + + /** + * convert to boolean
+ * The supported values of String are: true, false, yes, ok, no, 1,0 If the given value is empty or the conversion fails, the default value will be returned
+ * + * @param value value + * @param defaultValue defaultValue + * @return result + */ + public static Boolean toBool(Object value, Boolean defaultValue) { + if (value == null) { + return defaultValue; + } + if (value instanceof Boolean) { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) { + case "true": + return true; + case "false": + return false; + case "yes": + return true; + case "ok": + return true; + case "no": + return false; + case "1": + return true; + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * convert to boolean
+ * The supported values of String are: true, false, yes, ok, no, 1,0 If the given value is empty or the conversion fails, the default value will be returned
+ * + * @param value value + * @return result + */ + public static Boolean toBool(Object value) { + return toBool(value, null); + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/HostBean.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/HostBean.java new file mode 100644 index 000000000..f1827ebc2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/HostBean.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostBean.java + * + * IDENTIFICATION + * visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/HostBean.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.core.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * HostBean + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@Data +@AllArgsConstructor +public class HostBean { + private String host; + private Integer port; + private String username; + private String password; + + @Override + public String toString() { + return "HostBean{ host='" + host + "', port=" + port + ", username='" + username + "'}"; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/HostInfoVo.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/HostInfoVo.java new file mode 100644 index 000000000..a6048fac9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/HostInfoVo.java @@ -0,0 +1,19 @@ +package org.opengauss.admin.common.core.vo; + +import lombok.Data; + +/** + * Encapsulates some host attributes + * + * @author duanguoqiang + * @version 0.0 + * @date 2024/2/5 14:27 + * @since 0.0 + */ +@Data +public class HostInfoVo { + private String hostname; + private String os; + private String osVersion; + private String cpuArch; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/HostRealtimeStatistics.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/HostRealtimeStatistics.java new file mode 100644 index 000000000..d3fc88d39 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/HostRealtimeStatistics.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostRealtimeStatistics.java + * + * IDENTIFICATION + * org.opengauss.admin.common.core.vo.HostRealtimeStatistics + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.core.vo; + +import lombok.Data; + +import java.util.Locale; + +/** + * HostRealtimeStatistics + * + * @author wangchao + * @since 2024/10/29 09:26 + */ +@Data +public class HostRealtimeStatistics { + /** + * disk unit size 1024*1024,to format disk GB unit + */ + private static final long DISK_UNIT = 1024 * 1024; + + private float total; + private float used; + private float available; + private String use; + + /** + * build disk realtime statistics + * + * @param diskMonitor disk monitor string + * @return HostRealtimeStatistics + */ + public static HostRealtimeStatistics ofDisk(String diskMonitor) { + HostRealtimeStatistics bean = new HostRealtimeStatistics(); + String[] dev = diskMonitor.split(" "); + if (dev.length == 7) { + bean.total += Math.round((float) Long.parseLong(dev[2]) / DISK_UNIT); + bean.used += Math.round((float) Long.parseLong(dev[3]) / DISK_UNIT); + bean.available += Math.round((float) Long.parseLong(dev[4]) / DISK_UNIT); + bean.use = dev[5].replace("%", ""); + } + return bean; + } + + /** + * build memory realtime statistics + * + * @param memory memory monitor string + * @return HostRealtimeStatistics + */ + public static HostRealtimeStatistics ofMemory(String memory) { + HostRealtimeStatistics bean = new HostRealtimeStatistics(); + String[] split = memory.split(" "); + bean.total += Float.parseFloat(split[1]); + bean.used += Float.parseFloat(split[2]); + bean.available += Float.parseFloat(split[3]); + bean.use = toString(((bean.total - bean.available) / bean.total) * 100); + return bean; + } + + /** + * host statistics format to string %.2f + * + * @param metric metric + * @return formatted string + */ + public static String toString(float metric) { + return String.format(Locale.ROOT, "%.2f", metric); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/MenuVo.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/MenuVo.java new file mode 100644 index 000000000..8faed146c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/MenuVo.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MenuVo.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/MenuVo.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.vo; + +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Tolerate; + +/** + * @className: MenuVo + * @description: Menu value object + * @author: xielibo + * @date: 2022-09-24 6:02 PM + **/ +@Builder +@Data +public class MenuVo { + + /** + * menu ID + */ + private Integer menuId; + + /** + * menu Name + */ + private String menuName; + + /** + * parent ID + */ + private Integer parentId; + + @Tolerate + public MenuVo(){} + +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/MetaVo.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/MetaVo.java new file mode 100644 index 000000000..4dfba6393 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/MetaVo.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MetaVo.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/MetaVo.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.vo; + +import lombok.Data; + +/** + * Menu Router Info + * + * @author xielibo + */ +@Data +public class MetaVo { + /** + * title + */ + private String title; + + /** + * icon + */ + private String icon; + + private Integer order; + + private boolean requiresAuth; + + private boolean hideChildrenInMenu; + + /** + * hideInMenu + */ + private boolean hideInMenu; + + + public MetaVo() { + } + + public MetaVo(String title, String icon) { + this.title = title; + this.icon = icon; + } + + public MetaVo(String title, String icon, boolean noCache) { + this.title = title; + this.icon = icon; + this.requiresAuth = false; + } + + public MetaVo(String title, String icon, boolean noCache,Integer order,boolean hideChildrenInMenu,boolean hideInMenu) { + this.title = title; + this.icon = icon; + this.order = order; + this.requiresAuth = false; + this.hideChildrenInMenu = hideChildrenInMenu; + this.hideInMenu = hideInMenu; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/RouterVo.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/RouterVo.java new file mode 100644 index 000000000..940de7ed4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/RouterVo.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * RouterVo.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/RouterVo.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.vo; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * Router Info + * + * @author xielibo + */ +@Data +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouterVo { + /** + * name + */ + private String name; + + /** + * route path + */ + private String path; + + /** + * redirect,when noRedirect is set the route is not clickable in breadcrumbs + */ + private String redirect; + + /** + * component + */ + private String component; + + /** + * when you declare more than one route under the children of a route, it will automatically become a nested mode - such as a component page + */ + private Boolean alwaysShow; + + private Boolean isPluginMenu; + + private String menuTheme; + + private MetaVo meta; + + private Integer menuClassify; + + /** + * childrens + */ + private List children; + + /** + * route query + */ + @TableField(exist = false) + private Map query; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/SysLogConfigVo.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/SysLogConfigVo.java new file mode 100644 index 000000000..db694edb8 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/SysLogConfigVo.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysLogConfigVo.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/SysLogConfigVo.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.vo; + +import lombok.Data; + +@Data +public class SysLogConfigVo { + private String totalSizeCap; + + private Integer maxHistory; + + private String level; + + private String maxFileSize; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/UploadInfoVO.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/UploadInfoVO.java new file mode 100644 index 000000000..ec08308d6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/UploadInfoVO.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UploadInfoVO.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/vo/UploadInfoVO.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.vo; + +import lombok.Data; + +/** + * File upload common info + * + * @author wangyl + */ +@Data +public class UploadInfoVO { + private String realPath; + private String name; + private String realName; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/ws/WsConnectorManager.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/ws/WsConnectorManager.java new file mode 100644 index 000000000..9682b761c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/core/ws/WsConnectorManager.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WsConnectorManager.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/core/ws/WsConnectorManager.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.core.ws; + +import jakarta.websocket.Session; +import lombok.extern.slf4j.Slf4j; + + +import java.util.concurrent.ConcurrentHashMap; + +/** + * @className: WsConnectorManager + * @description: WsConnectorManager + * @author: xielibo + * @date: 2022-11-14 22:31 + **/ +@Slf4j +public class WsConnectorManager { + private static final ConcurrentHashMap CONNECTOR_CONTEXT = new ConcurrentHashMap<>(); + + /** + * register session + * + * @param pluginId + * @param sessionId + * @param session + */ + public static void register(String pluginId, String sessionId, Session session) { + CONNECTOR_CONTEXT.put(prepareKey(pluginId, sessionId), session); + } + + /** + * remove session + */ + public static void remove(String pluginId, String sessionId) { + CONNECTOR_CONTEXT.remove(prepareKey(pluginId, sessionId)); + } + + /** + * get session + */ + public static Session getSession(String pluginId, String sessionId) { + log.info("cache sessions list."); + return CONNECTOR_CONTEXT.get(prepareKey(pluginId, sessionId)); + } + + private static String prepareKey(String pluginId, String sessionId) { + return pluginId + "_" + sessionId; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/BusinessStatus.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/BusinessStatus.java new file mode 100644 index 000000000..29be2bcad --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/BusinessStatus.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BusinessStatus.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/BusinessStatus.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * Business Status + * + * @author xielibo + */ +public enum BusinessStatus { + /** + * success + */ + SUCCESS, + + /** + * fail + */ + FAIL, +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/BusinessType.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/BusinessType.java new file mode 100644 index 000000000..daa469c6a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/BusinessType.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BusinessType.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/BusinessType.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * Business Type + * + * @author xielibo + */ +public enum BusinessType { + /** + * other + */ + OTHER, + + /** + * insert + */ + INSERT, + + /** + * update + */ + UPDATE, + + /** + * delete + */ + DELETE, + + /** + * grant + */ + GRANT, + + /** + * start + */ + START, + + /** + * stop + */ + STOP, + + /** + * install + */ + INSTALL, + + /** + * uninstall + */ + UNINSTALL, + /** + * reset + */ + RESET, + /** + * confirm + */ + CONFIRM, + /** + * check_environment + */ + CHECK_ENVIRONMENT, + /** + * batch_install + */ + BATCH_INSTALL, + /** + * re install + */ + RE_INSTALL, + /** + * copy + */ + COPY +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/CpuArchName.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/CpuArchName.java new file mode 100644 index 000000000..c0d38f21c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/CpuArchName.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CpuArchName.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/CpuArchName.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author wangyl + * @date 203/03/08 13:38 + **/ +@Getter +@AllArgsConstructor +public enum CpuArchName { + NOARCH("noarch"), + X86_64("x86_64"), + AARCH64("aarch64"); + + private String cpuArchName; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/DbDataLocationEnum.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/DbDataLocationEnum.java new file mode 100644 index 000000000..37b9f95bb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/DbDataLocationEnum.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) GBA-NCTI-ISDC. 2022-2024. + * + * openGauss DataKit is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DbDataLocationEnum.java + * + * IDENTIFICATION + * plugins/alert-monitor/src/main/java/com/nctigba/alert/monitor/enums/DbDataLocationEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +/** + * DbDataLocationEnum + * + * @since 2024/1/15 + */ +@AllArgsConstructor +@Getter +public enum DbDataLocationEnum { + INTARKDB("org.intarkdb.Driver", Arrays.asList("classpath:db/intarkDB-init.sql")), + OPENGAUSS("org.opengauss.Driver", Arrays.asList("classpath:db/openGauss-visualtool.sql")); + + private String driverClass; + private List locations; + + /** + * get DbDataLocationEnum by driverClass + * + * @param driverClass String + * @return Optional + */ + public static Optional of(String driverClass) { + if (StrUtil.isEmpty(driverClass)) { + return Optional.empty(); + } + for (DbDataLocationEnum enumConstant : DbDataLocationEnum.class.getEnumConstants()) { + if (enumConstant.getDriverClass().equalsIgnoreCase(driverClass)) { + return Optional.of(enumConstant); + } + } + return Optional.empty(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/HttpMethod.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/HttpMethod.java new file mode 100644 index 000000000..8c190ae37 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/HttpMethod.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HttpMethod.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/HttpMethod.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +import org.springframework.lang.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * Request Method + * + * @author xielibo + */ +public enum HttpMethod { + //GET + GET, + //HEAD + HEAD, + //POST + POST, + //PUT + PUT, + //PATCH + PATCH, + //DELETE + DELETE, + //OPTIONS + OPTIONS, + //TRACE + TRACE; + + private static final Map MAPPINGS = new HashMap<>(16); + + static { + for (HttpMethod httpMethod : values()) { + MAPPINGS.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) { + return (method != null ? MAPPINGS.get(method) : null); + } + + public boolean matches(String method) { + return (this == resolve(method)); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/LimitType.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/LimitType.java new file mode 100644 index 000000000..6c7b876eb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/LimitType.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * LimitType.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/LimitType.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * Limit Type + * + * @author xielibo + */ + +public enum LimitType { + /** + * default + */ + DEFAULT, + + /** + * ip + */ + IP +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/OperatorType.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/OperatorType.java new file mode 100644 index 000000000..77572abda --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/OperatorType.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperatorType.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/OperatorType.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * operator type + * + * @author xielibo + */ +public enum OperatorType { + /** + * other + */ + OTHER, + + /** + * admin user + */ + MANAGE, + + /** + * plugin + */ + PLUGIN, + +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/OsName.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/OsName.java new file mode 100644 index 000000000..a41878a7b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/OsName.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OsName.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/OsName.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author wangyl + * @date 203/03/08 13:38 + **/ +@Getter +@AllArgsConstructor +public enum OsName { + CENTOS("centos"), + OPEN_EULER("openEuler"), + ALL("all"); + + private String osName; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/OsSupportMap.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/OsSupportMap.java new file mode 100755 index 000000000..2954c5adc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/OsSupportMap.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/OsSupportMap.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.opengauss.admin.common.core.dto.OsSet; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +/** + * 将各类衍生os映射为centos或者openEuler + * + * @author chenyuchen2024 + * @date 24/02/22 21:12 + **/ +@Getter +@AllArgsConstructor +public enum OsSupportMap { + CENTOS_7_X86_64("centos", "x86_64", "7.6"), + OPENEULER_20_X86_64("openEuler", "x86_64", "20.03"), + OPENEULER_20_AARCH64("openEuler", "aarch64", "20.03"), + OPENEULER_22_X86_64("openEuler", "x86_64", "22.03"), + OPENEULER_22_AARCH64("openEuler", "aarch64", "22.03"), + OPENEULER_24_X86_64("openEuler", "x86_64", "24.03"), + OPENEULER_24_AARCH64("openEuler", "aarch64", "24.03"); + + private final String os; + private final String cpuArch; + private final String osVersion; + + private static OsSet osBasedSet; + private static Map openEulerX86Mapping = new HashMap<>(); + private static Map openEulerAarch64Mapping = new HashMap<>(); + + static { + HashSet centosBasedSet = new HashSet<>(); + centosBasedSet.add("centos"); + HashSet openEulerBasedSet = new HashSet<>(); + openEulerBasedSet.add("openeuler"); + openEulerBasedSet.add("kylin"); + osBasedSet = new OsSet(centosBasedSet, openEulerBasedSet); + + initializeOpenEulerMapping(); + } + + private static void initializeOpenEulerMapping() { + openEulerX86Mapping.put("20.03", OPENEULER_20_X86_64); + openEulerX86Mapping.put("22.03", OPENEULER_22_X86_64); + openEulerX86Mapping.put("24.03", OPENEULER_24_X86_64); + + openEulerAarch64Mapping.put("20.03", OPENEULER_20_AARCH64); + openEulerAarch64Mapping.put("22.03", OPENEULER_22_AARCH64); + openEulerAarch64Mapping.put("24.03", OPENEULER_24_AARCH64); + } + + public static OsSupportMap of(String osInfo, String osVersionInfo, String cpuArchInfo) { + if (osBasedSet.isCentos(osInfo) && "x86_64".equalsIgnoreCase(cpuArchInfo)) { + return CENTOS_7_X86_64; + } + + if (osBasedSet.isOpenEuler(osInfo.toLowerCase())) { + // openEuler系os默认映射成openEuler20 + if ("x86_64".equalsIgnoreCase(cpuArchInfo)) { + return openEulerX86Mapping.getOrDefault(osVersionInfo, OPENEULER_20_X86_64); + } + if ("aarch64".equalsIgnoreCase(cpuArchInfo)) { + return openEulerAarch64Mapping.getOrDefault(osVersionInfo, OPENEULER_20_AARCH64); + } + } + + return CENTOS_7_X86_64; + } + + public static OsSet getSupportOsNameList() { + return osBasedSet; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/PluginLicenseType.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/PluginLicenseType.java new file mode 100644 index 000000000..a4953c6f3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/PluginLicenseType.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2012-2022. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.enums; + +/** + * PluginLicenseType + */ +public enum PluginLicenseType { + /** + * 开源 + */ + OPEN_SOURCE, + + /** + * 免费 + */ + FREE, + + /** + * 试用 + */ + TRIAL, + + /** + * 正式 + */ + PAID +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ResponseCode.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ResponseCode.java new file mode 100644 index 000000000..c55a6e041 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ResponseCode.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ResponseCode.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ResponseCode.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +import java.util.HashMap; +import java.util.Map; + +/** + * @description: error codes + * @param: + * @return: + * @author: xielibo + * @date: 2020/11/2 + */ +public enum ResponseCode { + //success + SUCCESS(200, "success"), + + //request params is bad + BAD_REQUEST(400, "request params is bad"), + + //unauthorized + UNAUTHORIZED(401, "unauthorized"), + + //authorization expired + FORBIDDEN(403, "authorization expired"), + + //system error + ERROR(500, "system error"), + + //platform + INTEGRATION_PLUGIN_STOP_ERROR(50101, "plugin stop error"), + INTEGRATION_PLUGIN_START_ERROR(50102, "plugin start error"), + INTEGRATION_PLUGIN_UNINSTALL_ERROR(50103, "plugin uninstall error"), + INTEGRATION_PLUGIN_INSTALL_ERROR(50104, "plugin install error"), + ROLE_EXISTS_ERROR(50105, "role is exists"), + ROLE_PERMISSIONS_EXISTS_ERROR(50106, "role permissions is exists"), + USER_PHONE_EXISTS_ERROR(50107, "user phone is exists"), + USER_EMAIL_EXISTS_ERROR(50108, "user email is exists"), + USER_RESET_PASS_ORIGIN_PASS_ERROR(50109, "origin pass is error"), + USER_PASS_SAME_ERROR(50110, "New password can't be same as old password!"), + MENU_NAME_IS_EXISTS_ERROR(50111, "menu name is exists"), + MENU_NOT_ADD_SELF_AS_SUBMENU_ERROR(50112, "A menu cannot add itself as a submenu"), + MENU_HAS_SUBMENU_NOT_DELETE(50113, "This menu has a submenu, it is not allowed to delete"), + MENU_ASSIGNED_NOT_DELETE(50114, "This menu has already been assigned and cannot be deleted"), + USER_ACCOUNT_EXISTS_ERROR(50115, "user account is exists"), + PLUGIN_MENU_HAS_OTHER_PLUGIN_SUBMENU_UNINSTALL_ERROR(50116, "This plugin menu has submenus for other plugins"), + ROLE_NAME_IS_NOT_EMPTY_ERROR(50117, "Role name cannot be empty"), + ROLE_NAME_MAX_LENGTH_ERROR(50118, "Role name length cannot exceed 25 characters"), + ROLE_REMARK_MAX_LENGTH_ERROR(50119, "Role remark length cannot exceed 200 characters"), + USER_NAME_MAX_LENGTH_ERROR(50120, "User account length cannot exceed 30 characters"), + USER_NICKNAME_MAX_LENGTH_ERROR(50121, "User nickname length cannot exceed 30 characters"), + USER_REMARK_MAX_LENGTH_ERROR(50122, "User remark length cannot exceed 200 characters"), + USER_TELEPHONE_MAX_LENGTH_ERROR(50123, "User telephone length cannot exceed 11 characters"), + + WHITELIST_TITLE_MAX_LENGTH_ERROR(50124, "Whitelist title length cannot exceed 100 characters"), + WHITELIST_IPS_MAX_LENGTH_ERROR(50125, "Whitelist IP list length cannot exceed 200 characters"), + + WHITELIST_TITLE_EXISTS_ERROR(50126, "Whitelist title already exists"), + WHITELIST_HAS_DUPLICATE_IP_ERROR(50127, "Duplicate IP addresses exist in the whitelist"), + WHITELIST_IPS_EXISTS_ERROR(50128, "Some IP addresses in current whitelist " + + "already exists in the existing whitelist"), + INTEGRATION_UNLOAD_PLUGIN_INFO_QUERY_ERROR(50129, "query unload plugins information error"), + INTEGRATION_PLUGIN_URL_QUERY_ERROR(50130, "query plugin url error"), + INTEGRATION_PLUGIN_VERSION_ERROR(50131, + "The uploaded pluginJar package version should be consistent with the current datakit version: "), + + USER_PASSWORD_CANNOT_BE_EMPTY_ERROR(50132, "The password for new system user cannot be empty"); + + + private static Map codeMap = new HashMap(); + + private Integer code; + + private String msg; + + private String value; + + /** + * + */ + private ResponseCode() { + } + + /** + * @param value + */ + private ResponseCode(String value) { + this.value = value; + } + + /** + * @param code + * @param msg + */ + private ResponseCode(Integer code, String msg) { + this.code = code; + this.msg = msg; + } + + /** + * @return + */ + public Integer code() { + return this.code; + } + + /** + * @return + */ + public String msg() { + return this.msg; + } + + /** + * @return + */ + public String value() { + return this.value; + } + + /** + * @param codeValue + * @return + */ + public static ResponseCode getInstance(Integer codeValue) { + return getCodeMap().get(codeValue); + } + + /** + * @return + */ + private static Map getCodeMap() { + if (codeMap == null || codeMap.size() == 0) { + ResponseCode[] codeList = ResponseCode.values(); + for (ResponseCode c : codeList) { + codeMap.put(c.code(), c); + } + } + return codeMap; + } + + +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysLanguage.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysLanguage.java new file mode 100644 index 000000000..658ec55e5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysLanguage.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysLanguage.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysLanguage.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * @className: SysLanguage + * @description: SysLanguage + * @author: xielibo + * @date: 2022-11-16 16:39 + **/ +public enum SysLanguage { + + //zh + ZH("zh-CN", "zh-CN"), + //en + EN("en-US", "en-US"); + + private final String code; + private final String info; + + SysLanguage(String code, String info) { + this.code = code; + this.info = info; + } + + public String getCode() { + return code; + } + + public String getInfo() { + return info; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuPluginComponent.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuPluginComponent.java new file mode 100644 index 000000000..dd6f3dc24 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuPluginComponent.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysMenuPluginComponent.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuPluginComponent.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * @className: SysMenuPluginComponent + * @description: SysMenuPluginComponent + * @author: xielibo + * @date: 2022-09-05 9:00 PM + **/ +public enum SysMenuPluginComponent { + PAGE_IFRAME("iframe/index", "page iframe open"), + WIN_IFRAME("iframe/index", "win iframe open"); + + private final String code; + private final String info; + + SysMenuPluginComponent(String code, String info) { + this.code = code; + this.info = info; + } + + public String getCode() { + return code; + } + + public String getInfo() { + return info; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuPluginOpenWay.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuPluginOpenWay.java new file mode 100644 index 000000000..646f7ff26 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuPluginOpenWay.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysMenuPluginOpenWay.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuPluginOpenWay.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * @className: SysMenuPluginComponent + * @description: SysMenuPluginOpenWay + * @author: xielibo + * @date: 2022-09-05 9:00 PM + **/ +public enum SysMenuPluginOpenWay { + + PAGE_OPEN(1, "page open"), + WIN_OPEN(2, "win open"); + + private final Integer code; + private final String info; + + SysMenuPluginOpenWay(Integer code, String info) { + this.code = code; + this.info = info; + } + + public Integer getCode() { + return code; + } + + public String getInfo() { + return info; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuRouteOpenPosition.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuRouteOpenPosition.java new file mode 100644 index 000000000..ab9b831f2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuRouteOpenPosition.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysMenuRouteOpenPosition.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuRouteOpenPosition.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * @description: route postion + * @author: xielibo + * @date: 2022-09-05 9:00 PM + **/ +public enum SysMenuRouteOpenPosition { + + LEFT_MENU_BAR(1, "left menu bar"), + INDEX_INSTANCE_DATA(2, "index instance"); + + private final Integer code; + private final String info; + + SysMenuRouteOpenPosition(Integer code, String info) { + this.code = code; + this.info = info; + } + + public Integer getCode() { + return code; + } + + public String getInfo() { + return info; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuStatus.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuStatus.java new file mode 100644 index 000000000..ef517254a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuStatus.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysMenuStatus.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuStatus.java + * + * ------------------------------------------------------------------------- + */ +package org.opengauss.admin.common.enums; + +/** + * SysMenu Status + * + * @className: SysMenuStatus + * @author: xielibo + * @date: 2023-05-24 20:40 + **/ +public enum SysMenuStatus { + ENABLE("0"), + DISABLE("1"); + + /** + * statusCode + */ + private final String code; + + /** + * SysMenuStatus Constructor + * + * @param code statusCode + */ + SysMenuStatus(String code) { + this.code = code; + } + + /** + * get status code + * + * @return statusCode + */ + public String getCode() { + return code; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuVisible.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuVisible.java new file mode 100644 index 000000000..bbb3746a4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuVisible.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysMenuVisible.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysMenuVisible.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * @description: SysMenuVisible + * @author: xielibo + * @date: 2022-09-05 9:00 PM + **/ +public enum SysMenuVisible { + + HIDE("1", "hide"), + SHOW("0", "show"); + + private final String code; + private final String info; + + SysMenuVisible(String code, String info) { + this.code = code; + this.info = info; + } + + public String getCode() { + return code; + } + + public String getInfo() { + return info; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysPluginDownloadStatus.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysPluginDownloadStatus.java new file mode 100644 index 000000000..3ce2ebb5f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysPluginDownloadStatus.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginStatus.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysPluginDownloadStatus.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * plugin download status + * + * @author zhaochen + * @since 2025-03-20 + */ +public enum SysPluginDownloadStatus { + DOWNLOADED_STATUS(1, "downloaded"), + UN_DOWNLOAD_STATUS(0, "undownloaded"); + + private final Integer code; + private final String info; + + SysPluginDownloadStatus(Integer code, String info) { + this.code = code; + this.info = info; + } + + public Integer getCode() { + return code; + } + + public String getInfo() { + return info; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysPluginStatus.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysPluginStatus.java new file mode 100644 index 000000000..5af335a61 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysPluginStatus.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginStatus.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysPluginStatus.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * plugin status + * + * @author xielibo + */ +public enum SysPluginStatus { + + START(1, "started"), + DISABLE(2, "stoped"); + + private final Integer code; + private final String info; + + SysPluginStatus(Integer code, String info) { + this.code = code; + this.info = info; + } + + public Integer getCode() { + return code; + } + + public String getInfo() { + return info; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysPluginTheme.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysPluginTheme.java new file mode 100644 index 000000000..83569c4ba --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysPluginTheme.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginTheme.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysPluginTheme.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * plugin theme + * + * @author xielibo + */ +public enum SysPluginTheme { + + DARK("dark", "dark"), + + LIGHT("light", "light"); + + private final String code; + private final String info; + + SysPluginTheme(String code, String info) { + this.code = code; + this.info = info; + } + + public String getCode() { + return code; + } + + public String getInfo() { + return info; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysTaskStatus.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysTaskStatus.java new file mode 100644 index 000000000..df9970c34 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysTaskStatus.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysTaskStatus.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysTaskStatus.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * task status + * + * @author xielibo + */ +public enum SysTaskStatus { + + CREATED(0, "created"), + PROCESSING(1, "processing"), + FINISH(2, "finish"), + EXEC_FAIL(3, "execution failed"), + STOP(4, "stop") + ; + + private final Integer code; + private final String info; + + SysTaskStatus(Integer code, String info) { + this.code = code; + this.info = info; + } + + public Integer getCode() { + return code; + } + + public String getInfo() { + return info; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysTaskType.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysTaskType.java new file mode 100644 index 000000000..ebedea413 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysTaskType.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysTaskType.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/SysTaskType.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * task type + * + * @author xielibo + */ +public enum SysTaskType { + + DATA_MIGRATION(1, "data_migration"); + + private final Integer code; + private final String info; + + SysTaskType(Integer code, String info) { + this.code = code; + this.info = info; + } + + public Integer getCode() { + return code; + } + + public String getInfo() { + return info; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/UserStatus.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/UserStatus.java new file mode 100644 index 000000000..7aec02ae6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/UserStatus.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UserStatus.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/UserStatus.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums; + +/** + * user status + * + * @author xielibo + */ +public enum UserStatus { + + OK("0", "ok"), + + DISABLE("1", "disable"), + + DELETED("2", "delete"); + + private final String code; + private final String info; + + UserStatus(String code, String info) { + this.code = code; + this.info = info; + } + + public String getCode() { + return code; + } + + public String getInfo() { + return info; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/agent/AgentStatus.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/agent/AgentStatus.java new file mode 100644 index 000000000..7cffcb3db --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/agent/AgentStatus.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.enums.agent; + +import com.baomidou.mybatisplus.annotation.IEnum; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * AgentStatus + * + * @author: wangchao + * @Date: 2025/4/17 16:01 + * @Description: AgentStatus + * @since 7.0.0-RC2 + **/ +@AllArgsConstructor +@Getter +public enum AgentStatus implements IEnum { + /** + * AgentStatus uninstall + */ + UNINSTALL("uninstall"), + /** + * agent status starting + */ + STARTING("starting"), + /** + * AgentStatus install but it's stop + */ + STOP("stop"), + /** + * AgentStatus running + */ + RUNNING("running"); + + private final String value; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/agent/ObjectType.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/agent/ObjectType.java new file mode 100644 index 000000000..0e0a51594 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/agent/ObjectType.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.enums.agent; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * ObjectType + * + * @author: wangchao + * @Date: 2025/4/17 16:01 + * @Description: ObjectType + * @since 7.0.0-RC2 + **/ +@AllArgsConstructor +@Getter +public enum ObjectType { + OSHI("oshi"), + OS("os"), + DB("db"), + HTTP("http"); + private final String value; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/agent/StoragePolicy.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/agent/StoragePolicy.java new file mode 100644 index 000000000..397a8bf30 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/agent/StoragePolicy.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.enums.agent; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * StoragePolicy + * + * @author: wangchao + * @Date: 2025/4/17 16:01 + * @Description: StoragePolicy + * @since 7.0.0-RC2 + **/ +@AllArgsConstructor +@Getter +public enum StoragePolicy { + CUSTOM("custom"), + REAL_TIME("real_time"), + HISTORY("history"), + FINGERPRINT("fingerprint"), + TREE("tree"); + private final String value; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/agent/TaskType.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/agent/TaskType.java new file mode 100644 index 000000000..0b157206e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/agent/TaskType.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.enums.agent; + +import com.baomidou.mybatisplus.annotation.IEnum; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * TaskType + * + * @author: wangchao + * @Date: 2025/4/16 09:34 + * @Description: TaskType + * @since 7.0.0-RC2 + **/ +@AllArgsConstructor +@Getter +public enum TaskType implements IEnum { + OSHI_FIXED_METRIC("OSHI_FIXED_METRIC"), + OSHI_DYNAMIC_METRIC("OSHI_DYNAMIC_METRIC"), + OS_METRIC("OS_METRIC"), + DB_METRIC("DB_METRIC"), + OS_PIPE("OS_PIPE"), + DB_PIPE("DB_PIPE"); + private final String value; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/ClusterEnvCheckResultEnum.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/ClusterEnvCheckResultEnum.java new file mode 100644 index 000000000..354a76eb0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/ClusterEnvCheckResultEnum.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterEnvCheckResultEnum.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/ClusterEnvCheckResultEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.enums.ops; + +/** + * cluster node env check + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +public enum ClusterEnvCheckResultEnum { + SUCCESS, + FAILED +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/ClusterRoleEnum.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/ClusterRoleEnum.java new file mode 100644 index 000000000..3d1908a1e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/ClusterRoleEnum.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterRoleEnum.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/ClusterRoleEnum.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums.ops; + +/** + * @author lhf + * @date 2022/8/6 17:10 + **/ +public enum ClusterRoleEnum { + /** + * master + */ + MASTER, + /** + * slave + */ + SLAVE, + /** + * cascade slave + */ + CASCADE; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/DbTypeEnum.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/DbTypeEnum.java new file mode 100644 index 000000000..7aa693e6e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/DbTypeEnum.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DbTypeEnum.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/DbTypeEnum.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums.ops; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author lhf + * @date 2023/1/13 11:00 + **/ +@AllArgsConstructor +@Getter +public enum DbTypeEnum { + MYSQL("com.mysql.cj.jdbc.Driver"), + OPENGAUSS("org.opengauss.Driver"), + POSTGRESQL("org.postgresql.Driver"); + + private String driverClass; + + public static DbTypeEnum typeOf(String dbType) { + if (StrUtil.isEmpty(dbType)) { + return null; + } + + for (DbTypeEnum enumConstant : DbTypeEnum.class.getEnumConstants()) { + if (enumConstant.name().equalsIgnoreCase(dbType)) { + return enumConstant; + } + } + return null; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/DeployTypeEnum.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/DeployTypeEnum.java new file mode 100644 index 000000000..07fe703b7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/DeployTypeEnum.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DeployTypeEnum.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/DeployTypeEnum.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums.ops; + +/** + * deploy type + * + * @author lhf + * @date 2022/8/6 17:08 + **/ +public enum DeployTypeEnum { + /** + * cluster + */ + CLUSTER, + /** + * single node + */ + SINGLE_NODE; + + public static DeployTypeEnum nameOf(String deployType) { + DeployTypeEnum[] enumConstants = DeployTypeEnum.class.getEnumConstants(); + for (DeployTypeEnum enumConstant : enumConstants) { + if (enumConstant.name().equalsIgnoreCase(deployType)) { + return enumConstant; + } + } + return null; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/HostAuthenticationTypeEnum.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/HostAuthenticationTypeEnum.java new file mode 100644 index 000000000..98059c0ba --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/HostAuthenticationTypeEnum.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostAuthenticationTypeEnum.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/HostAuthenticationTypeEnum.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums.ops; + +/** + * Host authentication type + * + * @author lhf + * @date 2022/8/4 22:21 + **/ +public enum HostAuthenticationTypeEnum { + /** + * User Name Password + */ + USERNAME_PASSWORD; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/HostEnvStatusEnum.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/HostEnvStatusEnum.java new file mode 100644 index 000000000..55b171d99 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/HostEnvStatusEnum.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostEnvStatusEnum.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/HostEnvStatusEnum.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums.ops; + +/** + * Host Environment Status + * + * @author lhf + * @date 2022/8/10 10:34 + **/ +public enum HostEnvStatusEnum { + NORMAL, + WARMING, + ERROR, + INFO +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/HostFileTypeEnum.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/HostFileTypeEnum.java new file mode 100644 index 000000000..2de76dea7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/HostFileTypeEnum.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostFileTypeEnum.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/HostFileTypeEnum.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums.ops; + +/** + * @author lhf + * @date 2022/8/8 10:12 + **/ +public enum HostFileTypeEnum { + FILE, + DIRECTORY +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/InstallModeEnum.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/InstallModeEnum.java new file mode 100644 index 000000000..79ab7609c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/InstallModeEnum.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * InstallModeEnum.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/InstallModeEnum.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums.ops; + +/** + * Installation Mode + * + * @author lhf + * @date 2022/8/4 23:00 + **/ +public enum InstallModeEnum { + /** + * online + */ + ON_LINE, + /** + * offline + */ + OFF_LINE; +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/OpenGaussVersionEnum.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/OpenGaussVersionEnum.java new file mode 100644 index 000000000..25ad81a65 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/OpenGaussVersionEnum.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpenGaussVersionEnum.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/OpenGaussVersionEnum.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.enums.ops; + +/** + * openGauss version + * + * @author lhf + * @date 2022/8/4 23:06 + **/ +public enum OpenGaussVersionEnum { + /** + * Enterprise Edition + */ + ENTERPRISE, + /** + * Minimalist version + */ + MINIMAL_LIST, + /** + * Light weight version + */ + LITE, +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/OpsClusterTaskStatusEnum.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/OpsClusterTaskStatusEnum.java new file mode 100644 index 000000000..055f512b1 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/OpsClusterTaskStatusEnum.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskStatusEnum.java + * + * IDENTIFICATION + * openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/OpsClusterTaskStatusEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.enums.ops; + +import java.util.Objects; + +/** + * OpsClusterTaskStatusEnum + * + * @author wangchao + * @date 2024/06/15 09:26 + */ +public enum OpsClusterTaskStatusEnum { + + // 任务状态 + /** + * 草稿 + */ + DRAFT("DRAFT"), + /** + * 待执行 + */ + PENDING("PENDING"), + /** + * 执行等待中 + */ + WAITING("WAITING"), + /** + * 执行中 + */ + RUNNING("RUNNING"), + /** + * 执行成功 + */ + SUCCESS("SUCCESS"), + /** + * 执行失败 + */ + FAILED("FAILED"), + /** + * 执行取消 + */ + CANCELED("CANCELED"), + /** + * 未知 任务不存在 + */ + UNKNOWN("UNKNOWN"); + + private String status; + + OpsClusterTaskStatusEnum(String status) { + this.status = status; + } + + public String getStatus() { + return status; + } + + + public static boolean isPending(OpsClusterTaskStatusEnum taskStatusEnum) { + return Objects.equals(taskStatusEnum, PENDING); + } + + public static boolean isFailed(OpsClusterTaskStatusEnum taskStatusEnum) { + return Objects.equals(taskStatusEnum, FAILED) || Objects.equals(taskStatusEnum, CANCELED); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/OpsHostPortUsedStatusEnum.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/OpsHostPortUsedStatusEnum.java new file mode 100644 index 000000000..3e410d55d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/OpsHostPortUsedStatusEnum.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024-2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsHostPortUsedStatusEnum.java + * + * IDENTIFICATION + * visualtool-common/src/main/java/org/opengauss/admin/common/enums/ops/OpsHostPortUsedStatusEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.enums.ops; + + +/** + * OpsHostPortUsedStatusEnum + * + * @author wangchao + * @since 2024/06/15 09:26 + */ +public enum OpsHostPortUsedStatusEnum { + /** + * not used + */ + NO_USED("NO_USED"), + + /** + * database used + */ + DATABASE_USED("DATABASE_USED"), + + /** + * database cm server used + */ + DATABASE_CM_USED("DATABASE_CM_USED"), + + /** + * host server used + */ + HOST_USED("HOST_USED"); + + private String status; + + OpsHostPortUsedStatusEnum(String status) { + this.status = status; + } + + public String getStatus() { + return status; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/CustomException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/CustomException.java new file mode 100644 index 000000000..f6a5ad3c4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/CustomException.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CustomException.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/exception/CustomException.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.exception; + +/** + * Customer Exception + * + * @author xielibo + */ +public class CustomException extends RuntimeException { + private static final long serialVersionUID = 1L; + + private Integer code; + + private String message; + + public CustomException(String message) { + this.message = message; + } + + public CustomException(String message, Integer code) { + this.message = message; + this.code = code; + } + + public CustomException(String message, Throwable e) { + super(message, e); + this.message = message; + } + + @Override + public String getMessage() { + return message; + } + + public Integer getCode() { + return code; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/SecureException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/SecureException.java new file mode 100644 index 000000000..d300ddc75 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/SecureException.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.exception; + +/** + * SecureException + * + * @author: wangchao + * @Date: 2025/7/28 10:41 + * @since 7.0.0-RC2 + **/ +public class SecureException extends RuntimeException { + /** + * secure exception + * + * @param message exception message + */ + public SecureException(String message) { + super(message); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ServiceException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ServiceException.java new file mode 100644 index 000000000..332809aa3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ServiceException.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ServiceException.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ServiceException.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.exception; + +/** + * Business Exception + * + * @author xielibo + */ +public final class ServiceException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * code + */ + private Integer code; + + /** + * message + */ + private String message; + + /** + * detailMessage + */ + private String detailMessage; + + /** + * parameterless constructor + */ + public ServiceException() { + } + + public ServiceException(String message) { + this.message = message; + } + + public ServiceException(String message, Integer code) { + this.message = message; + this.code = code; + } + + public String getDetailMessage() { + return detailMessage; + } + + @Override + public String getMessage() { + return message; + } + + public Integer getCode() { + return code; + } + + public ServiceException setMessage(String message) { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) { + this.detailMessage = detailMessage; + return this; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/UtilException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/UtilException.java new file mode 100644 index 000000000..a7013a0a5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/UtilException.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UtilException.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/exception/UtilException.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.exception; + +/** + * Tool Exception + * + * @author xielibo + */ +public class UtilException extends RuntimeException { + + public UtilException(Throwable e) { + super(e.getMessage(), e); + } + + public UtilException(String message) { + super(message); + } + + public UtilException(String message, Throwable throwable) { + super(message, throwable); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/base/BaseException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/base/BaseException.java new file mode 100644 index 000000000..7552325e0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/base/BaseException.java @@ -0,0 +1,99 @@ +/** + Copyright (c) 2005-2012 https: github.com zhangkaitao. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.common.exception.base; + +import org.opengauss.admin.common.utils.MessageUtils; +import org.opengauss.admin.common.utils.StringUtils; + +/** + * Base Exception + * + * @author xielibo + */ +public class BaseException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * module + */ + private String module; + + /** + * error code + */ + private String code; + + /** + * args + */ + private Object[] args; + + /** + * default message + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() { + String message = null; + if (!StringUtils.isEmpty(code)) { + message = MessageUtils.message(code, args); + } + if (message == null) { + message = defaultMessage; + } + return message; + } + + public String getModule() { + return module; + } + + public String getCode() { + return code; + } + + public Object[] getArgs() { + return args; + } + + public String getDefaultMessage() { + return defaultMessage; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/FileException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/FileException.java new file mode 100644 index 000000000..d683c5cea --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/FileException.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * FileException.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/FileException.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.exception.file; + +import org.opengauss.admin.common.exception.base.BaseException; + +/** + * File Exception + * + * @author xielibo + */ +public class FileException extends BaseException { + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) { + super("file", code, args, null); + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/FileNameLengthLimitExceededException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 000000000..8118dd669 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * FileNameLengthLimitExceededException.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/FileNameLengthLimitExceededException.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.exception.file; + +/** + * File Name Length Limit Exception + * + * @author xielibo + */ +public class FileNameLengthLimitExceededException extends FileException { + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) { + super("upload.filename.exceed.length", new Object[]{defaultFileNameLength}); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/FileSizeLimitExceededException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 000000000..269bc9f1f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * FileSizeLimitExceededException.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/FileSizeLimitExceededException.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.exception.file; + +/** + * File Size Limit Exception + * + * @author xielibo + */ +public class FileSizeLimitExceededException extends FileException { + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) { + super("upload.exceed.maxSize", new Object[]{defaultMaxSize}); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/InvalidExtensionException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/InvalidExtensionException.java new file mode 100644 index 000000000..c3abaef76 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/InvalidExtensionException.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * InvalidExtensionException.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/exception/file/InvalidExtensionException.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.exception.file; + +import org.apache.commons.fileupload.FileUploadException; + +import java.util.Arrays; + +/** + * Invalid Exception + * + * @author xielibo + */ +public class InvalidExtensionException extends FileUploadException { + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) { + super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() { + return allowedExtension; + } + + public String getExtension() { + return extension; + } + + public String getFilename() { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) { + super(allowedExtension, extension, filename); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/AgentConnectionException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/AgentConnectionException.java new file mode 100644 index 000000000..6a02f3df4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/AgentConnectionException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +package org.opengauss.admin.common.exception.ops; + +/** + * AgentHealthCheckException + * + * @author: wangchao + * @Date: 2025/9/10 15:36 + * @since 7.0.0-RC2 + **/ +public class AgentConnectionException extends RuntimeException { + /** + * agent health check exception + * + * @param message message + */ + public AgentConnectionException(String message) { + super(message); + } + + /** + * agent health check exception + * + * @param message message + * @param cause cause + */ + public AgentConnectionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/AgentHealthCheckException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/AgentHealthCheckException.java new file mode 100644 index 000000000..ce2f8af01 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/AgentHealthCheckException.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +package org.opengauss.admin.common.exception.ops; + +import org.springframework.http.HttpStatusCode; + +import java.util.Locale; + +/** + * AgentHealthCheckException + * + * @author: wangchao + * @Date: 2025/9/10 15:36 + * @since 7.0.0-RC2 + **/ +public class AgentHealthCheckException extends RuntimeException { + private final HttpStatusCode statusCode; + + /** + * agent health check exception + * + * @param message message + * @param statusCode status code + */ + public AgentHealthCheckException(String message, HttpStatusCode statusCode) { + super(message); + this.statusCode = statusCode; + } + + /** + * agent health check exception + * + * @param message message + * @param cause cause + */ + public AgentHealthCheckException(String message, Throwable cause) { + super(message, cause); + this.statusCode = null; + } + + /** + * status code + * + * @return status code + */ + public HttpStatusCode getStatusCode() { + return statusCode; + } + + /** + * error details + * + * @return details + */ + public String getDetailedMessage() { + if (statusCode != null) { + return String.format(Locale.getDefault(), "HTTP %d: %s", statusCode.value(), getMessage()); + } + return getMessage(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/AgentTaskException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/AgentTaskException.java new file mode 100644 index 000000000..94b27bfd5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/AgentTaskException.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.exception.ops; + +/** + * AgentTaskException + * + * @author: wangchao + * @Date: 2025/4/21 16:34 + * @Description: AgentTaskException + * @since 7.0.0-RC2 + **/ +public class AgentTaskException extends RuntimeException { + private String message; + + /** + * AgentTaskException + * + * @param message message + */ + public AgentTaskException(String message) { + super(message); + this.message = message; + } + + /** + * AgentTaskException + * + * @param message message + * @param e error stacktrace + */ + public AgentTaskException(String message, Throwable e) { + super(message, e); + this.message = message; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("AgentTaskException{"); + sb.append("message='").append(message).append('\''); + sb.append(", cause=").append(getCause()); + sb.append(", stackTrace=").append(getFilteredStackTrace()); + sb.append('}'); + return sb.toString(); + } + + private String getFilteredStackTrace() { + StackTraceElement[] stackTrace = getStackTrace(); + StringBuilder sb = new StringBuilder(); + for (StackTraceElement element : stackTrace) { + if (!element.getClassName().contains("org.opengauss")) { + sb.append(element).append(System.lineSeparator()); + } + } + return sb.toString(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/OpsException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/OpsException.java new file mode 100644 index 000000000..5a8ca62a9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/OpsException.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsException.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/OpsException.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.exception.ops; + +/** + * + * @author lhf + * @date 2022/8/4 22:24 + **/ +public class OpsException extends RuntimeException { + private String message; + + public OpsException(String message) { + super(message); + this.message = message; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("OpsException{"); + sb.append("message='").append(message).append('\''); + sb.append(", cause=").append(getCause()); + sb.append(", stackTrace=").append(getFilteredStackTrace()); + sb.append('}'); + return sb.toString(); + } + + private String getFilteredStackTrace() { + StackTraceElement[] stackTrace = getStackTrace(); + StringBuilder sb = new StringBuilder(); + for (StackTraceElement element : stackTrace) { + if (!element.getClassName().contains("org.opengauss.admin")) { + sb.append(element).append(System.lineSeparator()); + } + } + return sb.toString(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/UserAlreadyExistsException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/UserAlreadyExistsException.java new file mode 100644 index 000000000..e384d542c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/UserAlreadyExistsException.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UserAlreadyExistsException.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/exception/ops/UserAlreadyExistsException.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.exception.ops; + +public class UserAlreadyExistsException extends OpsException{ + public UserAlreadyExistsException(String message) { + super(message); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/user/UserException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/user/UserException.java new file mode 100644 index 000000000..c9b0c6530 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/user/UserException.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UserException.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/exception/user/UserException.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.exception.user; + + +import org.opengauss.admin.common.exception.base.BaseException; + +/** + * User Exception + * + * @author xielibo + */ +public class UserException extends BaseException { + private static final long serialVersionUID = 1L; + + public UserException(String code, Object[] args) { + super("user", code, args, null); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/user/UserPasswordNotMatchException.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 000000000..aa2925a3b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UserPasswordNotMatchException.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/exception/user/UserPasswordNotMatchException.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.exception.user; + +/** + * The user password is incorrect or does not meet the specification exception class + * + * @author xielibo + */ +public class UserPasswordNotMatchException extends UserException { + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() { + super("user.password.not.match", null); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/filter/RepeatableFilter.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/filter/RepeatableFilter.java new file mode 100644 index 000000000..d6becfb31 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/filter/RepeatableFilter.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * RepeatableFilter.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/filter/RepeatableFilter.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.filter; + +import org.opengauss.admin.common.utils.StringUtils; +import org.springframework.http.MediaType; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * Repeatable Filter + * + * @author xielibo + */ +public class RepeatableFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + ServletRequest requestWrapper = null; + if (request instanceof HttpServletRequest && StringUtils.startsWithIgnoreCase(request.getContentType(), + MediaType.APPLICATION_JSON_VALUE)) { + requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); + } + if (null == requestWrapper) { + chain.doFilter(request, response); + } else { + chain.doFilter(requestWrapper, response); + } + } + + @Override + public void destroy() { + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/filter/RepeatedlyRequestWrapper.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 000000000..18bdaafd3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,81 @@ +/** + Copyright ruoyi. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.common.filter; + +import org.opengauss.admin.common.utils.http.HttpHelper; + +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * Construct a request that can read inputStream repeatedly + * + * @author xielibo + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper { + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException { + super(request); + request.setCharacterEncoding("UTF-8"); + response.setCharacterEncoding("UTF-8"); + + body = HttpHelper.getBodyString(request).getBytes("UTF-8"); + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() { + @Override + public int read() throws IOException { + return bais.read(); + } + + @Override + public int available() throws IOException { + return body.length; + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + + } + }; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/filter/XssAttacksFilter.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/filter/XssAttacksFilter.java new file mode 100644 index 000000000..89da9d3f8 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/filter/XssAttacksFilter.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * XssAttacksFilter.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/filter/XssAttacksFilter.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.filter; + +import org.opengauss.admin.common.utils.StringUtils; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Filters to prevent XSS attacks + * + * @author xielibo + */ +public class XssAttacksFilter implements Filter { + + public List excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + String excludesArrStr = filterConfig.getInitParameter("excludes"); + if (StringUtils.isNotEmpty(excludesArrStr)) { + String[] urlArr = excludesArrStr.split(","); + for (int i = 0; urlArr != null && i < urlArr.length; i++) { + excludes.add(urlArr[i]); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + if (doExcludeUrlHandler(req)) { + chain.doFilter(request, response); + return; + } + XssAttacksHttpServletRequestWrapper xssWrapper = new XssAttacksHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssWrapper, response); + } + + private boolean doExcludeUrlHandler(HttpServletRequest request) { + String url = request.getServletPath(); + String method = request.getMethod(); + if (method == null || method.matches("GET") || method.matches("DELETE")) { + return true; + } + return StringUtils.matches(url, excludes); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/filter/XssAttacksHttpServletRequestWrapper.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/filter/XssAttacksHttpServletRequestWrapper.java new file mode 100644 index 000000000..b3ef63e23 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/filter/XssAttacksHttpServletRequestWrapper.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * XssAttacksHttpServletRequestWrapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/filter/XssAttacksHttpServletRequestWrapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.filter; + +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.common.utils.html.EscapeUtil; +import org.apache.commons.io.IOUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +/** + * XSS filter handler + * + * @author xielibo + */ +public class XssAttacksHttpServletRequestWrapper extends HttpServletRequestWrapper { + /** + * @param request + */ + public XssAttacksHttpServletRequestWrapper(HttpServletRequest request) { + super(request); + } + + @Override + public String[] getParameterValues(String name) { + String[] values = super.getParameterValues(name); + if (values != null) { + int length = values.length; + String[] esValues = new String[length]; + for (int i = 0; i < length; i++) { + esValues[i] = EscapeUtil.clean(values[i]).trim(); + } + return esValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + if (!checkJsonRequest()) { + return super.getInputStream(); + } + + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (StringUtils.isEmpty(json)) { + return super.getInputStream(); + } + + json = EscapeUtil.clean(json).trim(); + byte[] jsonBytes = json.getBytes("utf-8"); + final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes); + return new ServletInputStream() { + @Override + public boolean isFinished() { + return true; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public int available() throws IOException { + return jsonBytes.length; + } + @Override + public void setReadListener(ReadListener readListener) { + } + + @Override + public int read() throws IOException { + return bis.read(); + } + }; + } + public boolean checkJsonRequest() { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/AesGcmUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/AesGcmUtils.java new file mode 100644 index 000000000..c4f3e6170 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/AesGcmUtils.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.utils; + +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.exception.SecureException; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.Arrays; +import java.util.Base64; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; + +/** + * AesGcmUtils + * + * @author wangchao + * @since 2024/10/29 09:26 + */ +@Slf4j +public class AesGcmUtils { + private static final int GCM_IV_LENGTH = 12; + private static final int GCM_TAG_LENGTH = 16; + private static final SecureRandom RANDOM = new SecureRandom(); + private static final Base64.Encoder B64_ENCODER = Base64.getEncoder(); + private static final Base64.Decoder B64_DECODER = Base64.getDecoder(); + private static final Object LOCK = new Object(); + + private static volatile SecretKey aesKey = null; + + /** + * initialize key + */ + public static void initializeKey() { + if (aesKey == null) { + synchronized (LOCK) { + if (aesKey == null) { + aesKey = KeyLoader.loadOrGenerateKey(); + } + } + log.info("initialize AES key success"); + } + } + + /** + * encrypt plaintext + * + * @param plaintext plaintext + * @return ciphertext + */ + public static String encrypt(String plaintext) { + try { + byte[] iv = new byte[GCM_IV_LENGTH]; + RANDOM.nextBytes(iv); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv); + cipher.init(Cipher.ENCRYPT_MODE, aesKey, spec); + byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); + ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + ciphertext.length); + byteBuffer.put(iv); + byteBuffer.put(ciphertext); + return B64_ENCODER.encodeToString(byteBuffer.array()); + } catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException e) { + throw new SecureException("AES encrypt algorithm error :" + e.getMessage()); + } catch (NoSuchAlgorithmException | BadPaddingException | InvalidKeyException e) { + throw new SecureException("AES encrypt failed :" + e.getMessage()); + } + } + + /** + * decrypt ciphertext + * + * @param base64Ciphertext ciphertext + * @return plaintext + */ + public static String decrypt(String base64Ciphertext) { + try { + byte[] data = B64_DECODER.decode(base64Ciphertext); + ByteBuffer byteBuffer = ByteBuffer.wrap(data); + byte[] iv = new byte[GCM_IV_LENGTH]; + byteBuffer.get(iv); + byte[] encryptedData = new byte[byteBuffer.remaining()]; + byteBuffer.get(encryptedData); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv); + cipher.init(Cipher.DECRYPT_MODE, aesKey, spec); + return new String(cipher.doFinal(encryptedData), StandardCharsets.UTF_8); + } catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException e) { + throw new SecureException("AES decrypt algorithm error :" + e.getMessage()); + } catch (NoSuchAlgorithmException | BadPaddingException | InvalidKeyException e) { + throw new SecureException("AES decrypt failed :" + e.getMessage()); + } + } + + static class KeyLoader { + private static final int AES_KEY_SIZE = 256; + private static final String KEYSTORE_TYPE = "JCEKS"; + private static final String KEY_ALGORITHM = "AES"; + private static final String KEY_ALIAS = "aes_alias_key"; + private static final String AES_KEY_ENV = "DATA_KIT_AES_KEY"; + private static final Path KEYSTORE_PATH = Paths.get(System.getProperty("user.home"), ".secure", + "datakit.jceks"); + + /** + * KeyStore, load or generate key from keystore.jceks. + * if not exist, generate a new one and saved in keystore.jceks. + * the jceks file is saved in the current directory. like user.dir/keystore.jceks + * + * @return SecretKey + */ + public static synchronized SecretKey loadOrGenerateKey() { + if (StrUtil.isEmpty(System.getenv(AES_KEY_ENV)) && StrUtil.isEmpty(System.getProperty(AES_KEY_ENV))) { + throw new SecureException("there must be have the environment variable: " + AES_KEY_ENV); + } + try { + SecretKey initAesKey = null; + char[] password = getAesKeyEnv(); + KeyStore keyStore = loadOrCreateKeystore(password); + if (!keyStore.containsAlias(KEY_ALIAS)) { + initAesKey = generateAesKey(); + saveSecretKey(keyStore, initAesKey, password); + saveKeystore(keyStore, password); + } else { + Key key = keyStore.getKey(KEY_ALIAS, password); + if (key instanceof SecretKey aliasKey) { + initAesKey = aliasKey; + } + } + Arrays.fill(password, '\0'); + return initAesKey; + } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) { + throw new SecureException("load or generate key failed: " + e.getMessage()); + } + } + + private static char[] getAesKeyEnv() { + String aesEnvKey = System.getenv(AES_KEY_ENV); + if (StrUtil.isNotEmpty(aesEnvKey)) { + return aesEnvKey.toCharArray(); + } + aesEnvKey = System.getProperty(AES_KEY_ENV); + if (StrUtil.isNotEmpty(aesEnvKey)) { + return aesEnvKey.toCharArray(); + } else { + throw new SecureException("there must be have the environment variable: " + AES_KEY_ENV); + } + } + + private static KeyStore loadOrCreateKeystore(char[] password) throws SecureException { + try { + KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE); + if (Files.exists(KEYSTORE_PATH)) { + try (InputStream is = Files.newInputStream(KEYSTORE_PATH)) { + keyStore.load(is, password); + } + } else { + keyStore.load(null, null); + } + return keyStore; + } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) { + throw new SecureException("load or create keystore failed: " + e.getMessage()); + } + } + + private static SecretKey generateAesKey() throws NoSuchAlgorithmException { + KeyGenerator keyGen = KeyGenerator.getInstance(KEY_ALGORITHM); + keyGen.init(AES_KEY_SIZE, SecureRandom.getInstanceStrong()); + return keyGen.generateKey(); + } + + private static void saveSecretKey(KeyStore ks, SecretKey key, char[] password) throws KeyStoreException { + KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(key); + KeyStore.PasswordProtection protParam = new KeyStore.PasswordProtection(password); + ks.setEntry(KEY_ALIAS, entry, protParam); + } + + private static void saveKeystore(KeyStore ks, char[] password) throws SecureException { + try { + Files.createDirectories(KEYSTORE_PATH.getParent()); + try (OutputStream os = Files.newOutputStream(KEYSTORE_PATH, StandardOpenOption.CREATE)) { + ks.store(os, password); + } catch (CertificateException | KeyStoreException | NoSuchAlgorithmException e) { + throw new SecureException("save keystore failed: " + e.getMessage()); + } + } catch (IOException e) { + throw new SecureException("save keystore failed: " + e.getMessage()); + } + } + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ChainAssert.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ChainAssert.java new file mode 100644 index 000000000..0758cb537 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ChainAssert.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.utils; + +import cn.hutool.core.util.StrUtil; + +import org.opengauss.admin.common.exception.ops.OpsException; + +import java.util.function.Function; + +/** + * ChainAssert + * + * @author: wangchao + * @Date: 2025/3/18 16:17 + * @Description: ChainAssert + * @since 7.0.0-RC2 + **/ +public class ChainAssert { + private final T target; + + /** + * private constructor + * + * @param target target object + */ + private ChainAssert(T target) { + this.target = target; + } + + /** + * create a ChainAssert instance with target object + * + * @param target target object + * @param target object type + * @return ChainAssert instance + */ + public static ChainAssert of(T target) { + return new ChainAssert<>(target); + } + + /** + * check target object is not null + * + * @param message error message + * @return ChainAssert instance + */ + public ChainAssert nonNull(String message) { + if (target == null) { + throw new OpsException(message); + } + return this; + } + + /** + * check target object field is not blank + * + * @param fieldGetter getter function for field + * @param message error message + * @return ChainAssert instance + */ + public ChainAssert validateField(Function fieldGetter, String message) { + if (target == null) { + throw new OpsException("Target object is null"); + } + String value = fieldGetter.apply(target); + if (StrUtil.isBlank(value)) { + throw new OpsException(message); + } + return this; + } + + /** + * check target object field is none null + * + * @param fieldGetter getter function for field + * @param message error message + * @return ChainAssert instance + */ + public ChainAssert validateFieldNoneNull(Function fieldGetter, String message) { + if (target == null) { + throw new OpsException("Target object is null"); + } + Object value = fieldGetter.apply(target); + if (value == null) { + throw new OpsException(message); + } + return this; + } + + /** + * check condition is true + * + * @param isCondition isCondition + * @param message error message + * @return ChainAssert instance + */ + public ChainAssert isTrue(boolean isCondition, String message) { + if (!isCondition) { + throw new OpsException(message); + } + return this; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/CommonUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/CommonUtils.java new file mode 100644 index 000000000..72f782976 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/CommonUtils.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.utils; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.exception.ops.OpsException; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Locale; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Description: DataKit base tools common class + * reference : org.opengauss.admin.plugin.service.impl.MigrationTaskHostRefServiceImpl + * #checkJavaEnv(org.opengauss.admin.system.plugin.beans.SshLogin) + * + * @author: wangchao + * @Date: 2025/6/17 17:31 + * @since 7.0.0-RC2 + */ +@Slf4j +public class CommonUtils { + private static final Pattern JDK_VERSION_PATTERN = Pattern.compile( + "(?:openjdk|java) version \"(?:1\\.)?([8-9]|1\\d|\\d{2,})(?:[\\.-]|$|\\\")"); + private static final Pattern JDK_VERSION_SEC_PATTERN = Pattern.compile( + "(?:1\\.)?([8-9]|1\\d|\\d{2,})(?:[\\.-]|$|\\\")"); + + /** + * java version info by command "java -version" + * some example {@link org.opengauss.admin.system.service.agent.impl.JavaVersionParseTest} + * + * @param version version info by command "java -version" + * @return java version major + */ + public static int getJavaVersionMajor(String version) { + if (version == null || version.isEmpty()) { + return -1; + } + Matcher matcher = JDK_VERSION_PATTERN.matcher(version); + if (matcher.find()) { + String majorVersionStr = matcher.group(1); + try { + return Integer.parseInt(majorVersionStr); + } catch (NumberFormatException e) { + log.error("Failed to parse Java version: {}", majorVersionStr, e); + } + } + // 备选匹配 + matcher = JDK_VERSION_SEC_PATTERN.matcher(version); + if (matcher.find()) { + String majorVersionStr = matcher.group(1); + try { + return Integer.parseInt(majorVersionStr); + } catch (NumberFormatException e) { + log.error("Failed to parse Java version: {}", majorVersionStr, e); + } + } + log.error("Unrecognized Java version format: {}", version); + return -1; + } + + /** + *

+     * path validate contans :
+     *     path null or empty validate
+     *     path illegal char validate
+     *     path length validate
+     *     path deep validate
+     *     path normalize validate
+     *     path os validate
+     * some example {@link org.opengauss.admin.system.service.agent.impl.PahtValidateTest}
+     * 
+ * + * @param path path + * @throws OpsException OpsException + */ + public static void pathValidate(String path) throws OpsException { + PathValidatorHolder.combinedValidator().validate(path); + } + + /** + * path validator holder + */ + private static class PathValidatorHolder { + private static final PathValidator PATH_NULL_CHECK = path -> { + if (Objects.isNull(path) || path.isEmpty()) { + throw new OpsException("path is null or empty"); + } + }; + + /** + * The blacklist character filtering intercepts paths containing special characters + * (such as ;, |, $, \0) to prevent command injection + */ + private static final Pattern ILLEGAL_CHARS = Pattern.compile("[;|$\0]"); + private static final PathValidator PATH_ILLEGAL_CHECK = path -> { + if (ILLEGAL_CHARS.matcher(path).find()) { + throw new OpsException("path contain illegal char: " + path); + } + }; + private static final PathValidator PATH_LENGTH_CHECK = path -> { + if (Objects.isNull(path)) { + throw new OpsException("path length must be less than 255, but is null"); + } + if (path.length() > 255) { + throw new OpsException( + String.format(Locale.getDefault(), "path length must be less than 255, but got %d", path.length())); + } + }; + private static final PathValidator PATH_DEEP_CHECK = path -> { + Path normalizedPath = Paths.get(path).normalize(); + if (normalizedPath.getNameCount() > 5) { + throw new OpsException("path deep must be less than 5, but got " + normalizedPath.getNameCount()); + } + }; + private static final PathValidator PATH_NORMALIZE_CHECK = path -> { + if (Paths.get(path).isAbsolute()) { // 仅检查绝对路径 + Path normalizedPath = Paths.get(path).normalize(); + if (!isPathEqual(Paths.get(path), normalizedPath)) { + throw new OpsException( + String.format(Locale.getDefault(), "Path normalized mismatch! Input: '%s' -> Normalized: '%s'", + path, normalizedPath.toString())); + } + } + }; + private static final PathValidator PATH_OS_REGEX_CHECK = path -> { + // Regular expression verification matches path formats according to system types + // (e.g., Linux paths must start with '/', while Windows paths include a drive letter) + String regex = isLinux() ? "^/[a-zA-Z0-9_./-]+$" : "^[a-zA-Z]:\\\\[a-zA-Z0-9_ .\\\\-]+$"; + if (!path.matches(regex)) { + throw new OpsException("path regex not match: " + path); + } + }; + + private static boolean isPathEqual(Path path1, Path path2) { + if (isLinux()) { + return path1.toAbsolutePath().normalize().equals(path2.toAbsolutePath()); + } + return path1.toAbsolutePath().normalize().toString().equalsIgnoreCase(path2.toAbsolutePath().toString()); + } + + private static boolean isLinux() { + return !System.getProperty("os.name").toLowerCase().contains("win"); + } + + /** + * combined validator + * + * @return PathValidator + */ + public static PathValidator combinedValidator() { + return PATH_NULL_CHECK.andThen(PATH_ILLEGAL_CHECK) + .andThen(PATH_LENGTH_CHECK) + .andThen(PATH_NORMALIZE_CHECK) + .andThen(PATH_DEEP_CHECK); + } + + /** + * PathValidator + */ + @FunctionalInterface + public interface PathValidator { + /** + * validate path + * + * @param path path + * @throws OpsException OpsException + */ + void validate(String path) throws OpsException; + + /** + * combined validator + * + * @param after after + * @return PathValidator + */ + default PathValidator andThen(PathValidator after) { + Objects.requireNonNull(after); + return (String path) -> { + this.validate(path); + after.validate(path); + }; + } + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/DateUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/DateUtils.java new file mode 100644 index 000000000..6a708f888 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/DateUtils.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DateUtils.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/DateUtils.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils; + +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Date Tool + * + * @author xielibo + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils { + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * getNowDate + * + */ + public static Date getNowDate() { + return new Date(); + } + + /** + * getNowDate, format is 'yyyy-MM-dd' + * + * @return String + */ + public static String getDate() { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) { + try { + return new SimpleDateFormat(format).parse(ts); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * datePath. like 2018/08/08 + */ + public static final String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * getNowDate. format is 'yyyyMMdd' + */ + public static final String dateTime() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * Parse Date + */ + public static Date parseDate(Object str) { + if (str == null) { + return null; + } + try { + return parseDate(str.toString(), parsePatterns); + } catch (ParseException e) { + return null; + } + } + + /** + * getServerStartDate + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/MathUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/MathUtils.java new file mode 100644 index 000000000..f6e597b0a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/MathUtils.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * MathUtils + * + * @author: wangchao + * @Date: 2025/6/6 16:25 + * @since 7.0.0-RC2 + **/ +public class MathUtils { + private static final int SCALE_2 = 2; + private static final int SCALE_4 = 4; + private static final BigDecimal PERCENT = new BigDecimal(100); + + /** + * add two value and format to 2 decimal places + * + * @param value1 value1 + * @param value2 value2 + * @return String + */ + public static String add(String value1, String value2) { + return new BigDecimal(value1).add(new BigDecimal(value2)).setScale(SCALE_2, RoundingMode.HALF_UP).toString(); + } + + /** + * divide two value and format to 4 decimal places + * + * @param s s + * @param s1 s1 + * @return String + */ + public static String divide(String s, String s1) { + return divide(s, s1, SCALE_4).toString(); + } + + /** + * format to 2 decimal places + * + * @param s s + * @return String + */ + public static String formatScaleTwo(String s) { + return new BigDecimal(s).setScale(SCALE_2, RoundingMode.HALF_UP).toString(); + } + + /** + *
+     * calculate the percentage of the dividend and divisor, and format to 2 decimal places
+     * e.g. 1/10 = 10.00%
+     * e.g. 1/3 = 33.33%
+     * 
+ * + * @param dividend dividend + * @param divisorVal divisorVal + * @return String + */ + public static String percent(String dividend, String divisorVal) { + return divide(dividend, divisorVal, SCALE_4).multiply(PERCENT) + .setScale(SCALE_2, RoundingMode.HALF_UP) + .toString(); + } + + private static BigDecimal divide(String s, String s1, int scale) { + BigDecimal divisor = new BigDecimal(s1); + if (divisor.compareTo(BigDecimal.ZERO) == 0) { + return BigDecimal.ZERO; + } + return new BigDecimal(s).divide(divisor, scale, RoundingMode.HALF_UP); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/MessageUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/MessageUtils.java new file mode 100644 index 000000000..62cef7b21 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/MessageUtils.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MessageUtils.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/MessageUtils.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils; + +import org.opengauss.admin.common.utils.spring.SpringUtils; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; + +/** + * I18n Message Tool + * + * @author xielibo + */ +public class MessageUtils { + /** + * According to the message key and parameters, get the message and delegate to spring messageSource + * + * @param code code + * @param args args + * @return result + */ + public static String message(String code, Object... args) { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/OpsAssert.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/OpsAssert.java new file mode 100644 index 000000000..7b28d3c6f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/OpsAssert.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsAssert.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/OpsAssert.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.utils; + +import org.opengauss.admin.common.exception.ops.OpsException; + +import java.util.Objects; + +/** + * OpsAssert + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +public class OpsAssert { + /** + * Assert a boolean expression, throwing an {@code OpsException} + * if the expression evaluates to {@code false}. + *
Assert.isTrue(i > 0, "The value must be greater than zero");
+ * + * @param expression a boolean expression + * @param message the exception message to use if the assertion fails + * @throws OpsException if {@code expression} is {@code false} + */ + public static void isTrue(boolean expression, String message) { + if (!expression) { + throw new OpsException(message); + } + } + + /** + * Assert a object non-null, throwing an {@code OpsException} + * if object is null ,then throwing an {@code OpsException} + * + * @param object a Object + * @param message the exception message to use if the assertion fails + * @throws OpsException if {@code expression} is {@code false} + */ + public static void nonNull(Object object, String message) { + if (Objects.isNull(object)) { + throw new OpsException(message); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/PermissionUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/PermissionUtils.java new file mode 100644 index 000000000..271cf28b9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/PermissionUtils.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.utils; + +/** + * Description: permission judge tools class + * + * @version 1.0 + * @date: 2023/9/13 + * @since JDK 11 + */ +public class PermissionUtils { + /** + * judge user had root permission + * + * @param userName userName + * @return boolean + **/ + public static boolean hasRootPermission(String userName) { + return "root".equals(userName); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/RandomCharGenerator.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/RandomCharGenerator.java new file mode 100644 index 000000000..03c87952f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/RandomCharGenerator.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +package org.opengauss.admin.common.utils; + +import java.security.SecureRandom; + +/** + * RandomCharGenerator + * + * @author: wangchao + * @Date: 2025/9/25 10:07 + * @since 7.0.0-RC2 + **/ +public class RandomCharGenerator { + private static final int LENGTH = 128; + private static final SecureRandom RANDOM = new SecureRandom(); + private static final char[] CHAR_SET = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + }; + + /** + * generator base random string ,length=128 + * + * @return random string + */ + public static String generateRandomBased128String() { + byte[] bytes = new byte[LENGTH]; + RANDOM.nextBytes(bytes); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < LENGTH; i++) { + sb.append(CHAR_SET[Math.abs(bytes[i]) % CHAR_SET.length]); + } + return sb.toString(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/RsaUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/RsaUtils.java new file mode 100644 index 000000000..9ca0bc73b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/RsaUtils.java @@ -0,0 +1,125 @@ +/** + * Copyright ruoyi. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless 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. + */ + +package org.opengauss.admin.common.utils; + +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.CryptoException; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.codec.binary.Base64; +import org.opengauss.admin.common.exception.SecureException; + +import java.security.*; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Description: RSA encrypt and decode + * + * @author xielibo + * @version 1.0 + * @date: 2021/9/28 11:40 PM + * @since JDK 1.8 + */ +@Slf4j +public class RsaUtils { + private static final AtomicReference PUBLIC_KEY_CACHE = new AtomicReference<>(); + private static final AtomicReference PRIVATE_KEY_CACHE = new AtomicReference<>(); + + static { + initRsaKeyPair(); + } + + private static void initRsaKeyPair() { + try { + KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA"); + gen.initialize(4096); + KeyPair keyPair = gen.generateKeyPair(); + PUBLIC_KEY_CACHE.set(Base64.encodeBase64String(keyPair.getPublic().getEncoded())); + PRIVATE_KEY_CACHE.set(Base64.encodeBase64String(keyPair.getPrivate().getEncoded())); + log.info("RSA key pair generation success"); + } catch (NoSuchAlgorithmException e) { + log.error("RSA key pair generation failed", e); + throw new SecureException("Critical failure: RSA key generation failed: " + e.getMessage()); + } + } + + /** + * get public key + * + * @return public key + */ + public static String publicKey() { + if (StrUtil.isEmpty(PUBLIC_KEY_CACHE.get())) { + initRsaKeyPair(); + } + return PUBLIC_KEY_CACHE.get(); + } + + /** + * encrypt with default public key + * + * @param plainText plain text + * @return cipher text + */ + public static String encrypt(String plainText) { + return encrypt(plainText, PUBLIC_KEY_CACHE.get()); + } + + /** + * encrypt with public key + * + * @param plainText plain text + * @param publicKey public key + * @return cipher text + */ + public static String encrypt(String plainText, String publicKey) { + RSA rsa = new RSA(null, publicKey); + byte[] encrypt = rsa.encrypt(StrUtil.bytes(plainText, CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey); + return Base64.encodeBase64String(encrypt); + } + + /** + * decrypt with default private key + * + * @param cipherText cipher text + * @return plain text + */ + public static String decrypt(String cipherText) { + return decrypt(cipherText, PRIVATE_KEY_CACHE.get()); + } + + /** + * decrypt with private key + * + * @param cipherText cipher text + * @param privateKey private key + * @return plain text + */ + public static String decrypt(String cipherText, String privateKey) { + try { + RSA rsa = new RSA(privateKey, null); + byte[] decrypt = rsa.decrypt(Base64.decodeBase64(cipherText), KeyType.PrivateKey); + return StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8); + } catch (CryptoException e) { + log.error("RSA decryption failed for cipherText", e); + throw new SecurityException("Decryption failed. Possible tampering detected", e); + } + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/SecurityUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/SecurityUtils.java new file mode 100644 index 000000000..97292188f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/SecurityUtils.java @@ -0,0 +1,80 @@ +/** + Copyright yangdi. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.common.utils; + +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.common.exception.CustomException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +/** + * Security Tool + * + * @author xielibo + */ +public class SecurityUtils { + /** + * getUsername + **/ + public static String getUsername() { + try { + return getLoginUser().getUsername(); + } catch (Exception e) { + throw new CustomException("Get Account Error", ResponseCode.UNAUTHORIZED.code()); + } + } + + /** + * getLoginUser + **/ + public static LoginUser getLoginUser() { + try { + return (LoginUser) getAuthentication().getPrincipal(); + } catch (Exception e) { + throw new CustomException("Get Account Error", ResponseCode.UNAUTHORIZED.code()); + } + } + + /** + * Get Authentication + */ + public static Authentication getAuthentication() { + return SecurityContextHolder.getContext().getAuthentication(); + } + + /** + * Generate BCryptPasswordEncoder Password + * + * @param password password + */ + public static String encryptPassword(String password) { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * Check if the passwords are the same + * + * @param rawPassword rawPassword + * @param encodedPassword encodedPassword + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ServletUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ServletUtils.java new file mode 100644 index 000000000..a4270fee9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ServletUtils.java @@ -0,0 +1,113 @@ +/** + Copyright ruoyi. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.common.utils; + +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.text.Convert; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import java.io.IOException; + + +/** + * Servlet Tool + * + * @author xielibo + */ +@Slf4j +public class ServletUtils { + + /** + * Get the String parameter + */ + public static String getParameter(String name) { + return getRequest().getParameter(name); + } + + + /** + * Get the Integer parameter + */ + public static Integer getParameterToInt(String name) { + return Convert.toInt(getRequest().getParameter(name),null); + } + + + /** + * Get the Boolean parameter + */ + public static Boolean getParameterToBool(String name) { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * Get the Boolean parameter + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * get Request + */ + public static HttpServletRequest getRequest() { + return getRequestAttributes().getRequest(); + } + + /** + * get Response + */ + public static HttpServletResponse getResponse() { + return getRequestAttributes().getResponse(); + } + + /** + * get Session + */ + public static HttpSession getSession() { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * Render the string to the client + * + * @param response response + * @param string data + * @return null + */ + public static String renderString(HttpServletResponse response, String string) { + try { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } catch (IOException e) { + log.error("error, message: {}", e.getMessage()); + } + return null; + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/StringUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/StringUtils.java new file mode 100644 index 000000000..a31b6fdbf --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/StringUtils.java @@ -0,0 +1,351 @@ +/** + Copyright ewem. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.common.utils; + +import org.springframework.util.AntPathMatcher; + +import java.util.*; + +/** + * string tools + * + * @author xielibo + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils { + private static final String NULLSTR = ""; + + private static final char SEPARATOR = '_'; + + /** + * get parameter is not null + * + */ + public static T nvl(T value, T defaultValue) { + return value != null ? value : defaultValue; + } + + /** + * * Determine whether a Collection is empty, including List, Set, Queue + * + */ + public static boolean isEmpty(Collection coll) { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * Determine whether a Collection is not empty, including List, Set, Queue + * + */ + public static boolean isNotEmpty(Collection coll) { + return !isEmpty(coll); + } + + /** + * * Check if an array of objects is empty + * + */ + public static boolean isEmpty(Object[] objects) { + return isNull(objects) || (objects.length == 0); + } + + /** + * * Check if an array of objects is empty + * + */ + public static boolean isNotEmpty(Object[] objects) { + return !isEmpty(objects); + } + + /** + * * Determine whether a Map is empty + * + */ + public static boolean isEmpty(Map map) { + return isNull(map) || map.isEmpty(); + } + + /** + * * Determine whether a Map is empty + * + */ + public static boolean isNotEmpty(Map map) { + return !isEmpty(map); + } + + /** + * * Check if a string is empty + * + */ + public static boolean isEmpty(String str) { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * Check if a string is empty + * + */ + public static boolean isNotEmpty(String str) { + return !isEmpty(str); + } + + /** + * * Check if an object is empty + * + */ + public static boolean isNull(Object object) { + return object == null; + } + + /** + * * Check if an object is empty + * + */ + public static boolean isNotNull(Object object) { + return !isNull(object); + } + + /** + * * Determine whether an object is an array type (Java basic type array) + * + */ + public static boolean isArray(Object object) { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * trim + */ + public static String trim(String str) { + return (str == null ? "" : str.trim()); + } + + /** + * substring + * + */ + public static String substring(final String str, int start) { + if (str == null) { + return NULLSTR; + } + + if (start < 0) { + start = str.length() + start; + } + + if (start < 0) { + start = 0; + } + if (start > str.length()) { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * substring + * + */ + public static String substring(final String str, int start, int end) { + if (str == null) { + return NULLSTR; + } + + if (end < 0) { + end = str.length() + end; + } + if (start < 0) { + start = str.length() + start; + } + + if (end > str.length()) { + end = str.length(); + } + + if (start > end) { + return NULLSTR; + } + + if (start < 0) { + start = 0; + } + if (end < 0) { + end = 0; + } + + return str.substring(start, end); + } + + + /** + * string to list + * + */ + public static final List str2List(String str, String sep, boolean filterBlank, boolean trim) { + List list = new ArrayList(); + if (StringUtils.isEmpty(str)) { + return list; + } + + if (filterBlank && StringUtils.isBlank(str)) { + return list; + } + String[] split = str.split(sep); + for (String string : split) { + if (filterBlank && StringUtils.isBlank(string)) { + continue; + } + if (trim) { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + + /** + * toUnderScoreCase + */ + public static String toUnderScoreCase(String str) { + if (str == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + boolean preCharIsUpperCase = true; + boolean curreCharIsUpperCase = true; + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (i > 0) { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } else { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) { + sb.append(SEPARATOR); + } else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * inStringIgnoreCase + */ + public static boolean inStringIgnoreCase(String str, String... strs) { + if (str != null && strs != null) { + for (String s : strs) { + if (str.equalsIgnoreCase(trim(s))) { + return true; + } + } + } + return false; + } + + /** + * Convert underscore-capitalized strings to camel case. Returns an empty string if the underscore-capitalized string before conversion is empty. For example: HELLO_WORLD->HelloWorld + * + */ + public static String convertToCamelCase(String name) { + StringBuilder result = new StringBuilder(); + if (name == null || name.isEmpty()) { + return ""; + } else if (!name.contains("_")) { + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + String[] camels = name.split("_"); + for (String camel : camels) { + if (camel.isEmpty()) { + continue; + } + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * CamelCase example:user_name->userName + */ + public static String toCamelCase(String s) { + if (s == null) { + return null; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + + if (c == SEPARATOR) { + upperCase = true; + } else if (upperCase) { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } else { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * Finds whether the specified string matches any string in the specified string list + * + */ + public static boolean matches(String str, List strs) { + if (isEmpty(str) || isEmpty(strs)) { + return false; + } + for (String pattern : strs) { + if (isMatch(pattern, str)) { + return true; + } + } + return false; + } + + /** + * Determine whether the url matches the rule configuration: + * ? represents a single character; + * Indicates any character string within a layer path, and cannot cross layers; + * Indicates any layer path; + * + * @return + */ + public static boolean isMatch(String pattern, String url) { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast(Object obj) { + return (T) obj; + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/Threads.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/Threads.java new file mode 100644 index 000000000..af9f25506 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/Threads.java @@ -0,0 +1,89 @@ +/** + Copyright ruoyi. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.common.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.*; + + +/** + * Thread Tool. + * + * @author xielibo + */ +public class Threads { + private static final Logger logger = LoggerFactory.getLogger(Threads.class); + + /** + * sleep + */ + public static void sleep(long milliseconds) { + try { + Thread.sleep(milliseconds); + } catch (InterruptedException e) { + return; + } + } + + /** + * Stop thread pool + * Use shutdown first, stop accepting new tasks and try to complete all existing tasks. + * If it times out, call shutdownNow, cancel the pending tasks in the workQueue, and interrupt all blocking functions. + * Force quit if still timeout. + * In addition, the thread itself is interrupted by the call during shutdown. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) { + if (pool != null && !pool.isShutdown()) { + pool.shutdown(); + try { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + logger.info("Pool did not terminate"); + } + } + } catch (InterruptedException ie) { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * Print thread exception information + */ + public static void printException(Runnable r, Throwable t) { + if (t == null && r instanceof Future) { + try { + Future future = (Future) r; + if (future.isDone()) { + future.get(); + } + } catch (CancellationException ce) { + t = ce; + } catch (ExecutionException ee) { + t = ee.getCause(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + if (t != null) { + logger.error(t.getMessage(), t); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/YamlUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/YamlUtils.java new file mode 100644 index 000000000..003f6f2d5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/YamlUtils.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.common.utils; + +import cn.hutool.core.collection.CollUtil; + +import org.opengauss.admin.common.exception.ops.OpsException; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +/** + * YamlUtils + * + * @author: wangchao + * @Date: 2025/4/26 17:32 + * @Description: YamlUtils + * @since 7.0.0-RC2 + **/ +public class YamlUtils { + /** + * read Yaml file + * + * @param filePath yaml file path + * @return yaml file content + */ + public static Map readYaml(String filePath) { + Yaml yaml = new Yaml(); + try (InputStream in = Files.newInputStream(Paths.get(filePath))) { + return yaml.load(in); + } catch (IOException e) { + throw new OpsException("Failed to read YAML file " + filePath + " failed : " + e.getMessage()); + } + } + + /** + * write Yaml file + * + * @param filePath yaml file path + * @param data yaml file content + */ + public static void writeYaml(String filePath, Map data) { + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + options.setPrettyFlow(true); + options.setIndent(2); + Yaml yaml = new Yaml(options); + try { + String yamlContent = yaml.dump(data); + Files.writeString(Paths.get(filePath), yamlContent); + } catch (IOException e) { + throw new OpsException("Failed to write YAML file" + e.getMessage()); + } + } + + /** + * dump Yaml content + * + * @param data yaml content + * @return yaml content + */ + public static String dumpYaml(Map data) { + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + options.setPrettyFlow(true); + options.setIndent(2); + Yaml yaml = new Yaml(options); + if (CollUtil.isEmpty(data)) { + return yaml.dumpAsMap(new HashMap<>()); + } + return yaml.dump(data); + } + + /** + * update properties + * + * @param properties yaml content + * @param key key + * @param value value + */ + public static void updateProperties(Map properties, String key, Object value) { + String[] split = key.split("\\."); + updateNestedMap(properties, split, 0, value); + } + + /** + * get properties + * + * @param properties yaml content + * @param key key + * @return value + */ + public static Object getProperties(Map properties, String key) { + String[] split = key.split("\\."); + return getNestedMap(properties, split, 0); + } + + private static Object getNestedMap(Map currentMap, String[] keys, int index) { + String currentKey = keys[index]; + if (index == keys.length - 1) { + return currentMap.get(currentKey); + } + Object nextLevel = currentMap.get(currentKey); + if (nextLevel instanceof Map) { + return getNestedMap((Map) nextLevel, keys, index + 1); + } else { + return nextLevel; + } + } + + private static void updateNestedMap(Map currentMap, String[] keys, int index, Object value) { + String currentKey = keys[index]; + if (index == keys.length - 1) { + currentMap.put(currentKey, value); + return; + } + if (currentMap.get(currentKey) instanceof Map nextLevel) { + updateNestedMap(nextLevel, keys, index + 1, value); + } else { + Map newMap = new HashMap<>(); + currentMap.put(currentKey, newMap); + updateNestedMap(newMap, keys, index + 1, value); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/excel/ImportAsynInfoUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/excel/ImportAsynInfoUtils.java new file mode 100644 index 000000000..b3db12751 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/excel/ImportAsynInfoUtils.java @@ -0,0 +1,83 @@ +package org.opengauss.admin.common.utils.excel; + +import org.opengauss.admin.common.core.domain.model.ops.ImportAsynInfo; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * Progress Information Utility Class. + * + * @author zzh + * @version 1.0 + * @data 2024/6/25 10:35 + */ +public class ImportAsynInfoUtils { + /** + * Map for storing progress information. + */ + public static Map allAsynInfo = new HashMap(); + + /** + * Create a UUID and store the progress information object. + * + * @param asynInfo + * @return + */ + public static String createUUID(ImportAsynInfo asynInfo) { + String uuid = UUID.randomUUID().toString().replace("-", ""); + allAsynInfo.put(uuid, asynInfo); + return uuid; + } + + /** + * Retrieve progress information by UUID. + * + * @param uuid + * @return + */ + public static ImportAsynInfo getAsynInfo(String uuid) { + return allAsynInfo.get(uuid); + } + + /** + * Delete the progress information corresponding to the UUID. + * + * @param uuid + * @return + */ + public static void deleteAsynInfo(String uuid) { + allAsynInfo.remove(uuid); + } + + /** + * Increment the number of processed data entries by one for the progress associated with the UUID. + * + * @param uuid + */ + public static synchronized void addDoneSum(String uuid) { + ImportAsynInfo asynInfo = getAsynInfo(uuid); + asynInfo.setDoneSum(asynInfo.getDoneSum() + 1); + } + + /** + * Increment the number of failed data entries by one for the progress associated with the UUID. + * + * @param uuid + */ + public static synchronized void addErrorSum(String uuid) { + ImportAsynInfo asynInfo = getAsynInfo(uuid); + asynInfo.setErrorSum(asynInfo.getErrorSum() + 1); + } + + /** + * Increment the number of successful data entries by one for the progress associated with the UUID. + * + * @param uuid + */ + public static synchronized void addSuccessSum(String uuid) { + ImportAsynInfo asynInfo = getAsynInfo(uuid); + asynInfo.setSuccessSum(asynInfo.getSuccessSum() + 1); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/excel/TagsConverter.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/excel/TagsConverter.java new file mode 100644 index 000000000..93fce9c2b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/excel/TagsConverter.java @@ -0,0 +1,44 @@ +package org.opengauss.admin.common.utils.excel; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.WriteConverterContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +import java.util.Arrays; +import java.util.List; + +/** + * Excel to Java Object Conversion Label Converter Class. + * + * @author zzh + * @version 1.0 + * @data 2024/6/25 10:35 + */ +public class TagsConverter implements Converter> { + @Override + public Class supportJavaTypeKey() { + return List.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public List convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { + List resultList = Arrays.asList(cellData.getStringValue().split(",")); + return resultList; + } + + @Override + public WriteCellData convertToExcelData(WriteConverterContext> context) throws Exception { + List value = context.getValue(); + String str = String.join(",", value); + return new WriteCellData<>(str); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/file/FileTypeUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/file/FileTypeUtils.java new file mode 100644 index 000000000..504137eaf --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/file/FileTypeUtils.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * FileTypeUtils.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/file/FileTypeUtils.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.file; + +import org.apache.commons.lang3.StringUtils; + +import java.io.File; + +/** + * File Type Tool + * + * @author xielibo + */ +public class FileTypeUtils { + /** + * Get File Type + * @param file file + * @return + */ + public static String getFileType(File file) { + if (null == file) { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * Get File Type + * + */ + public static String getFileType(String fileName) { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * Get File Extend Name + * + */ + public static String getFileExtendName(byte[] photoByte) { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) { + strFileExtendName = "GIF"; + } else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) { + strFileExtendName = "JPG"; + } else if ((photoByte[0] == 66) && (photoByte[1] == 77)) { + strFileExtendName = "BMP"; + } else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/file/FileUploadUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/file/FileUploadUtils.java new file mode 100644 index 000000000..206430571 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/file/FileUploadUtils.java @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * FileUploadUtils.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/file/FileUploadUtils.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.file; + +import cn.hutool.core.io.FileUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.io.IOUtils; +import org.opengauss.admin.common.config.SystemConfig; +import org.opengauss.admin.common.constant.Constants; +import org.opengauss.admin.common.exception.file.FileNameLengthLimitExceededException; +import org.opengauss.admin.common.exception.file.FileSizeLimitExceededException; +import org.opengauss.admin.common.exception.file.InvalidExtensionException; +import org.opengauss.admin.common.utils.DateUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.common.utils.uuid.IdUtils; +import org.apache.commons.io.FilenameUtils; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +/** + * file upload tool + * + * @author xielibo + */ +@Slf4j +public class FileUploadUtils { + /** + * default size 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; + + /** + * default filename length 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * default baseDir + */ + private static String defaultBaseDir = SystemConfig.getStoragePath(); + + public static void setDefaultBaseDir(String defaultBaseDir) { + FileUploadUtils.defaultBaseDir = defaultBaseDir; + } + + public static String getDefaultBaseDir() { + return defaultBaseDir; + } + + /** + * default upload + * + * @param file file + * @return filename + * @throws Exception + */ + public static final String upload(MultipartFile file) throws IOException { + try { + return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } catch (Exception e) { + throw new IOException(e.getMessage(), e); + } + } + + /** + * upload + * + * @param baseDir file root directory + * @param file file + * @return filename + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file) throws IOException { + try { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } catch (Exception e) { + throw new IOException(e.getMessage(), e); + } + } + + /** + * upload + * + * @param baseDir file root directory + * @param file file + * @param allowedExtension file extension + * @return filename + * @throws FileSizeLimitExceededException file size limit exception + * @throws FileNameLengthLimitExceededException file name length limit exception + * @throws IOException Io exception + * @throws InvalidExtensionException file invalid exception + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException { + int fileNamelength = file.getOriginalFilename().length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + File desc = getAbsoluteFile(baseDir, fileName); + file.transferTo(desc); + String pathFileName = getPathFileName(baseDir, fileName); + return pathFileName; + } + + /** + * upload + * @param baseDir file root directory + * @param fileName fileName + * @param allowedExtension file extension + * @return filename + * @throws FileSizeLimitExceededException file size limit exception + * @throws FileNameLengthLimitExceededException file name length limit exception + * @throws IOException Io exception + * @throws InvalidExtensionException file invalid exception + */ + public static final String upload(String baseDir, String fileName, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException { + int fileNamelength = file.getOriginalFilename().length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + assertAllowed(file, allowedExtension); + File desc = getAbsoluteFile(baseDir, fileName); + file.transferTo(desc); + String pathFileName = getPathFileName(baseDir, fileName); + return pathFileName; + } + + /** + * generate filename + */ + public static final String extractFilename(MultipartFile file) { + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + fileName = DateUtils.datePath() + "/" + IdUtils.fastUuid() + "." + extension; + return fileName; + } + + public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) { + if (!desc.getParentFile().exists()) { + desc.getParentFile().mkdirs(); + } + } + return desc; + } + + public static final String getPathFileName(String uploadDir, String fileName) { + int dirLastIndex = SystemConfig.getStoragePath().length() + 1; + String currentDir = StringUtils.substring(uploadDir, dirLastIndex); + String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; + return pathFileName; + } + + /** + * file size valid + * + * @param file file + * @param allowedExtension file extension + * @return + * @throws FileNameLengthLimitExceededException file name length limit exception + * @throws InvalidExtensionException file invalid exception + */ + public static final void assertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException { + long size = file.getSize(); + if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE) { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } else { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + + } + + /** + * Determine whether the MIME type is an allowed MIME type + * + * @param extension + * @param allowedExtension + * @return + */ + public static final boolean isAllowedExtension(String extension, String[] allowedExtension) { + for (String str : allowedExtension) { + if (str.equalsIgnoreCase(extension)) { + return true; + } + } + return false; + } + + /** + * Get the suffix of the file name + * + * @param file file + * @return suffix + */ + public static final String getExtension(MultipartFile file) { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) { + extension = MimeTypeUtils.getExtension(file.getContentType()); + } + return extension; + } + + /** + * delete file + * + * @return + */ + public static final void deleteByPath(String path) { + String realPath = path.replace(Constants.RESOURCE_PREFIX + "/", "/"); + String absolutePath = SystemConfig.getStoragePath() + "/" + realPath; + FileUtil.del(absolutePath); + } + + /** + * save the svg file + * @param svgContent + */ + public static final String writeMenuSvgIcon(String svgContent) { + final String pathCombainOpt = "/"; + String baseDir = SystemConfig.getIconsPath() + pathCombainOpt + DateUtils.datePath(); + File dir = new File(baseDir); + if (!dir.exists()) { + dir.mkdirs(); + } + String fileName = IdUtils.fastUuid() + ".svg"; + File file = new File(baseDir + pathCombainOpt + fileName); + try { + if (!file.exists()) { + file.createNewFile(); + } + FileUtil.writeBytes(svgContent.getBytes(StandardCharsets.UTF_8), file.getPath()); + return Constants.RESOURCE_PREFIX + "/icons" + pathCombainOpt + DateUtils.datePath() + pathCombainOpt + fileName; + } catch (IOException e) { + log.error("write menu svg error, message: {}", e.getMessage()); + } + return ""; + } + + public static MultipartFile inputStreamToMultipartFile(InputStream is, String fieldName, String fileName) throws IOException { + final DiskFileItemFactory fac = new DiskFileItemFactory(); + FileItem fileItem = fac.createItem(fieldName, MediaType.MULTIPART_FORM_DATA_VALUE, true, fileName); + final OutputStream fileItemOutStream; + try { + fileItemOutStream = fileItem.getOutputStream(); + } catch (IOException e) { + String errMsg = "get FileItem output stream failed: {}" + e.getMessage(); + log.error(errMsg); + throw new IOException(errMsg); + } + + try { + IOUtils.copy(is, fileItemOutStream, 20480); + is.close(); + } catch (IOException e) { + String errMsg = "write FileItem failed: {}" + e.getMessage(); + log.error(errMsg); + throw new IOException(errMsg); + } + byte[] bytes = fileItem.get(); + String contentType = fileItem.getContentType(); + return new MockMultipartFile(fieldName, fileName, contentType, bytes); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/file/MimeTypeUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/file/MimeTypeUtils.java new file mode 100644 index 000000000..27658b199 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,69 @@ +/** + Copyright ruoyi. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ + +package org.opengauss.admin.common.utils.file; + + +/** + * Mime Type Tool + * + * @author xielibo + */ +public class MimeTypeUtils { + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"}; + + public static final String[] FLASH_EXTENSION = {"swf", "flv"}; + + public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb"}; + + public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"}; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + "bmp", "gif", "jpg", "jpeg", "png", + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + "rar", "zip", "gz", "bz2", + "mp3", + "mp4", "avi", "rmvb", + "pdf"}; + + public static String getExtension(String prefix) { + switch (prefix) { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/html/EscapeUtil.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/html/EscapeUtil.java new file mode 100644 index 000000000..3c4e78dee --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/html/EscapeUtil.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * EscapeUtil.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/html/EscapeUtil.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.html; + +import org.opengauss.admin.common.utils.StringUtils; + +/** + * EscapeUtil + * + * @author xielibo + */ +public class EscapeUtil { + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static { + for (int i = 0; i < 64; i++) { + TEXT[i] = new char[]{(char) i}; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); + TEXT['"'] = """.toCharArray(); + TEXT['&'] = "&".toCharArray(); + TEXT['<'] = "<".toCharArray(); + TEXT['>'] = ">".toCharArray(); + } + + /** + * Escape HTML characters in text to safe characters + * + */ + public static String escape(String text) { + return encode(text); + } + + /** + * Restore escaped HTML special characters + * + */ + public static String unescape(String content) { + return decode(content); + } + + /** + * Clear all HTML tags, but do not delete the content inside the tags + * + */ + public static String clean(String content) { + return new HTMLFilter().filter(content); + } + + /** + * Escape Encode + * + */ + private static String encode(String text) { + int len; + if ((text == null) || ((len = text.length()) == 0)) { + return StringUtils.EMPTY; + } + StringBuilder buffer = new StringBuilder(len + (len >> 2)); + char c; + for (int i = 0; i < len; i++) { + c = text.charAt(i); + if (c < 64) { + buffer.append(TEXT[c]); + } else { + buffer.append(c); + } + } + return buffer.toString(); + } + + /** + * Escape Decode + * + */ + public static String decode(String content) { + if (StringUtils.isEmpty(content)) { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) { + if (content.charAt(pos + 1) == 'u') { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } else { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } else { + if (pos == -1) { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } else { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/html/HTMLFilter.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/html/HTMLFilter.java new file mode 100644 index 000000000..49b1cea58 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/html/HTMLFilter.java @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HTMLFilter.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/html/HTMLFilter.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.html; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML filter, used to remove hidden dangers of XSS vulnerabilities. + * + * @author xielibo + */ +public final class HTMLFilter { + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() { + vAllowed = new HashMap<>(); + + final ArrayList atts = new ArrayList<>(); + atts.add("href"); + atts.add("target"); + vAllowed.put("a", atts); + + final ArrayList imgAtts = new ArrayList<>(); + imgAtts.add("src"); + imgAtts.add("width"); + imgAtts.add("height"); + imgAtts.add("alt"); + vAllowed.put("img", imgAtts); + + final ArrayList noAtts = new ArrayList<>(); + vAllowed.put("b", noAtts); + vAllowed.put("strong", noAtts); + vAllowed.put("i", noAtts); + vAllowed.put("em", noAtts); + + vSelfClosingTags = new String[]{"img"}; + vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"}; + vDisallowed = new String[]{}; + vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp. + vProtocolAtts = new String[]{"src", "href"}; + vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"}; + vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"}; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHtml(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() { + return alwaysMakeTags; + } + + public boolean isStripComments() { + return stripComment; + } + + private String escapeComments(final String s) { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHtml(String s) { + if (alwaysMakeTags) { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } else { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) { + for (int ii = 0; ii < vTagCounts.get(key); ii++) { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) { + String result = s; + for (String tag : vRemoveBlanks) { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regexPattern, final String replacement, final String s) { + Matcher m = regexPattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) { + if (false == inArray(name, vSelfClosingTags)) { + if (vTagCounts.containsKey(name)) { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + if (allowedAttribute(name, paramName)) { + if (inArray(paramName, vProtocolAtts)) { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\""); + } + } + + if (inArray(name, vSelfClosingTags)) { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) { + ending = ""; + } + + if (ending == null || ending.length() < 1) { + if (vTagCounts.containsKey(name)) { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } else { + vTagCounts.put(name, 1); + } + } else { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } else { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) { + if (encodeQuotes) { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } else { + return s; + } + } + + private String checkEntity(final String preamble, final String term) { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) { + for (String item : array) { + if (item != null && item.equals(s)) { + return true; + } + } + return false; + } + + private boolean allowed(final String name) { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/http/HttpHelper.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/http/HttpHelper.java new file mode 100644 index 000000000..a54a8bb8f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/http/HttpHelper.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HttpHelper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/http/HttpHelper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.http; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jakarta.servlet.ServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; + +/** + * Http Tool Helper + * + * @author xielibo + */ +@Slf4j +public class HttpHelper { + private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); + + public static String getBodyString(ServletRequest request) { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + InputStreamReader isr = null; + try (InputStream inputStream = request.getInputStream()) { + isr = new InputStreamReader(inputStream, Charset.forName("UTF-8")); + reader = new BufferedReader(isr); + String line = ""; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + } catch (IOException e) { + LOGGER.warn("getBodyString Error"); + } finally { + if (isr != null) { + try { + isr.close(); + } catch (IOException e) { + log.error("close input stream failed: " + e.getMessage()); + } + } + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + LOGGER.error(ExceptionUtils.getMessage(e)); + } + } + } + return sb.toString(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/http/HttpUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/http/HttpUtils.java new file mode 100644 index 000000000..cad0813dc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/http/HttpUtils.java @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HttpUtils.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/http/HttpUtils.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.http; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import lombok.Data; +import org.opengauss.admin.common.constant.Constants; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; + +import javax.net.ssl.*; +import java.io.*; +import java.net.*; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Http Tool + * + * @author xielibo + */ +public class HttpUtils { + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + private static Proxy httpProxy = null; + + /** + * http proxy + * + * @param proxy proxy + */ + public static void setProxy(Proxy proxy) { + httpProxy = proxy; + } + + /** + * sendGet + * + * @param url URL + * @param param param,like name1=value1&name2=value2. + */ + public static String sendGet(String url, String param) { + return sendGet(url, param, Constants.UTF8); + } + + /** + * sendGet + * + * @param url URL + * @param param param,like name1=value1&name2=value2. + * @param contentType contentType + */ + public static String sendGet(String url, String param, String contentType) { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + InputStreamReader isr = null; + try { + String urlNameString = url + "?" + param; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.setConnectTimeout(5000); + connection.connect(); + isr = new InputStreamReader(connection.getInputStream(), contentType); + in = new BufferedReader(isr); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + log.info("recv - {}", result); + } catch (ConnectException e) { + log.error("sendGet ConnectException, url=" + url + ",param=" + param, e); + } catch (SocketTimeoutException e) { + log.error("sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } catch (IOException e) { + log.error("sendGet IOException, url=" + url + ",param=" + param, e); + } catch (Exception e) { + log.error("sendGet Exception, url=" + url + ",param=" + param, e); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (Exception ex) { + log.error("close Exception, url=" + url + ",param=" + param, ex); + } + if (isr != null) { + try { + isr.close(); + } catch (IOException e) { + log.error("close input stream reader failed: " + e.getMessage()); + } + } + } + return result.toString(); + } + + /** + * sendPost + * + * @param url URL + * @param param param,like name1=value1&name2=value2. + */ + public static PostResponse sendPost(String url, String param) { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + PostResponse response = new PostResponse(); + InputStreamReader isr = null; + try { + log.info("sendPost - {}", url); + URL realUrl = new URL(url); + HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); + conn.setRequestProperty(HttpHeaders.ACCEPT, "*/*"); + conn.setRequestProperty(HttpHeaders.CONNECTION, "Keep-Alive"); + conn.setRequestProperty(HttpHeaders.USER_AGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty(HttpHeaders.ACCEPT_CHARSET, "utf-8"); + conn.setRequestProperty(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + conn.setRequestMethod(HttpMethod.POST.name()); + conn.setConnectTimeout(0); + conn.setReadTimeout(0); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + isr = new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8); + in = new BufferedReader(isr); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + response.setBody(result.toString()); + response.setHeaders(conn.getHeaderFields()); + log.info("recv - {}", result); + } catch (ConnectException e) { + log.error("sendPost ConnectException, url=" + url + ",param=" + param, e); + } catch (SocketTimeoutException e) { + log.error("sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } catch (IOException e) { + log.error("sendPost IOException, url=" + url + ",param=" + param, e); + } catch (Exception e) { + log.error("sendPost Exception, url=" + url + ",param=" + param, e); + } finally { + try { + if (out != null) { + out.close(); + } + if (in != null) { + in.close(); + } + if (isr != null) { + isr.close(); + } + } catch (IOException ex) { + log.error("close Exception, url=" + url + ",param=" + param, ex); + } + } + return response; + } + + public static String sendSslPost(String url, String param) { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + InputStreamReader isr = null; + try { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String ret = ""; + while ((ret = br.readLine()) != null) { + if (ret != null && !"".equals(ret.trim())) { + result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8")); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } catch (ConnectException e) { + log.error("sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } catch (SocketTimeoutException e) { + log.error("sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } catch (IOException e) { + log.error("sendSSLPost IOException, url=" + url + ",param=" + param, e); + } catch (Exception e) { + log.error("sendSSLPost Exception, url=" + url + ",param=" + param, e); + } finally { + if (isr != null) { + try { + isr.close(); + } catch (IOException e) { + log.error("close input stream reader failed: " + e.getMessage()); + } + } + } + return result.toString(); + } + + /** + * check url can be connected + * + * @param urlAddr url address + * @return AjaxResult + */ + public static AjaxResult checkUrl(String urlAddr) { + try { + if (StrUtil.isEmpty(urlAddr)) { + return AjaxResult.error("The URL must not empty."); + } + URL url = new URL(urlAddr); + URLConnection urlConnection = openConnectionWithProxy(url); + if (urlConnection instanceof HttpsURLConnection) { + HttpsURLConnection connection = (HttpsURLConnection) urlConnection; + connection.setRequestMethod("GET"); + connection.connect(); + int responseCode = connection.getResponseCode(); + if (responseCode == 200) { + return AjaxResult.success("The URL is connected successfully."); + } else { + return AjaxResult.error("The URL is not connected."); + } + } else { + return AjaxResult.error("The URL is not https connected."); + } + } catch (IOException e) { + log.error("connected {} error ", urlAddr, e); + return AjaxResult.error("The URL is not connected : " + e.getMessage()); + } + } + + private static URLConnection openConnectionWithProxy(URL url) throws IOException { + if (Objects.isNull(httpProxy)) { + return url.openConnection(); + } else { + return url.openConnection(httpProxy); + } + } + + private static class TrustAnyTrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + } + + @Data + public static class PostResponse { + private String body; + private Map> headers; + + public String getHeader(String key) { + if (CollUtil.isEmpty(headers)) { + return ""; + } + List value = headers.get(key); + if (CollUtil.isEmpty(value)) { + return ""; + } + return value.get(0); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ip/AddressUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ip/AddressUtils.java new file mode 100644 index 000000000..637941c86 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ip/AddressUtils.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AddressUtils.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ip/AddressUtils.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.ip; + +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.common.constant.Constants; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.common.utils.http.HttpUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Address Tool + * + * @author xielibo + */ +public class AddressUtils { + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIp(String ip) { + String address = UNKNOWN; + + if (IpUtils.internalIp(ip)) { + return "Intranet IP"; + } + try { + String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); + if (StringUtils.isEmpty(rspStr)) { + log.error("Get geographic location exception {}", ip); + return UNKNOWN; + } + JSONObject obj = JSONObject.parseObject(rspStr); + String region = obj.getString("pro"); + String city = obj.getString("city"); + return String.format("%s %s", region, city); + } catch (Exception e) { + log.error("Get geographic location exception {}", ip); + } + return address; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ip/IpUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ip/IpUtils.java new file mode 100644 index 000000000..3e1926b4f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ip/IpUtils.java @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IpUtils.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ip/IpUtils.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.ip; + +import org.apache.commons.net.util.SubnetUtils; +import org.apache.commons.validator.routines.InetAddressValidator; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.common.utils.html.EscapeUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jakarta.servlet.http.HttpServletRequest; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** + * IP Tool + * + * @author xielibo + */ +public class IpUtils { + private static final String IPV4 = "ipv4"; + private static final String IPV6 = "ipv6"; + + private static final Logger log = LoggerFactory.getLogger(IpUtils.class); + + /** + * Format the IP string to ensure the correct format ipv4 or ipv6 used in ip:port. + * + * @param ip The IP character string + * @return The IP:port is formatted as [ip]:port for ipv6 and ip:port for ipv4 + */ + public static String formatIp(String ip) { + String ipType = getIpType(ip); + + if (ipType.isEmpty()) { + return ""; + } + + if (IPV4.equals(ipType)) { + return ip; + } else if (IPV6.equals(ipType)) { + return "[" + ip + "]"; + } else { + log.warn(ip + " is neither an IPv4 nor an IPv6 address."); + return ""; + } + } + + /** + * getIpType + * + * @param ip ip + * @return String + */ + public static String getIpType(String ip) { + try { + InetAddress inetAddress = InetAddress.getByName(ip); + if (inetAddress instanceof Inet4Address) { + return IPV4; + } else if (inetAddress instanceof Inet6Address) { + return IPV6; + } else { + log.warn(ip + " is neither an IPv4 nor an IPv6 address."); + } + } catch (UnknownHostException e) { + log.warn(ip + " is not a valid IP address."); + return ""; + } + return ""; + } + + public static String getIpAddr(HttpServletRequest request) { + if (request == null) { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : EscapeUtil.clean(ip); + } + + public static boolean internalIp(String ip) { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + private static boolean internalIp(byte[] addr) { + if (StringUtils.isNull(addr) || addr.length < 2) { + return false; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte sectionOne = 0x0A; + // 172.16.x.x/12 + final byte sectionTwo = (byte) 0xAC; + final byte sectionThree = (byte) 0x10; + final byte SectionFour = (byte) 0x1F; + // 192.168.x.x/16 + final byte sectionFive = (byte) 0xC0; + final byte sectionSix = (byte) 0xA8; + switch (b0) { + case sectionOne: + return true; + case sectionTwo: + if (b1 >= sectionThree && b1 <= SectionFour) { + return true; + } + case sectionFive: + switch (b1) { + case sectionSix: + return true; + default: + return false; + } + default: + return false; + } + } + + /** + * Convert IPv4 address to bytes + * + * @return byte + */ + public static byte[] textToNumericFormatV4(String text) { + if (text.length() == 0) { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try { + long l; + int i; + switch (elements.length) { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } catch (NumberFormatException e) { + return null; + } + return bytes; + } + + public static String getHostIp() { + try { + return InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + } + return "127.0.0.1"; + } + + public static String getHostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + } + return "Unknown"; + } + + public static boolean mayBeIPAddress(String hostIp) { + return InetAddressValidator.getInstance().isValid(hostIp); + } + + /** + *

+     * Get local IP address and mask (Ip,Mask).
+     * if the local IP address is invalid, return empty. multiple IPs are supported.
+     * "192.168.1.20", "255.255.255.0"
+     * "2001:db8:abcd:0012::2", "64"
+     * 
+ * + * @return Map + */ + public static Map getLocalIpAddressMap() { + Map ipMaskMap = new HashMap<>(); + try { + Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + NetworkInterface ni = interfaces.nextElement(); + if (ni.isLoopback() || !ni.isUp()) { + continue; + } + for (InterfaceAddress ia : ni.getInterfaceAddresses()) { + InetAddress addr = ia.getAddress(); + if (addr instanceof Inet4Address) { + int prefix = ia.getNetworkPrefixLength(); + String mask = calculateIPv4Mask(prefix); + ipMaskMap.put(addr.getHostAddress(), mask); + } + if (addr instanceof Inet6Address && !addr.isLinkLocalAddress()) { + ipMaskMap.put(addr.getHostAddress(), String.valueOf(ia.getNetworkPrefixLength())); + } + } + } + } catch (SocketException e) { + log.error("Failed to load DataKit Server Network IP", e); + } + return ipMaskMap; + } + + private static String calculateIPv4Mask(int prefix) { + int mask = 0xffffffff << (32 - prefix); + return String.format(Locale.getDefault(), "%d.%d.%d.%d", (mask >> 24) & 0xff, (mask >> 16) & 0xff, + (mask >> 8) & 0xff, mask & 0xff); + } + + /** + *
+     * check ip1 and ip2 is in the same subnet
+     * // IPv4 测试
+     * (isIpInSubnet("192.168.1.10", "192.168.1.20", "255.255.255.0")); // 同子网
+     * (isIpInSubnet("192.168.1.10", "192.168.2.20", "255.255.255.0")); // 不同子网
+     *
+     * // IPv6 测试
+     * (isIpInSubnet("2001:db8:abcd:0012::1", "2001:db8:abcd:0012::2", "64"));
+     * (isIpInSubnet("2001:db8:abcd:0012::1", "2001:db8:1234:5678::1", "64"));
+     * 
+ * + * @param ip1 ip1 eg. 192.168.1.10 + * @param ip2 ip2 eg. 192.168.1.20 + * @param mask mask eg. 255.255.255.0 + * @return true or false + * @throws UnknownHostException UnknownHostException + */ + public static boolean isIpInSubnet(String ip1, String ip2, String mask) throws UnknownHostException { + if (mask.contains(".")) { + SubnetUtils subnet = new SubnetUtils(ip1, mask); + return subnet.getInfo().isInRange(ip2); + } else { + int prefixLength = Integer.parseInt(mask); + return isSameSubnetV6(ip1, ip2, prefixLength); + } + } + + private static boolean isSameSubnetV6(String ip1, String ip2, int prefixLength) throws UnknownHostException { + if (Inet6Address.getByName(ip1) instanceof Inet6Address addr1 && Inet6Address.getByName( + ip2) instanceof Inet6Address addr2) { + byte[] a1 = addr1.getAddress(); + byte[] a2 = addr2.getAddress(); + int bytes = prefixLength / 8; + int bits = prefixLength % 8; + for (int i = 0; i < bytes; i++) { + if (a1[i] != a2[i]) { + return false; + } + } + if (bits > 0) { + int mask = (0xFF << (8 - bits)) & 0xFF; + int b1 = a1[bytes] & 0xFF; // 转换为无符号 + int b2 = a2[bytes] & 0xFF; + return (b1 & mask) == (b2 & mask); + } else { + return true; + } + } else { + return false; + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/JdbcUtil.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/JdbcUtil.java new file mode 100644 index 000000000..76287fa6b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/JdbcUtil.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JdbcUtil.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/JdbcUtil.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.ops; + +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcInfo; +import org.opengauss.admin.common.enums.ops.DbTypeEnum; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.springframework.util.ObjectUtils; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author lhf + * @date 2023/1/13 13:43 + **/ +@Slf4j +public class JdbcUtil { + private static Pattern p = Pattern.compile("jdbc:(?\\w+):.*((//)|@)(?.+):(?\\d+)"); + + public static JdbcInfo parseUrl(String url) { + if (StrUtil.isEmpty(url)) { + throw new OpsException("JDBC URL information does not exist"); + } + + JdbcInfo jdbcInfo = new JdbcInfo(); + + Matcher m = p.matcher(url); + if (m.find()) { + String dbType = m.group("dbType"); + if (StrUtil.isEmpty(dbType)) { + throw new OpsException("Error parsing JDBC URL, database type not found"); + } + jdbcInfo.setDbType(DbTypeEnum.typeOf(dbType)); + + String ip = m.group("ip"); + if (StrUtil.isEmpty(ip)) { + throw new OpsException("Error parsing JDBC URL, database ip not found"); + } + jdbcInfo.setIp(ip); + + String port = m.group("port"); + if (StrUtil.isEmpty(port)) { + throw new OpsException("Error parsing JDBC URL, database port not found"); + } + jdbcInfo.setPort(port); + } + + return jdbcInfo; + } + + public static Connection getConnection(String url, String username, String password) { + Connection connection = null; + try { + JdbcInfo jdbcInfo = parseUrl(url); + Class.forName(jdbcInfo.getDbType().getDriverClass()); + + connection = DriverManager.getConnection(url, username, password); + } catch (Exception e) { + log.error("jdbc failed to get connection", e); + throw new OpsException("jdbc failed to get connection"); + } + return connection; + } + + /** + * Select string value string. + * + * @param connection the connection + * @param selectSql the select sql + * @param key the key + * @return the string + * @throws SQLException the sql exception + */ + public static String selectStringValue(Connection connection, String selectSql, String key) throws SQLException { + String value = ""; + if (connection != null) { + try (Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(selectSql)) { + if (rs.next()) { + value = rs.getString(key); + } + } catch (SQLException e) { + throw e; + } + } + return value; + } + + /** + * judge database user is admin + * + * @param ip ip + * @param port port + * @param user user + * @param password password + * @return result boolean + */ + public static boolean judgeSystemAdmin(String ip, String port, String user, String password) { + boolean isAdmin = false; + Connection pgConnection = null; + try { + String opengaussUrl = "jdbc:opengauss://" + ip + ":" + port + "/postgres"; + pgConnection = JdbcUtil.getConnection(opengaussUrl, user, password); + String permissionStr = JdbcUtil.selectStringValue(pgConnection, + "select rolsystemadmin from pg_roles where rolname= '" + user + "'", "rolsystemadmin"); + isAdmin = permissionStr.equals("t") || permissionStr.equals("1"); + } catch (OpsException | SQLException e) { + log.error("get connection or sql execute failed."); + } finally { + closeConnection(pgConnection); + } + return isAdmin; + } + + /** + * get openGauss jdbc url + * + * @param host host ip + * @param port port + * @param database database name + * @param schema schema name + * @return String url + */ + public static String getOpengaussJdbcUrl(String host, String port, String database, String schema) { + StringBuilder stringBuilder = new StringBuilder("jdbc:opengauss://"); + stringBuilder.append(host).append(":").append(port).append("/").append(database); + + if (!ObjectUtils.isEmpty(schema)) { + stringBuilder.append("?currentSchema=").append(schema); + } + + return stringBuilder.toString(); + } + + /** + * determine whether execute query has result + * + * @param url jdbc url + * @param sql sql + * @param username database user name + * @param password database user password + * @return boolean + */ + public static boolean hasResultSetByExecuteQuery(String url, String sql, String username, String password) { + try (Connection connection = getConnection(url, username, password); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(sql)) { + return resultSet.next(); + } catch (SQLException e) { + log.error("Failed to execute the query. SQL: {}. ERROR: {}", sql, e.getMessage()); + } + + return false; + } + + /** + * close connection + * + * @param connection connection + */ + public static void closeConnection(Connection connection) { + try { + if (connection != null) { + connection.close(); + } + } catch (SQLException e) { + log.error("close connection fail."); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/JschExecUtil.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/JschExecUtil.java new file mode 100644 index 000000000..0b2373ed3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/JschExecUtil.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JschExecUtil.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/JschExecUtil.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.common.utils.ops; + +import com.jcraft.jsch.Session; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.vo.HostBean; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.Properties; + +/** + * cluster task operations + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@Slf4j +@Component +@Scope("prototype") +public class JschExecUtil { + private static final int SESSION_TIMEOUT = 10000; + + /** + * create session + * + * @param host host + * @return session + */ + public Session createSession(HostBean host) { + Session session = null; + try { + JSch jsch = new JSch(); + // 创建与远程服务器的会话 + session = jsch.getSession(host.getUsername(), host.getHost(), host.getPort()); + // 设置会话的密码 + session.setPassword(host.getPassword()); + // 设置一些配置,例如不检查主机密钥 + Properties config = new Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config); + + // 连接会话 + session.connect(SESSION_TIMEOUT); + } catch (JSchException e) { + throw new OpsException(e.getMessage()); + } + return session; + } + + /** + * exec command and session will be created and closed + * + * @param host host + * @param command command + * @return result + */ + public String execCommand(HostBean host, String command) { + Session session = null; + BufferedReader reader = null; + ChannelExec channelExec = null; + StringBuffer result = new StringBuffer(); + try { + JSch jsch = new JSch(); + // 创建与远程服务器的会话 + session = jsch.getSession(host.getUsername(), host.getHost(), host.getPort()); + // 设置会话的密码 + session.setPassword(host.getPassword()); + // 设置一些配置,例如不检查主机密钥 + Properties config = new Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config); + + // 连接会话 + session.connect(SESSION_TIMEOUT); + // 打开执行命令的通道 + Channel exec = session.openChannel("exec"); + if (exec instanceof ChannelExec) { + channelExec = (ChannelExec) exec; + channelExec.setCommand(command); + channelExec.setPtyType("dump"); + channelExec.setPty(true); + // 连接通道 + channelExec.connect(); + // 创建读取通道输入流的缓冲读取器 + reader = new BufferedReader(new InputStreamReader(channelExec.getInputStream(), + StandardCharsets.UTF_8)); + } else { + throw new OpsException("Unable to open exec channel."); + } + String line; + // 逐行读取并输出结果 + while ((line = reader.readLine()) != null) { + result.append(line.trim()); + } + } catch (JSchException | IOException e) { + throw new OpsException(e.getMessage()); + } finally { + // 关闭读取器 + if (Objects.nonNull(reader)) { + try { + reader.close(); + } catch (IOException e) { + log.error("close reader error"); + } + } + // 断开通道 + if (Objects.nonNull(channelExec)) { + channelExec.disconnect(); + } + // 断开会话 + if (Objects.nonNull(session)) { + session.disconnect(); + } + } + return result.toString(); + } + + + /** + * exec command by session + * + * @param session session + * @param command command + * @return result + */ + public String execCommand(Session session, String command) { + Channel channel = null; + StringBuilder result = new StringBuilder(); + try { + if (!session.isConnected()) { + Properties config = new Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config); + session.connect(SESSION_TIMEOUT); + } + + channel = session.openChannel("exec"); + if (channel instanceof ChannelExec) { + ((ChannelExec) channel).setCommand(command); + } + + try (InputStream in = channel.getInputStream(); + InputStream err = ((ChannelExec) channel).getErrStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); + BufferedReader errorReader = new BufferedReader(new InputStreamReader(err, StandardCharsets.UTF_8))) { + channel.connect(); + + String line; + while ((line = reader.readLine()) != null) { + result.append(line.trim()).append("\n"); + } + + StringBuilder errorResult = new StringBuilder(); + while ((line = errorReader.readLine()) != null) { + errorResult.append(line.trim()).append("\n"); + } + + int exitStatus = channel.getExitStatus(); + if (exitStatus != 0) { + log.error("Command: {} execution failed with exit status: {}", command, exitStatus); + log.error("Error output: {}", errorResult.toString().trim()); + } + } + } catch (JSchException | IOException e) { + throw new OpsException(e.getMessage()); + } finally { + if (Objects.nonNull(channel)) { + channel.disconnect(); + } + } + return result.toString().trim(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/JschUtil.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/JschUtil.java new file mode 100644 index 000000000..22dd67ead --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/JschUtil.java @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JschUtil.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/JschUtil.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.ops; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.jcraft.jsch.*; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.model.ops.HostFile; +import org.opengauss.admin.common.core.domain.model.ops.JschResult; +import org.opengauss.admin.common.core.domain.model.ops.WsSession; +import org.opengauss.admin.common.core.domain.model.ops.host.SSHBody; +import org.opengauss.admin.common.core.handler.ops.cache.WsConnectorManager; +import org.opengauss.admin.common.enums.ops.HostFileTypeEnum; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * Jsch Tools + * + * @author lhf + * @date 2022/6/13 15:18 + * @since 1.0 + **/ +@Slf4j +@Component +@Scope("prototype") +public class JschUtil { + private static final int SESSION_TIMEOUT = 10000; + private static final int CHANNEL_TIMEOUT = 50000; + + @Autowired + private WsConnectorManager wsConnectorManager; + @Autowired + private WsUtil wsUtil; + + /** + * Acquiring a Session + * + * @param host host + * @param port port + * @param username username + * @param password password + * @return ssh session + */ + public Optional getSession(String host, Integer port, String username, String password) { + log.info("host:{},port:{},username:{}", host, port, username); + return createSession(host, port, username, password); + } + + /** + * ChannelExec + * + * @param command Instructions to execute + * @param session session + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + */ + public JschResult executeCommand(String command, Session session) throws IOException, InterruptedException { + return executeCommand(command, session, null, null); + } + + /** + * ChannelExec + * + * @param command Instructions to execute + * @param session session + * @param env env path + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + */ + public JschResult executeCommand(String command, Session session, + String env) throws IOException, InterruptedException { + String comm = command; + if (StrUtil.isNotEmpty(env)) { + comm = "source " + env + " && " + command; + } + + return executeCommand(comm, session, null, null); + } + + /** + * ChannelExec + * + * @param env env + * @param command Instructions to execute + * @param session session + * @param autoResponse autoResponse + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + */ + public JschResult executeCommand(String env, String command, Session session, + Map autoResponse) throws IOException, InterruptedException { + String comm = command; + if (StrUtil.isNotEmpty(env)) { + comm = "source " + env + " && " + command; + } + return executeCommand(comm, session, null, autoResponse); + } + + /** + * ChannelExec + * + * @param command Instructions to execute + * @param session session + * @param autoResponse autoResponse + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + */ + public JschResult executeCommand(String command, Session session, + Map autoResponse) throws IOException, InterruptedException { + return executeCommand(command, session, null, autoResponse); + } + + /** + * ChannelExec + * + * @param command Instructions to execute + * @param sshBody sshBody + * @param autoResponse autoResponse + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + */ + public JschResult executeCommand(String command, SSHBody sshBody, + Map autoResponse) throws IOException, InterruptedException { + Optional session = getSession(sshBody.getIp(), sshBody.getSshPort(), + sshBody.getSshUsername(), sshBody.getSshPassword()); + return executeCommand(command, session.get(), autoResponse); + } + + /** + * ChannelExec + * + * @param command Instructions to execute + * @param session session + * @param wsSession websocket session + * @param autoResponse autoResponse + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + */ + public JschResult executeCommand(String command, Session session, WsSession wsSession, + Map autoResponse) throws IOException, InterruptedException { + log.debug("Execute an order {}", command); + + ChannelExec channel = null; + try { + Channel exeChannel = session.openChannel("exec"); + if (exeChannel instanceof ChannelExec) { + channel = (ChannelExec) exeChannel; + } + channel.setPtyType("dump"); + channel.setPty(true); + } catch (JSchException e) { + log.error("Obtaining the exec channel fails:", e); + throw new OpsException("Obtaining the exec channel fails"); + } + + channel.setCommand(command); + try { + channel.connect(CHANNEL_TIMEOUT); + } catch (JSchException e) { + log.error("Execute instruction [{}] exception:", command, e); + throw new OpsException("Command execution exception"); + } + + return buildJschResult(channel, wsSession, autoResponse); + } + + /** + * open ChannelShell + * + * @param session session + * @return SSH Result + */ + public ChannelShell openChannelShell(Session session) { + ChannelShell channel = null; + try { + Channel channelShell = session.openChannel("shell"); + if (channelShell instanceof ChannelShell) { + channel = (ChannelShell) channelShell; + } + channel.setPtySize(200, 40, 200 * 8, 40 * 8); + channel.connect(CHANNEL_TIMEOUT); + } catch (JSchException e) { + log.error("Channel establishment Exception", e); + throw new OpsException("Channel establishment Exception"); + } + return channel; + } + + /** + * Send instructions to ChannelShell + * + * @param channelShell channelShell + * @param command command + */ + public void sendToChannelShell(ChannelShell channelShell, String command) { + try { + if (channelShell != null) { + OutputStream outputStream = channelShell.getOutputStream(); + outputStream.write(command.getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + } + } catch (IOException e) { + log.error("Message Sending Exception", e); + throw new OpsException("Message Sending Exception"); + } + } + + /** + * The SSH Channel message is sent to the Websocket session + * + * @param channel SSH Channel + * @param wsSession WebSocket session Object + */ + public void channelToWsSession(Channel channel, WsSession wsSession) { + try (InputStream inputStream = channel.getInputStream()) { + // Cyclic reading + byte[] buffer = new byte[1024]; + int len; + // If there is no data coming, the thread will remain blocked at this point waiting for data. + while ((len = inputStream.read(buffer)) != -1) { + byte[] bytes = Arrays.copyOfRange(buffer, 0, len); + wsConnectorManager.getSession(wsSession.getBusinessId()).ifPresent(newSession -> { + try { + newSession.getSession().getBasicRemote().sendText(new String(bytes, StandardCharsets.UTF_8)); + } catch (IOException e) { + log.error("Failed to send message [{}] to websocket:", + new String(bytes, StandardCharsets.UTF_8), e); + } + }); + } + } catch (IOException e) { + log.error("Description Failed to send a message to the websocket:", e); + } finally { + wsSession.close(); + channel.disconnect(); + } + } + + /** + * File upload + * + * @param session Host SSH Session + * @param wsSession websocket session + * @param sourceFilePath Source path + * @param targetPath The target path + */ + public synchronized void upload(Session session, WsSession wsSession, String sourceFilePath, String targetPath) { + log.info("Start uploading {} to {}", sourceFilePath, targetPath); + try { + ChannelSftp channel = null; + Channel sftpChannel = session.openChannel("sftp"); + if (sftpChannel instanceof ChannelSftp) { + channel = (ChannelSftp) sftpChannel; + } + channel.connect(); + JschProgressMonitor jschProgressMonitor = new JschProgressMonitor(wsSession); + channel.put(sourceFilePath, targetPath, jschProgressMonitor, ChannelSftp.RESUME); + } catch (JSchException | SftpException e) { + log.error("sftp upload Failure", e); + throw new OpsException("sftp upload Failure"); + } + log.info("Upload End"); + } + + + /** + * ChannelShell Creates a session + * + * @param host host + * @param port port + * @param username username + * @param password password + * @return SSH session + */ + private Optional createSession(String host, Integer port, String username, String password) { + JSch jSch = new JSch(); + Session session = null; + try { + session = jSch.getSession(username, host, port); + session.setPassword(password); + session.setConfig("StrictHostKeyChecking", "no"); + session.connect(SESSION_TIMEOUT); + } catch (JSchException e) { + log.error("Connection establishment fail:", e); + session = null; + } + + return Optional.ofNullable(session); + } + + private JschResult buildJschResult(ChannelExec channelExec, WsSession wsSession, + Map autoResponse) throws IOException, InterruptedException { + JschResult jschResult = new JschResult(); + StringBuilder resultStrBuilder = new StringBuilder(); + // The output of the script execution is an input stream to the program + InputStream in = channelExec.getInputStream(); + OutputStream out = channelExec.getOutputStream(); + // Read the input stream from the remote host and get the script execution results + byte[] tmp = new byte[1024]; + while (true) { + while (in.available() > 0) { + int len = in.read(tmp, 0, 1024); + if (len < 0) { + break; + } + + String msg = new String(tmp, 0, len, StandardCharsets.UTF_8); + + msgToWs(msg, wsSession); + + resultStrBuilder.append(msg); + } + + if (channelExec.isClosed()) { + if (in.available() > 0) { + continue; + } + in.close(); + out.close(); + + int exitStatus = channelExec.getExitStatus(); + jschResult.setExitCode(exitStatus); + + break; + } + + autoResponse(autoResponse, resultStrBuilder, out); + } + + channelExec.disconnect(); + jschResult.setResult(resultStrBuilder.toString().trim()); + return jschResult; + } + + private void msgToWs(String msg, WsSession wsSession) throws IOException { + if (Objects.nonNull(wsSession) && Objects.nonNull(wsSession.getSession()) + && wsSession.getSession().isOpen()) { + wsSession.getSession().getBasicRemote().sendText(msg); + } else { + if (Objects.nonNull(wsSession) + && StrUtil.isNotEmpty(wsSession.getBusinessId())) { + wsConnectorManager.getSession(wsSession.getBusinessId()).ifPresent(newSession -> { + try { + newSession.getSession().getBasicRemote().sendText(msg); + } catch (IOException e) { + log.error("Failed to send message [{}] to websocket:", msg, e); + } + }); + } + } + } + + private void autoResponse(Map autoResponse, + StringBuilder resultStrBuilder, OutputStream out) { + if (CollUtil.isNotEmpty(autoResponse)) { + autoResponse.forEach((k, v) -> { + if (resultStrBuilder.toString().trim().endsWith(k.trim())) { + try { + out.write((v.trim() + System.getProperty("line.separator")).getBytes(StandardCharsets.UTF_8)); + out.flush(); + resultStrBuilder.append(v.trim() + System.getProperty("line.separator")); + } catch (IOException e) { + log.error("Automatic response exception", e); + } + } + }); + } + } + + /** + * ls + * + * @param session session + * @param remoteDir remoteDir + * @return hostFiles + */ + public List ls(Session session, String remoteDir) { + List res = new ArrayList<>(); + + try { + Channel channel = session.openChannel("sftp"); + channel.connect(); + ChannelSftp channelSftp = null; + if (channel instanceof ChannelSftp) { + channelSftp = (ChannelSftp) channel; + } + + Vector vector = channelSftp.ls(remoteDir); + Iterator iterator = vector.iterator(); + while (iterator.hasNext()) { + ChannelSftp.LsEntry file = null; + Object next = iterator.next(); + if (next instanceof ChannelSftp.LsEntry) { + file = (ChannelSftp.LsEntry) next; + } + res.add(HostFile.of(file.getFilename(), file.getAttrs().isDir() + ? HostFileTypeEnum.DIRECTORY : HostFileTypeEnum.FILE, file.getAttrs().getSize())); + } + } catch (JSchException | SftpException e) { + log.error("File enumeration failure", e); + } + + return res; + } + + private class JschProgressMonitor implements SftpProgressMonitor { + private long count = 0L; + private long max = 0L; + private long percent = -1L; + + private WsSession wsSession; + + public JschProgressMonitor(WsSession wsSession) { + this.wsSession = wsSession; + } + + @Override + public void init(int op, String src, String dest, long max) { + this.max = max; + count = 0; + percent = -1; + } + + @Override + public boolean count(long count) { + this.count += count; + if (percent >= this.count * 100 / max) { + return true; + } + percent = this.count * 100 / max; + + log.info("Completed " + this.count + "(" + percent + + "%) out of " + max + "."); + + wsUtil.sendText(wsSession, percent + "%"); + + return true; + } + + @Override + public void end() { + + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/WsUtil.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/WsUtil.java new file mode 100644 index 000000000..e561d57fa --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/WsUtil.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WsUtil.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/ops/WsUtil.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.ops; + +import cn.hutool.core.util.StrUtil; +import org.opengauss.admin.common.core.domain.model.ops.WsSession; +import org.opengauss.admin.common.core.handler.ops.cache.WsConnectorManager; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import jakarta.websocket.Session; +import java.io.IOException; +import java.util.Objects; + +/** + * @author lhf + * @date 2022/8/15 09:32 + **/ +@Slf4j +@Component +public class WsUtil { + + @Autowired + private WsConnectorManager wsConnectorManager; + + public void sendText(WsSession wsSession, String message) { + if (StrUtil.isEmpty(message)) { + return; + } + + if (Objects.nonNull(wsSession)) { + wsConnectorManager.getSession(wsSession.getBusinessId()).ifPresent(wss -> { + Session session = wss.getSession(); + if (session.isOpen()) { + try { + session.getBasicRemote().sendText(message); + } catch (IOException e) { + log.error("Failed to send a message to {}, error: {}", wsSession.getBusinessId(), e); + } + } else { + log.error("Connection {} closed", wsSession.getBusinessId()); + } + }); + } + } + + public void close(WsSession wsSession) { + if (Objects.nonNull(wsSession)) { + wsConnectorManager.removeByVal(wsSession); + Session session = wsSession.getSession(); + + if (Objects.nonNull(session)) { + try { + session.close(); + } catch (IOException e) { + log.error("close websocket session {} error : {}", e, wsSession.getBusinessId()); + } + } + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/spring/SpringUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/spring/SpringUtils.java new file mode 100644 index 000000000..8c35b80d5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/spring/SpringUtils.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SpringUtils.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/spring/SpringUtils.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.spring; + +import org.opengauss.admin.common.utils.StringUtils; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * Spring Tool + * + * @author xielibo + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware { + /** + * Spring ApplicationContext + */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringUtils.applicationContext = applicationContext; + } + + /** + * Get Bean + * + * @param name + * @throws BeansException + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException { + return (T) beanFactory.getBean(name); + } + + /** + * Get Bean + * + * @param clz + * @return + * @throws BeansException + */ + public static T getBean(Class clz) throws BeansException { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * Check containsBean + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) { + return beanFactory.containsBean(name); + } + + /** + * Determines whether the bean definition registered with the given name is a singleton or a prototype. + * If no bean definition corresponding to the given name is found, an exception (NoSuchBeanDefinitionException) will be thrown + * @param name + * @return boolean + * @throws NoSuchBeanDefinitionException + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return beanFactory.isSingleton(name); + } + + /** + * Get Type + * @param name + * @throws NoSuchBeanDefinitionException + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getType(name); + } + + /** + * If the given bean name has aliases in the bean definition, return those aliases + * + * @param name + * @return + * @throws NoSuchBeanDefinitionException + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getAliases(name); + } + + /** + * Get the aop proxy object + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) { + return (T) AopContext.currentProxy(); + } + + /** + * Get the current environment configuration, if no configuration returns null + * + */ + public static String[] getActiveProfiles() { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * Get the current environment configuration, when there are multiple environment configurations, only get the first one + * + */ + public static String getActiveProfile() { + final String[] activeProfiles = getActiveProfiles(); + return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/uuid/IdUtils.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/uuid/IdUtils.java new file mode 100644 index 000000000..26e67cb26 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/uuid/IdUtils.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IdUtils.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/uuid/IdUtils.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.uuid; + +/** + * ID generate tool + * + * @author xielibo + */ +public class IdUtils { + /** + * random UUID + * + * @return UUID + */ + public static String randomUuid() { + return UUID.randomUuid().toString(); + } + + /** + * simple uuid + * + * @return UUID + */ + public static String simpleUuid() { + return UUID.randomUuid().toString(true); + } + + /** + * Get a random UUID and use ThreadLocalRandom with better performance to generate a UUID + * + */ + public static String fastUuid() { + return UUID.fastUuid().toString(); + } + + /** + * Simplified UUID, remove the horizontal line, use ThreadLocalRandom with better performance to generate UUID + * + */ + public static String fastSimpleUuid() { + return UUID.fastUuid().toString(true); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/uuid/UUID.java b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/uuid/UUID.java new file mode 100644 index 000000000..cb194fc44 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-common/src/main/java/org/opengauss/admin/common/utils/uuid/UUID.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UUID.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-common/src/main/java/org/opengauss/admin/common/utils/uuid/UUID.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.common.utils.uuid; + +import org.opengauss.admin.common.exception.UtilException; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Provide a Universal Unique Identifier + * + * @author xielibo + */ +public final class UUID implements java.io.Serializable, Comparable { + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom + */ + private static class Holder { + static final SecureRandom NUMBER_GENERATOR = getSecureRandom(); + } + + /** + * Up to 64 significant bits + */ + private final long mostSigBits; + + /** + * Least significant 64 bits + */ + private final long leastSigBits; + + /** + * structure + */ + private UUID(byte[] data) { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * Constructs a new UUID using the specified data。 + */ + public UUID(long mostSigBits, long leastSigBits) { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * Get a static factory for type 4 (pseudo-randomly generated) UUIDs. + * The UUID is generated using a cryptographically thread-local pseudo-random number generator. + */ + public static UUID fastUuid() { + return randomUuid(false); + } + + /** + * Get a static factory for type 4 (pseudo-randomly generated) UUIDs. + * The UUID is generated using a cryptographically strong pseudorandom number generator. + * + */ + public static UUID randomUuid() { + return randomUuid(true); + } + + /** + * Get a static factory for type 4 (pseudo-randomly generated) UUIDs. + * The UUID is generated using a cryptographically strong pseudorandom number generator. + * + */ + public static UUID randomUuid(boolean isSecure) { + final Random ng = isSecure ? Holder.NUMBER_GENERATOR : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + + /** + * Returns the least significant 64 bits of this UUID's 128-bit value. + */ + public long getLeastSignificantBits() { + return leastSigBits; + } + + /** + * Returns the most significant 64 bits of this UUID's 128-bit value. + * + */ + public long getMostSignificantBits() { + return mostSigBits; + } + + /** + * version + * + */ + public int version() { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * variant + * + */ + public int variant() { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * timestamp + */ + public long timestamp() throws UnsupportedOperationException { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * clockSequence + */ + public int clockSequence() throws UnsupportedOperationException { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * node + */ + public long node() throws UnsupportedOperationException { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * toString + */ + @Override + public String toString() { + return toString(false); + } + + /** + * toString + */ + public String toString(boolean isSimple) { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (false == isSimple) { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (false == isSimple) { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (false == isSimple) { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (false == isSimple) { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * get hashCode + * + * @return UUID + */ + @Override + public int hashCode() { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * equals + */ + @Override + public boolean equals(Object obj) { + if ((null == obj) || (obj.getClass() != UUID.class)) { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * compareTo + */ + @Override + public int compareTo(UUID val) { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + + /** + * digits + * + */ + private static String digits(long val, int digits) { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * checkTimeBase + */ + private void checkTimeBase() { + if (version() != 1) { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * getSecureRandom + */ + public static SecureRandom getSecureRandom() { + try { + return SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + throw new UtilException(e); + } + } + + /** + * getRandom + */ + public static ThreadLocalRandom getRandom() { + return ThreadLocalRandom.current(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/pom.xml b/ER-Model/openGauss-datakit/visualtool-framework/pom.xml new file mode 100644 index 000000000..2732b5a8f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/pom.xml @@ -0,0 +1,86 @@ + + + + org.opengauss + visualtool-parent + ${admin.version} + ../../pom.xml + + 4.0.0 + + visualtool-framework + + + visualtool-framework + + + + + + org.springframework.boot + spring-boot-starter-web + + + com.baomidou + mybatis-plus-jsqlparser + + + org.opengauss + visualtool-service + + + + com.github.oshi + oshi-core + + + + + + + org.apache.maven.plugins + maven-clean-plugin + ${maven-clean-plugin.version} + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + default-prepare-agent + + prepare-agent + + + + report + prepare-package + + report + + + target/jacoco.exec + + + + junit-test + test + + report + + + target/jacoco.exec + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/aspectj/LogAspect.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/aspectj/LogAspect.java new file mode 100644 index 000000000..864ce3bd3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/aspectj/LogAspect.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * LogAspect.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/aspectj/LogAspect.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.aspectj; + +import com.alibaba.fastjson.JSON; +import org.opengauss.admin.common.annotation.Log; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.enums.BusinessStatus; +import org.opengauss.admin.common.enums.HttpMethod; +import org.opengauss.admin.common.utils.SecurityUtils; +import org.opengauss.admin.common.utils.ServletUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.common.utils.ip.IpUtils; +import org.opengauss.admin.framework.manager.AsyncManager; +import org.opengauss.admin.framework.manager.factory.AsyncFactory; +import org.opengauss.admin.system.domain.SysOperLog; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.HandlerMapping; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Collection; +import java.util.Map; + +/** + * Operation log record AOP processing + * + * @author xielibo + */ +@Slf4j +@Aspect +@Component +public class LogAspect { + + /** + * doAfterReturning + * + * @param joinPoint + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * doAfterThrowing + * + * @param joinPoint + * @param e + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) { + try { + LoginUser loginUser = SecurityUtils.getLoginUser(); + + SysOperLog operLog = new SysOperLog(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + operLog.setOperIp(ip); + operLog.setOperUrl(ServletUtils.getRequest().getRequestURI()); + if (loginUser != null) { + operLog.setOperName(loginUser.getUsername()); + } + if (e != null) { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); + } + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + RequestContextHolder.setRequestAttributes(sra, true); + AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + } catch (Exception exp) { + log.error("error:{}", exp.getMessage()); + } + } + + /** + * Obtain the description information of the method in the annotation for Controller layer annotation + * + * @param log log + * @param operLog operLog + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception { + operLog.setBusinessType(log.businessType().ordinal()); + operLog.setTitle(log.title()); + operLog.setOperatorType(log.operatorType().ordinal()); + if (log.isSaveRequestData()) { + setRequestValue(joinPoint, operLog); + } + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) { + operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); + } + } + + /** + * Get the parameters of the request and put them in the log + * + * @param operLog operLog + * @throws Exception e + */ + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception { + String requestMethod = operLog.getRequestMethod(); + if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { + String params = argsArrayToString(joinPoint.getArgs()); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + } else { + Map paramsMap = (Map) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000)); + } + } + + /** + * parameter assembly + */ + private String argsArrayToString(Object[] paramsArray) { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) { + for (Object o : paramsArray) { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) { + try { + Object jsonObj = JSON.toJSON(o); + params += jsonObj.toString() + " "; + } catch (Exception e) { + } + } + } + } + return params.trim(); + } + + /** + * Determine whether to filter the object. + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection) o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map) o; + for (Object value : map.entrySet()) { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/ApplicationConfig.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/ApplicationConfig.java new file mode 100644 index 000000000..63a227b0b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/ApplicationConfig.java @@ -0,0 +1,42 @@ +/** + Copyright ruoyi. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.framework.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +import java.util.TimeZone; + +/** + * Application Configuration + * + * @author xielibo + */ +@Configuration +@EnableAspectJAutoProxy(exposeProxy = true) +@MapperScan("org.opengauss.admin.**.mapper") +public class ApplicationConfig { + /** + * TimeZone Configuration + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/AutoFillMetaObjectHandler.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/AutoFillMetaObjectHandler.java new file mode 100644 index 000000000..be98bfdc5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/AutoFillMetaObjectHandler.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AutoFillMetaObjectHandler.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/AutoFillMetaObjectHandler.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.config; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.utils.ServletUtils; +import org.opengauss.admin.framework.web.service.TokenService; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; + +/** + * @className: AutoFillMetaObjectHandler + * @description: Autofill public attribute data + * @author: xielibo + * @date: 2022-08-06 10:36 PM + **/ +@Slf4j +@Component +public class AutoFillMetaObjectHandler implements MetaObjectHandler { + + @Autowired + private TokenService tokenService; + + @Override + public void insertFill(MetaObject metaObject) { + LoginUser loginUser = null; + try { + loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + } catch (Exception e) { + log.info("default user information not found, this is a new installation"); + } + + if (loginUser != null) { + this.setFieldValByName("createBy", loginUser.getUser().getUserName(), metaObject); + this.setFieldValByName("updateBy", loginUser.getUser().getUserName(), metaObject); + } + this.setFieldValByName("createTime", new Date(), metaObject); + this.setFieldValByName("updateTime", new Date(), metaObject); + } + + + @Override + public void updateFill(MetaObject metaObject) { + LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); + if (loginUser != null) { + this.setFieldValByName("updateBy", loginUser.getUser().getUserName(), metaObject); + } + this.setFieldValByName("updateTime", new Date(), metaObject); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/CacheConfig.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/CacheConfig.java new file mode 100644 index 000000000..a883a67c7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/CacheConfig.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CacheConfig.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/CacheConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.framework.config; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +import org.opengauss.admin.common.constant.Constants; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.TimeUnit; + +/** + * @className: CacheConfig + * @description: CacheConfig + * @author: xielibo + * @date: 2022-12-17 12:50 + **/ +@Configuration +public class CacheConfig { + private int expireTime = Constants.TOKEN_EXPIRE_TIME; + + @Bean + public Cache caffeineCache() { + return Caffeine.newBuilder() + .expireAfterWrite(5, TimeUnit.SECONDS) + .initialCapacity(100) + .maximumSize(1000) + .build(); + } + + @Bean + public Cache loginCache() { + return Caffeine.newBuilder() + .expireAfterWrite(expireTime, TimeUnit.MINUTES) + .initialCapacity(100) + .maximumSize(1000) + .build(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/FilterConfig.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/FilterConfig.java new file mode 100644 index 000000000..19ace2caa --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/FilterConfig.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * FilterConfig.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/FilterConfig.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.config; + +import org.opengauss.admin.common.filter.RepeatableFilter; +import org.opengauss.admin.common.filter.XssAttacksFilter; +import org.opengauss.admin.common.utils.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import jakarta.servlet.DispatcherType; +import java.util.HashMap; +import java.util.Map; + +/** + * Application Filter Configuration + * + * @author xielibo + */ +@Configuration +@ConditionalOnProperty(value = "xss.enabled", havingValue = "true") +public class FilterConfig { + + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Bean + public FilterRegistrationBean xssFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssAttacksFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("excludes", excludes); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Bean + public FilterRegistrationBean someFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/MybatisPlusConfig.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/MybatisPlusConfig.java new file mode 100644 index 000000000..bba968eed --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/MybatisPlusConfig.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MybatisPlusConfig.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/MybatisPlusConfig.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * Description: mybatis-plus configuration + * + * @author xielibo + * @version 1.0 + * @date: 2021/9/28 5:54 PM + * @since JDK 1.8 + */ +@EnableTransactionManagement(proxyTargetClass = true) +@Configuration +public class MybatisPlusConfig { + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(paginationInnerInterceptor()); + interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); + interceptor.addInnerInterceptor(blockAttackInnerInterceptor()); + return interceptor; + } + + /** + * Page Intercaptor + */ + public PaginationInnerInterceptor paginationInnerInterceptor() { + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); + paginationInnerInterceptor.setDbType(DbType.POSTGRE_SQL); + paginationInnerInterceptor.setMaxLimit(-1L); + return paginationInnerInterceptor; + } + + /** + * Optimistic Lock Plugin + */ + public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() { + return new OptimisticLockerInnerInterceptor(); + } + + /** + * Prevent full table deletion + */ + public BlockAttackInnerInterceptor blockAttackInnerInterceptor() { + return new BlockAttackInnerInterceptor(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/PluginProperties.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/PluginProperties.java new file mode 100644 index 000000000..ea2b45fb6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/PluginProperties.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +package org.opengauss.admin.framework.config; + +import lombok.Data; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * PluginProperties + * + * @author: wangchao + * @Date: 2025/9/17 11:14 + * @since 7.0.0-RC2 + **/ +@Data +@Component +@ConfigurationProperties(prefix = "plugin") +public class PluginProperties { + private String mainPackage; + private String runMode; + private List pluginPath; +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/ResourcesConfig.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/ResourcesConfig.java new file mode 100644 index 000000000..40dbe2cec --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/ResourcesConfig.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ResourcesConfig.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/ResourcesConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.framework.config; + +import com.google.common.collect.Lists; + +import jakarta.annotation.Resource; + +import org.opengauss.admin.common.config.SystemConfig; +import org.opengauss.admin.common.constant.Constants; +import org.opengauss.admin.framework.interceptor.impl.SameUrlDataInterceptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +/** + * Web configuration + * + * @author xielibo + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer { + @Autowired + private SameUrlDataInterceptor sameUrlDataInterceptor; + + @Resource + private PluginProperties pluginProperties; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**") + .addResourceLocations("file:" + SystemConfig.getStoragePath() + "/"); + registry.addResourceHandler("/web/**").addResourceLocations("classpath:/static/"); + String mainPluginPath = pluginProperties.getPluginPath().get(0); + loadPluginDirectoryJars(registry, mainPluginPath); + } + + private static void loadPluginDirectoryJars(ResourceHandlerRegistry registry, String mainPluginPath) { + File pluginDirectory = new File(mainPluginPath); + if (pluginDirectory.exists() && pluginDirectory.isDirectory()) { + File[] jars = pluginDirectory.listFiles((dir, name) -> name.endsWith(".jar")); + if (jars != null) { + Arrays.stream(jars).forEach(jar -> { + String jarPath = "jar:file:" + jar.getAbsolutePath() + "!/classes/resources/"; + registry.addResourceHandler("/static-plugin/**").addResourceLocations(jarPath); + }); + } + } + } + + /** + * Add a custom intercept + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(sameUrlDataInterceptor).addPathPatterns("/**"); + } + + /** + * cors config + */ + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + List patterns = Lists.newArrayList(); + patterns.add("*"); + config.setAllowedOriginPatterns(patterns); + config.addAllowedHeader("*"); + config.addAllowedMethod("*"); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/SecurityConfig.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/SecurityConfig.java new file mode 100644 index 000000000..6d773e089 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/SecurityConfig.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SecurityConfig.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/SecurityConfig.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.config; + +import org.opengauss.admin.framework.security.filter.JwtAuthenticationTokenFilter; +import org.opengauss.admin.framework.security.handle.AuthenticationEntryPointImpl; +import org.opengauss.admin.framework.security.handle.LogoutSuccessHandlerImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.filter.CorsFilter; + +/** + * spring security configuration + * + * @author xielibo + */ +@EnableWebSecurity +@Configuration +public class SecurityConfig { + /** + * Authentication failure Handler + */ + @Autowired + private AuthenticationEntryPointImpl unauthorizedHandler; + + /** + * Logout Handler + */ + @Autowired + private LogoutSuccessHandlerImpl logoutSuccessHandler; + + /** + * Token Authentication Filter + */ + @Autowired + private JwtAuthenticationTokenFilter authenticationTokenFilter; + + /** + * CORS Filter + */ + @Autowired + private CorsFilter corsFilter; + + /** + * Autowrite AuthenticationManager + * + * @return + * @throws Exception + */ + @Bean + public AuthenticationManager authenticationManager(BCryptPasswordEncoder bCryptPasswordEncoder, + UserDetailsService userDetailService) { + DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(userDetailService); + authenticationProvider.setPasswordEncoder(bCryptPasswordEncoder); + return new ProviderManager(authenticationProvider); + } + + /** + * filter chain + * + * @param httpSecurity httpSecurity + * @return SecurityFilterChain + * @throws Exception Exception + */ + @Bean + protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { + httpSecurity + // 配置注销 + .logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler)) + // 添加过滤器 + .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class) + .addFilterBefore(corsFilter, LogoutFilter.class) + // 禁用 CSRF + .csrf(AbstractHttpConfigurer::disable) + // 异常处理 + .exceptionHandling(exceptionHandling -> exceptionHandling.authenticationEntryPoint(unauthorizedHandler)) + // 无状态会话 + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + // 授权配置(关键修改) + .authorizeHttpRequests(authorize -> authorize + // 允许所有用户访问的端点 + .requestMatchers("/login", "/register", "/captchaImage", "/pubKey") + .permitAll() + // 静态资源 + .requestMatchers(HttpMethod.GET, "/", "/*.html", "/*/*.html", "/*/*.css", "/*/*.js", "/img/**", + "/profile/**") + .permitAll() + .requestMatchers("/*.ico", "/notice/send", "/common/download**", "/common/download/resource**") + .permitAll() + // WebSocket + .requestMatchers("/websocket/**", "/ws/**") + .permitAll() + // 插件相关 添加需要的静态资源路径(关键) + .requestMatchers("/static-plugin/**", "/plugins/base-ops/**", "/plugins/oauth-login/oauth/**", + "/plugins/alert-monitor/api/v1/alerts", + "/plugins/observability-sql-diagnosis/sqlDiagnosis/api/open/v1/diagnosisTasks/**") + .permitAll() + // 其他公共端点 + .requestMatchers("/webjars/**", "/agent/**", "/receive/**", "/prometheus", + "/modeling/visualization/report/share/**") + .permitAll() + // 其他请求需要认证 + .anyRequest() + .authenticated()) + // 禁用 X-Frame-Options 以便嵌入 iframe + .headers(headers -> headers.frameOptions(frameOptions -> frameOptions.disable())); + return httpSecurity.build(); + } + + /** + * Hash Encryption Implementation + */ + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/ThreadPoolConfig.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/ThreadPoolConfig.java new file mode 100644 index 000000000..bfec76b40 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/ThreadPoolConfig.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ThreadPoolConfig.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/ThreadPoolConfig.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.config; + +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.opengauss.admin.common.utils.Threads; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * ThreadPool Configuration + * + * @author xielibo + **/ +@Configuration +public class ThreadPoolConfig { + private int scheduledCorePoolSize = 10; + private int corePoolSize = 50; + private int maxPoolSize = 50; + private int queueCapacity = 2000; + private int keepAliveSeconds = 300; + + /** + * Agent Task Thread Pool Task Executor + * + * @return ThreadPoolTaskExecutor + */ + @Bean(name = "agentTaskExecutor") + public ThreadPoolTaskExecutor agentTaskThreadPoolTaskExecutor() { + return buildThreadPoolTaskExecutor("agent-task-group"); + } + + /** + * Common Thread Pool Task Executor + * + * @return ThreadPoolTaskExecutor + */ + @Bean(name = "threadPoolTaskExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() { + return buildThreadPoolTaskExecutor(""); + } + + private ThreadPoolTaskExecutor buildThreadPoolTaskExecutor(String threadNamePrefix) { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(maxPoolSize); + executor.setThreadNamePrefix(threadNamePrefix); + executor.setCorePoolSize(corePoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * Scheduled Thread Pool Task Executor + * + * @return ScheduledExecutorService + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() { + return new ScheduledThreadPoolExecutor(scheduledCorePoolSize, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) { + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/WebClientConfig.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/WebClientConfig.java new file mode 100644 index 000000000..a121fd196 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/config/WebClientConfig.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.framework.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +/** + * WebClientConfig + * + * @author: wangchao + * @Date: 2025/6/10 20:26 + * @since 7.0.0-RC2 + **/ +@Configuration +public class WebClientConfig { + /** + * WebClient builder + * + * @return WebClient + */ + @Bean + public WebClient webClient() { + return WebClient.builder().build(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/interceptor/BaseRepeatSubmitInterceptor.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/interceptor/BaseRepeatSubmitInterceptor.java new file mode 100644 index 000000000..a53a4a443 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/interceptor/BaseRepeatSubmitInterceptor.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BaseRepeatSubmitInterceptor.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/interceptor/BaseRepeatSubmitInterceptor.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.framework.interceptor; + +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.common.annotation.RepeatSubmit; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.utils.ServletUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.lang.reflect.Method; + +/** + * Duplicate Submission Interceptor + * + * @author xielibo + */ +public abstract class BaseRepeatSubmitInterceptor implements HandlerInterceptor { + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (handler instanceof HandlerMethod) { + HandlerMethod handlerMethod = (HandlerMethod) handler; + Method method = handlerMethod.getMethod(); + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); + if (annotation != null) { + if (this.isRepeatSubmit(request, annotation)) { + AjaxResult ajaxResult = AjaxResult.error(annotation.message()); + ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult)); + return false; + } + } + return true; + } else { + return true; + } + } + + /** + * To verify whether to submit repeatedly, the subclass implements specific anti-repeat submission rules + * + * @param request + * @return + * @throws Exception + */ + public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/interceptor/impl/SameUrlDataInterceptor.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/interceptor/impl/SameUrlDataInterceptor.java new file mode 100644 index 000000000..a0a261c1f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/interceptor/impl/SameUrlDataInterceptor.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SameUrlDataInterceptor.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/interceptor/impl/SameUrlDataInterceptor.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.framework.interceptor.impl; + +import com.alibaba.fastjson.JSONObject; +import com.github.benmanes.caffeine.cache.Cache; +import org.opengauss.admin.common.annotation.RepeatSubmit; +import org.opengauss.admin.common.constant.Constants; +import org.opengauss.admin.common.filter.RepeatedlyRequestWrapper; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.common.utils.http.HttpHelper; +import org.opengauss.admin.framework.interceptor.BaseRepeatSubmitInterceptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; + +/** + * Prevent Duplicate Data Submission + * + * @author xielibo + */ +@Component +public class SameUrlDataInterceptor extends BaseRepeatSubmitInterceptor { + public final String REPEAT_PARAMS = "repeatParams"; + + public final String REPEAT_TIME = "repeatTime"; + + private final String header = Constants.TOKEN_HEADER; + @Autowired + private Cache caffeineCache; + + @SuppressWarnings("unchecked") + @Override + public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + nowParams = HttpHelper.getBodyString(repeatedlyRequest); + } + + if (StringUtils.isEmpty(nowParams)) { + nowParams = JSONObject.toJSONString(request.getParameterMap()); + } + Map nowDataMap = new HashMap(); + nowDataMap.put(REPEAT_PARAMS, nowParams); + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); + + String url = request.getRequestURI(); + + String submitKey = request.getHeader(header); + if (StringUtils.isEmpty(submitKey)) { + submitKey = url; + } + + String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey; + Object sessionObj = caffeineCache.asMap().get(cacheRepeatKey); + if (sessionObj != null) { + Map sessionMap = (Map) sessionObj; + if (sessionMap.containsKey(url)) { + Map preDataMap = (Map) sessionMap.get(url); + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) { + return true; + } + } + } + Map cacheMap = new HashMap(); + cacheMap.put(url, nowDataMap); + caffeineCache.put(cacheRepeatKey, cacheMap); + return false; + } + + /** + * Determine whether the parameters are the same + */ + private boolean compareParams(Map nowMap, Map preMap) { + String nowParams = (String) nowMap.get(REPEAT_PARAMS); + String preParams = (String) preMap.get(REPEAT_PARAMS); + return nowParams.equals(preParams); + } + + /** + * Determine the interval between two requests + */ + private boolean compareTime(Map nowMap, Map preMap, int interval) { + long time1 = (Long) nowMap.get(REPEAT_TIME); + long time2 = (Long) preMap.get(REPEAT_TIME); + if ((time1 - time2) < interval) { + return true; + } + return false; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/manager/AsyncManager.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/manager/AsyncManager.java new file mode 100644 index 000000000..37131be9a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/manager/AsyncManager.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AsyncManager.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/manager/AsyncManager.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.manager; + +import org.opengauss.admin.common.utils.Threads; +import org.opengauss.admin.common.utils.spring.SpringUtils; + +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * Asynchronous task manager + * + * @author xielibo + */ +public class AsyncManager { + + /** + * Operation delay time. (millisecond) + */ + private final int OPERATE_DELAY_TIME = 10; + + /** + * Asynchronous operation task scheduling thread pool + */ + private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); + + private AsyncManager() { + } + + private static AsyncManager me = new AsyncManager(); + + public static AsyncManager me() { + return me; + } + + /** + * Execute Task + * + * @param task task + */ + public void execute(TimerTask task) { + executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); + } + + /** + * shutdown + */ + public void shutdown() { + Threads.shutdownAndAwaitTermination(executor); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/manager/factory/AsyncFactory.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/manager/factory/AsyncFactory.java new file mode 100644 index 000000000..c39730962 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/manager/factory/AsyncFactory.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AsyncFactory.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/manager/factory/AsyncFactory.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.manager.factory; + +import org.opengauss.admin.common.utils.ip.AddressUtils; +import org.opengauss.admin.common.utils.spring.SpringUtils; +import org.opengauss.admin.system.domain.SysOperLog; +import org.opengauss.admin.system.service.ISysOperLogService; + +import java.util.Date; +import java.util.TimerTask; + +/** + * Async factory + * + * @author xielibo + */ +public class AsyncFactory { + + /** + * Record Operation Log + * + * @param operLog operLog + * @return task + */ + public static TimerTask recordOper(final SysOperLog operLog) { + return new TimerTask() { + @Override + public void run() { + operLog.setOperLocation(AddressUtils.getRealAddressByIp(operLog.getOperIp())); + operLog.setOperTime(new Date()); + SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); + } + }; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/security/filter/JwtAuthenticationTokenFilter.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/security/filter/JwtAuthenticationTokenFilter.java new file mode 100644 index 000000000..7964b8c4e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JwtAuthenticationTokenFilter.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/security/filter/JwtAuthenticationTokenFilter.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.security.filter; + +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.utils.SecurityUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.framework.web.service.TokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * token filter to verify token validity + * + * @author xielibo + */ +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { + @Autowired + private TokenService tokenService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) { + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } + chain.doFilter(request, response); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/security/handle/AuthenticationEntryPointImpl.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/security/handle/AuthenticationEntryPointImpl.java new file mode 100644 index 000000000..c4175d8a6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/security/handle/AuthenticationEntryPointImpl.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AuthenticationEntryPointImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/security/handle/AuthenticationEntryPointImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.security.handle; + +import com.alibaba.fastjson.JSON; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.common.utils.ServletUtils; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.Serializable; + +/** + * Authentication failure processing class returns Unauthorized + * + * @author xielibo + */ +@Component +public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable { + private static final long serialVersionUID = -8970718410437077606L; + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) + throws IOException { + int code = ResponseCode.UNAUTHORIZED.code(); + String msg = String.format("Authentication failed, unable to access system resource.URI: {}", request.getRequestURI()); + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/security/handle/LogoutSuccessHandlerImpl.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/security/handle/LogoutSuccessHandlerImpl.java new file mode 100644 index 000000000..ad43ef578 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * LogoutSuccessHandlerImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/security/handle/LogoutSuccessHandlerImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.security.handle; + +import com.alibaba.fastjson.JSON; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.common.utils.ServletUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.framework.web.service.TokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Custom exit success processing class + * + * @author xielibo + */ +@Configuration +public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { + + @Autowired + private TokenService tokenService; + + /** + * logout + * + * @return + */ + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) { + tokenService.delLoginUser(loginUser.getToken()); + } + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(ResponseCode.SUCCESS.code(), "Logout Success"))); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/exception/GlobalExceptionHandler.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/exception/GlobalExceptionHandler.java new file mode 100644 index 000000000..8152957d7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * GlobalExceptionHandler.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/exception/GlobalExceptionHandler.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.web.exception; + +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.common.exception.ServiceException; +import org.opengauss.admin.common.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.stream.Collectors; + +/** + * GlobalExceptionHandler + * + * @author xielibo + */ +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + + + @ExceptionHandler(value = MethodArgumentNotValidException.class) + public AjaxResult handleMethodArgumentNotValid(HttpServletRequest request, MethodArgumentNotValidException e) { + BindingResult bindingResult = e.getBindingResult(); + StringBuilder sb = new StringBuilder("Validation failed: "); + for (FieldError fieldError : bindingResult.getFieldErrors()) { + sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", "); + } + String msg = sb.toString(); + String requestUri = request.getRequestURI(); + log.error("Parameter exception occurred. Request URL'{}', params{}, message{}", requestUri, JSONObject.toJSONString(request.getParameterMap()), msg); + return AjaxResult.error(ResponseCode.BAD_REQUEST.code(), msg); + } + + @ExceptionHandler(HttpMessageNotReadableException.class) + public AjaxResult handleHttpMessageNotReadableException(HttpServletRequest request, HttpMessageNotReadableException ex) { + String requestUri = request.getRequestURI(); + log.error("Parameter exception occurred. Request URL'{}', params{}, message{}", requestUri, JSONObject.toJSONString(request.getParameterMap()), ex.getMessage()); + return AjaxResult.error(ResponseCode.BAD_REQUEST.code(), ResponseCode.BAD_REQUEST.msg()); + } + + @ExceptionHandler(ConstraintViolationException.class) + public AjaxResult handleConstraintViolationException(HttpServletRequest request, ConstraintViolationException ex) { + String message = ex.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining()); + String requestUri = request.getRequestURI(); + log.error("Parameter exception occurred. Request URL'{}', params{}, message{}", requestUri, JSONObject.toJSONString(request.getParameterMap()), message); + return AjaxResult.error(ResponseCode.BAD_REQUEST.code(), ResponseCode.BAD_REQUEST.msg()); + } + + @ExceptionHandler(MissingServletRequestParameterException.class) + public AjaxResult handleMissingServletRequestParameterException(HttpServletRequest request, MissingServletRequestParameterException ex) { + String requestUri = request.getRequestURI(); + log.error("Parameter exception occurred. Request URL'{}', params{}, message{}", requestUri, JSONObject.toJSONString(request.getParameterMap()), ex.getMessage()); + return AjaxResult.error(ResponseCode.BAD_REQUEST.code(), ResponseCode.BAD_REQUEST.msg()); + } + + /** + * Permission verification exception + */ + @ExceptionHandler(AccessDeniedException.class) + public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) { + String requestUri = request.getRequestURI(); + log.error("Permission verification exception. Request URL'{}', message{}", requestUri, e.getMessage()); + return AjaxResult.error(ResponseCode.FORBIDDEN.code()); + } + + /** + * The request method does not support + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, + HttpServletRequest request) { + String requestUri = request.getRequestURI(); + log.error("The request method does not support. Request URL'{}',Not Support'{}' {}", requestUri, e.getMethod(), e.getMessage()); + return AjaxResult.error(e.getMessage()); + } + + /** + * Business Error + */ + @ExceptionHandler(ServiceException.class) + public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); + } + + /** + * unknown runtime exception + */ + @ExceptionHandler(RuntimeException.class) + public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) { + String requestUri = request.getRequestURI(); + StringWriter errorsWriter = new StringWriter(); + e.printStackTrace(new PrintWriter(errorsWriter)); + log.error("Unknown runtime exception. Request URL'{}' {}", requestUri, errorsWriter.toString()); + return AjaxResult.error(e.getMessage()); + } + + /** + * System Error + */ + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e, HttpServletRequest request) { + String requestUri = request.getRequestURI(); + log.error("System Error. Request URL '{}' {}", requestUri, e.getMessage()); + return AjaxResult.error(e.getMessage()); + } + @ExceptionHandler(BindException.class) + public AjaxResult handleBindException(BindException e, HttpServletRequest request) { + String message = e.getAllErrors().get(0).getDefaultMessage(); + String requestUri = request.getRequestURI(); + log.error("System Error. Request URL '{}' {}", requestUri, message); + return AjaxResult.error(message); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/filter/WhiteListFilter.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/filter/WhiteListFilter.java new file mode 100644 index 000000000..51a732c73 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/filter/WhiteListFilter.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WhiteListFilter.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/filter/WhiteListFilter.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.framework.web.filter; + +import org.opengauss.admin.common.utils.ip.IpUtils; +import org.opengauss.admin.common.utils.spring.SpringUtils; +import org.opengauss.admin.system.service.ISysWhiteListService; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * @className: WhiteListFilter + * @description: WhiteListFilter + * @author: xielibo + * @date: 2022-11-25 13:00 + **/ +@Slf4j +@Component +@ConditionalOnProperty(value = "system.whitelist.enabled", havingValue = "true") +public class WhiteListFilter implements Filter { + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + String ip = IpUtils.getIpAddr((HttpServletRequest) servletRequest); + boolean isExists = SpringUtils.getBean(ISysWhiteListService.class).checkSingleIpExistsInWhiteList(ip); + if (isExists) { + filterChain.doFilter(servletRequest, servletResponse); + } else { + try { + returnContent(servletResponse, + "Sorry,The current IP is not in the white list, please contact the administrator to add it before" + + " accessing."); + } catch (Exception e) { + log.error("error, message: {}", e.getMessage()); + } + } + } + + private void returnContent(ServletResponse response, String content) throws Exception{ + PrintWriter writer = null; + response.setCharacterEncoding("UTF-8"); + response.setContentType("text/html; charset=utf-8"); + try { + writer = response.getWriter(); + writer.print(content); + + } catch (IOException e) { + } finally { + if (writer != null) { + writer.close(); + } + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/PermissionService.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/PermissionService.java new file mode 100644 index 000000000..622049d79 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/PermissionService.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * PermissionService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/PermissionService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.web.service; + +import org.opengauss.admin.common.core.domain.entity.SysRole; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.utils.SecurityUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.Set; + +/** + * Custom permission implementation, ss is taken from the initials of SpringSecurity + * + * @author xielibo + */ +@Service("ss") +public class PermissionService { + /** + * All permission IDs + */ + private static final String ALL_PERMISSION = "*:*:*"; + + /** + * Administrator role permission identifier + */ + private static final String SUPER_ADMIN = "admin"; + + private static final String ROLE_DELIMETER = ","; + + private static final String PERMISSION_DELIMETER = ","; + + /** + * Verify that the user has a certain permission + * + * @param permission permission + */ + public boolean hasPermi(String permission) { + if (StringUtils.isEmpty(permission)) { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) { + return false; + } + return hasPermissions(loginUser.getPermissions(), permission); + } + + /** + * Verify whether the user does not have a certain permission, contrary to the logic of hasPermi + * + * @param permission permission + */ + public boolean lacksPermi(String permission) { + return hasPermi(permission) != true; + } + + /** + * Verify that the user has any of the following permissions + */ + public boolean hasAnyPermi(String permissions) { + if (StringUtils.isEmpty(permissions)) { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) { + return false; + } + Set authorities = loginUser.getPermissions(); + for (String permission : permissions.split(PERMISSION_DELIMETER)) { + if (permission != null && hasPermissions(authorities, permission)) { + return true; + } + } + return false; + } + + /** + * Determine if a user has a role + * + * @param role role + */ + public boolean hasRole(String role) { + if (StringUtils.isEmpty(role)) { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) { + return false; + } + for (SysRole sysRole : loginUser.getUser().getRoles()) { + String roleKey = sysRole.getRoleKey(); + if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) { + return true; + } + } + return false; + } + + /** + * Verify that the user does not have a certain role, the opposite of isRole logic. + * + * @param role role + */ + public boolean lacksRole(String role) { + return hasRole(role) != true; + } + + /** + * Verify that the user has any of the following roles + * + */ + public boolean hasAnyRoles(String roles) { + if (StringUtils.isEmpty(roles)) { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) { + return false; + } + for (String role : roles.split(ROLE_DELIMETER)) { + if (hasRole(role)) { + return true; + } + } + return false; + } + + /** + * Determine whether to include permissions + * + * @param permissions permissions + * @param permission permission + */ + private boolean hasPermissions(Set permissions, String permission) { + return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/SysLoginService.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/SysLoginService.java new file mode 100644 index 000000000..bde6bdf9f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/SysLoginService.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysLoginService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/SysLoginService + * .java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.framework.web.service; + +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.exception.ServiceException; +import org.opengauss.admin.common.exception.user.UserPasswordNotMatchException; +import org.opengauss.admin.common.utils.RsaUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; + +/** + * Login Service + * + * @author xielibo + */ +@Component +public class SysLoginService { + @Autowired + private TokenService tokenService; + @Resource + private AuthenticationManager authenticationManager; + + /** + * Login + * + * @param username username + * @param password password + * @param code code + * @return result + * @throws UserPasswordNotMatchException UserPasswordNotMatchException + * @throws ServiceException ServiceException + */ + public String login(String username, String password, String code) + throws UserPasswordNotMatchException, ServiceException { + Authentication authentication = null; + try { + authentication = authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(username, RsaUtils.decrypt(password))); + } catch (Exception e) { + if (e instanceof BadCredentialsException) { + throw new UserPasswordNotMatchException(); + } else { + throw new ServiceException(e.getMessage()); + } + } + LoginUser loginUser = (LoginUser) authentication.getPrincipal(); + return tokenService.createToken(loginUser); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/SysPermissionService.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/SysPermissionService.java new file mode 100644 index 000000000..a3da7bc3c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/SysPermissionService.java @@ -0,0 +1,71 @@ +/** + Copyright yangdi. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.framework.web.service; + +import org.opengauss.admin.common.core.domain.entity.SysUser; +import org.opengauss.admin.system.service.ISysMenuService; +import org.opengauss.admin.system.service.ISysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Set; + +/** + * User permission processing + * + * @author xielibo + */ +@Component +public class SysPermissionService { + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysMenuService menuService; + + /** + * Get role data permission + * + * @param user user + * @return permissions + */ + public Set getRolePermission(SysUser user) { + Set roles = new HashSet(); + if (user.isAdmin()) { + roles.add("admin"); + } else { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * Get menu data permission + * + * @param user user + * @return permissions + */ + public Set getMenuPermission(SysUser user) { + Set perms = new HashSet(); + if (user.isAdmin()) { + perms.add("*:*:*"); + } else { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + return perms; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/TokenService.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/TokenService.java new file mode 100644 index 000000000..c47b836f2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/TokenService.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * TokenService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/TokenService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.framework.web.service; + +import com.github.benmanes.caffeine.cache.Cache; + +import org.opengauss.admin.common.constant.Constants; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.utils.RandomCharGenerator; +import org.opengauss.admin.common.utils.ServletUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.common.utils.ip.AddressUtils; +import org.opengauss.admin.common.utils.ip.IpUtils; +import org.opengauss.admin.common.utils.uuid.IdUtils; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; + +import java.util.HashMap; +import java.util.Map; + +/** + * TokenService + * + * @author: wangchao + * @Date: 2025/9/25 10:07 + * @since 7.0.0-RC2 + **/ +@Component +public class TokenService { + private static final String HEADER = Constants.TOKEN_HEADER; + private static final String SECRET; + + static { + SECRET = RandomCharGenerator.generateRandomBased128String(); + } + + @Autowired + private Cache loginCache; + + /** + * Get logged in user + * + * @return loginUser + */ + public LoginUser getLoginUser(HttpServletRequest request) { + String token = getToken(request); + if (StringUtils.isNotEmpty(token)) { + try { + Claims claims = parseToken(token); + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + LoginUser user = (LoginUser) loginCache.asMap().get(userKey); + setLoginUser(user); + return user; + } catch (Exception e) { + } + } + return null; + } + + /** + * Set login user + */ + public void setLoginUser(LoginUser loginUser) { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) { + refreshToken(loginUser); + } + } + + /** + * delete login user + */ + public void delLoginUser(String token) { + if (StringUtils.isNotEmpty(token)) { + String userKey = getTokenKey(token); + loginCache.asMap().remove(userKey); + } + } + + /** + * create token + * + * @param loginUser user + * @return token + */ + public String createToken(LoginUser loginUser) { + String token = IdUtils.fastUuid(); + loginUser.setToken(token); + setUserAgent(loginUser); + refreshToken(loginUser); + Map claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_KEY, token); + return createToken(claims); + } + + /** + * Refresh Token Validity Period + */ + public void refreshToken(LoginUser loginUser) { + String userKey = getTokenKey(loginUser.getToken()); + loginCache.put(userKey, loginUser); + } + + /** + * setUserAgent + * + * @param loginUser login user + */ + public void setUserAgent(LoginUser loginUser) { + String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); + loginUser.setIpaddr(ip); + loginUser.setLoginLocation(AddressUtils.getRealAddressByIp(ip)); + } + + /** + * create Token + * + * @param claims claims + * @return token + */ + private String createToken(Map claims) { + return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, SECRET).compact(); + } + + /** + * parse token + * + * @param token token + */ + private Claims parseToken(String token) { + return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody(); + } + + /** + * get Username + * + * @param token token + * @return username + */ + public String getUsernameFromToken(String token) { + Claims claims = parseToken(token); + return claims.getSubject(); + } + + /** + * get token + * + * @param request + * @return token + */ + private String getToken(HttpServletRequest request) { + String token = request.getHeader(HEADER); + if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) { + token = token.replace(Constants.TOKEN_PREFIX, ""); + } + return token; + } + + private String getTokenKey(String uuid) { + return Constants.LOGIN_TOKEN_KEY + uuid; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/UserDetailsServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/UserDetailsServiceImpl.java new file mode 100644 index 000000000..3507f61eb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/UserDetailsServiceImpl.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UserDetailsServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-framework/src/main/java/org/opengauss/admin/framework/web/service/UserDetailsServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.framework.web.service; + +import org.opengauss.admin.common.core.domain.entity.SysUser; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.enums.UserStatus; +import org.opengauss.admin.common.exception.base.BaseException; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.system.service.ISysUserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +/** + * User Valid Handler + * + * @author xielibo + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); + + @Autowired + private ISysUserService userService; + + @Autowired + private SysPermissionService permissionService; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + SysUser user = userService.selectUserByUserName(username); + if (StringUtils.isNull(user)) { + log.info("User: {} Not Exists.", username); + throw new UsernameNotFoundException("User:" + username + " Not Exists."); + } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { + log.info("User:{} Deleted.", username); + throw new BaseException("Sorry, your account: " + username + " has been deleted."); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("User:{} has been deactivated.", username); + throw new BaseException("Sorry, your account: " + username + " has been deactivated."); + } + + return createLoginUser(user); + } + + public UserDetails createLoginUser(SysUser user) { + return new LoginUser(user, permissionService.getMenuPermission(user)); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/pom.xml b/ER-Model/openGauss-datakit/visualtool-service/pom.xml new file mode 100644 index 000000000..b3e6495b6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/pom.xml @@ -0,0 +1,118 @@ + + + + org.opengauss + visualtool-parent + ${admin.version} + ../../pom.xml + + 4.0.0 + + visualtool-service + + + visualtool-service + + + + + + + org.springframework.boot + spring-boot-starter-aop + + + org.opengauss + visualtool-common + + + org.opengauss + jsch-session-pool + + + com.alibaba + druid-spring-boot-3-starter + + + + com.gitee.starblues + spring-brick + ${spring-brick.version} + + + io.micrometer + micrometer-registry-prometheus + + + + com.mysql + mysql-connector-j + + + org.postgresql + postgresql + + + io.opentelemetry + opentelemetry-sdk + + + com.lmax + disruptor + + + org.springframework.boot + spring-boot-starter-webflux + + + + + + org.apache.maven.plugins + maven-clean-plugin + ${maven-clean-plugin.version} + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + default-prepare-agent + + prepare-agent + + + + report + prepare-package + + report + + + target/jacoco.exec + + + + junit-test + test + + report + + + target/jacoco.exec + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/config/MyBatisConfig.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/config/MyBatisConfig.java new file mode 100644 index 000000000..abdf68f1b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/config/MyBatisConfig.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MyBatisConfig.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/config/MyBatisConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.config; + +import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionTemplate; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; + +import javax.sql.DataSource; + +/** + * MyBatis configuration + * + * @author xielibo + */ +@Configuration +@MapperScan("org.opengauss.admin.system.mapper") +public class MyBatisConfig { + + @Autowired + private DataSource dataSource; + + @Bean + public SqlSessionFactory sqlSessionFactory() throws Exception { + MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); + sqlSessionFactory.setDataSource(dataSource); + sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/**/*.xml")); + return sqlSessionFactory.getObject(); + } + + @Bean + public DataSourceTransactionManager transactionManager() { + return new DataSourceTransactionManager(dataSource); + } + + @Bean + public SqlSessionTemplate sqlSessionTemplate() throws Exception { + return new SqlSessionTemplate(sqlSessionFactory()); + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/base/DbDataLocation.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/base/DbDataLocation.java new file mode 100644 index 000000000..e0323df81 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/base/DbDataLocation.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.base; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import org.opengauss.admin.common.enums.DbDataLocationEnum; + +/** + * DbDataLocation + * + * @author: wangchao + * @Date: 2025/6/18 11:55 + * @since 7.0.0-RC2 + **/ +@Getter +@AllArgsConstructor +public class DbDataLocation { + private DbDataLocationEnum dataLocationEnum; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/base/GenericCriteria.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/base/GenericCriteria.java new file mode 100644 index 000000000..b6037dafd --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/base/GenericCriteria.java @@ -0,0 +1,356 @@ +/** + Copyright (c) 2014-2016 abel533@gmail.com. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.system.base; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GenericCriteria { + + protected boolean distinct; + + public String orderByClause; + + protected String tableCate; + + private Integer firstResult; + private Integer maxResults; + + private Map params; + + protected List generic; + + public GenericCriteria() { + generic = new ArrayList(); + params = new HashMap(); + } + + public void or(Criteria criteria) { + generic.add(criteria); + } + + public Criteria or() { + Criteria criteria = createCriteriaInternal(); + generic.add(criteria); + return criteria; + } + + public Criteria createCriteria() { + Criteria criteria = createCriteriaInternal(); + if (generic.size() == 0) { + generic.add(criteria); + } + return criteria; + } + + public void put(String key, Object value) { + params.put(key, value); + } + + protected Criteria createCriteriaInternal() { + Criteria criteria = new Criteria(); + return criteria; + } + + public void clear() { + generic.clear(); + params.clear(); + orderByClause = null; + distinct = false; + } + + public List getGeneric() { + return generic; + } + + public void setGeneric(List generic) { + this.generic = generic; + } + + public void setDistinct(boolean distinct) { + this.distinct = distinct; + } + + public boolean isDistinct() { + return distinct; + } + + public String getOrderByClause() { + return orderByClause; + } + + public void setOrderByClause(String orderByClause) { + this.orderByClause = orderByClause; + } + + public Integer getFirstResult() { + return firstResult; + } + + public void setFirstResult(Integer firstResult) { + this.firstResult = firstResult; + } + + public Integer getMaxResults() { + return maxResults; + } + + public void setMaxResults(Integer maxResults) { + this.maxResults = maxResults; + } + + public String getTableCate() { + return tableCate; + } + + public void setTableCate(String tableCate) { + this.tableCate = tableCate; + } + + protected abstract static class AbstractGeneratedCriteria { + protected List criteria; + + protected AbstractGeneratedCriteria() { + super(); + criteria = new ArrayList(); + } + + public boolean isValid() { + return criteria.size() > 0; + } + + public List getAllCriteria() { + return criteria; + } + + public List getCriteria() { + return criteria; + } + + protected void addCriterion(String condition) { + if (condition == null) { + throw new RuntimeException("Value for condition cannot be null"); + } + criteria.add(new Criterion(condition)); + } + + protected void addCriterion(String condition, Object value, + String property) { + if (value == null) { + throw new RuntimeException("Value for " + property + + " cannot be null"); + } + criteria.add(new Criterion(condition, value)); + } + + protected void addCriterion(String condition, Object value1, + Object value2, String property) { + if (value1 == null || value2 == null) { + throw new RuntimeException("Between values for " + property + + " cannot be null"); + } + criteria.add(new Criterion(condition, value1, value2)); + } + + public Criteria condition(String property) { + addCriterion(property); + return (Criteria) this; + } + + public Criteria isNull(String property) { + addCriterion(property + " is null"); + return (Criteria) this; + } + + public Criteria isNotNull(String property) { + addCriterion(property + " is not null"); + return (Criteria) this; + } + + public Criteria equalTo(String property, Object value) { + addCriterion(property + " =", value, property); + return (Criteria) this; + } + + public Criteria notEqualTo(String property, Object value) { + addCriterion(property + " <>", value, property); + return (Criteria) this; + } + + public Criteria greaterThan(String property, Object value) { + addCriterion(property + " >", value, property); + return (Criteria) this; + } + + public Criteria greaterThanOrEqualTo(String property, Object value) { + addCriterion(property + " >=", value, property); + return (Criteria) this; + } + + public Criteria lessThan(String property, Object value) { + addCriterion(property + " <", value, property); + return (Criteria) this; + } + + public Criteria lessThanOrEqualTo(String property, Object value) { + addCriterion(property + " <=", value, property); + return (Criteria) this; + } + + public Criteria like(String property, Object value) { + addCriterion(property + " like", value, property); + return (Criteria) this; + } + + public Criteria notLike(String property, Object value) { + addCriterion(property + " not like", value, property); + return (Criteria) this; + } + + public Criteria in(String property, List values) { + addCriterion(property + " in", values, property); + return (Criteria) this; + } + + public Criteria notIn(String property, List values) { + addCriterion(property + " not in", values, property); + return (Criteria) this; + } + + public Criteria between(String property, Object value1, Object value2) { + addCriterion(property + " between", value1, value2, property); + return (Criteria) this; + } + + public Criteria notBetween(String property, Object value1, Object value2) { + addCriterion(property + " between", value1, value2, property); + return (Criteria) this; + } + + public Criteria exists(String sql) { + addCriterion(sql); + return (Criteria) this; + } + + public Criteria sinSql(String sql) { + addCriterion(sql); + return (Criteria) this; + } + + } + + public static class Criteria extends AbstractGeneratedCriteria { + + protected Criteria() { + super(); + } + + public Criteria likeInsensitive(String property, String value) { + addCriterion("upper(" + property + ") like", value.toUpperCase(), + property); + return this; + } + } + + public static class Criterion { + private String condition; + + private Object value; + + private Object secondValue; + + private boolean noValue; + + private boolean singleValue; + + private boolean betweenValue; + + private boolean listValue; + + private String typeHandler; + + public String getCondition() { + return condition; + } + + public Object getValue() { + return value; + } + + public Object getSecondValue() { + return secondValue; + } + + public boolean isNoValue() { + return noValue; + } + + public boolean isSingleValue() { + return singleValue; + } + + public boolean isBetweenValue() { + return betweenValue; + } + + public boolean isListValue() { + return listValue; + } + + public String getTypeHandler() { + return typeHandler; + } + + protected Criterion(String condition) { + super(); + this.condition = condition; + this.typeHandler = null; + this.noValue = true; + } + + protected Criterion(String condition, Object value, String typeHandler) { + super(); + this.condition = condition; + this.value = value; + this.typeHandler = typeHandler; + if (value instanceof List) { + this.listValue = true; + } else { + this.singleValue = true; + } + } + + protected Criterion(String condition, Object value) { + this(condition, value, null); + } + + protected Criterion(String condition, Object value, Object secondValue, + String typeHandler) { + super(); + this.condition = condition; + this.value = value; + this.secondValue = secondValue; + this.typeHandler = typeHandler; + this.betweenValue = true; + } + + protected Criterion(String condition, Object value, Object secondValue) { + this(condition, value, secondValue, null); + } + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/HostRecordDataListener.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/HostRecordDataListener.java new file mode 100644 index 000000000..55f2b25c3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/HostRecordDataListener.java @@ -0,0 +1,117 @@ +package org.opengauss.admin.system.domain; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.model.ops.HostRecord; +import org.opengauss.admin.system.service.ops.IHostService; +import org.opengauss.admin.system.service.ops.impl.HostServiceImpl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Excel Host Record Listener Class. + * + * @author zzh + * @version 1.0 + * @data 2024/6/25 10:35 + */ +@Slf4j +public class HostRecordDataListener implements ReadListener { + + private IHostService hostService; + private boolean headerChecked = false; + private String isSwitchingLanguage; + + public HostRecordDataListener() { + hostService = new HostServiceImpl(); + } + + public HostRecordDataListener(IHostService hostService, String isSwitchingLanguage) { + this.hostService = hostService; + this.isSwitchingLanguage = isSwitchingLanguage; + } + + @Override + public void invoke(HostRecord data, AnalysisContext context) { + if (!headerChecked) { + throw new IllegalArgumentException("Error parsing header."); + } + log.info("Parsed a data record: {}", JSON.toJSONString(data)); + + } + + @Override + public void invokeHead(Map> headMap, AnalysisContext context) { + headerChecked = true; + Set>> entries = headMap.entrySet(); + if (!isHeaderValid(entries)) { + throw new IllegalArgumentException("Error parsing header."); + } + } + + private boolean isHeaderValid(Set>> entries) { + List expectedzhHeaders = new ArrayList<>(); + expectedzhHeaders.add("序号"); + expectedzhHeaders.add("服务器名称"); + expectedzhHeaders.add("内网IP"); + expectedzhHeaders.add("公网IP"); + expectedzhHeaders.add("端口号"); + expectedzhHeaders.add("用户名称"); + expectedzhHeaders.add("用户密码"); + expectedzhHeaders.add("是否为管理员(是|否)"); + expectedzhHeaders.add("标签"); + expectedzhHeaders.add("备注"); + + List expectedenHeaders = new ArrayList<>(); + expectedenHeaders.add("id"); + expectedenHeaders.add("name"); + expectedenHeaders.add("privateIP"); + expectedenHeaders.add("publicIP"); + expectedenHeaders.add("port"); + expectedenHeaders.add("userName"); + expectedenHeaders.add("password"); + expectedenHeaders.add("isAdmin(yes|no)"); + expectedenHeaders.add("tags"); + expectedenHeaders.add("remark"); + + boolean containsExpectedHeaders = true; + + for (String expectedHeader : "zh-CN".equals(isSwitchingLanguage) ? expectedzhHeaders : expectedenHeaders) { + boolean found = false; + for (Map.Entry> entry : entries) { + if (expectedHeader.equals(entry.getValue().getStringValue())) { + found = true; + break; + } + } + if (!found) { + containsExpectedHeaders = false; + break; + } + } + return containsExpectedHeaders; + } + + @Override + public void onException(Exception exception, AnalysisContext context) throws Exception { + ReadListener.super.onException(exception, context); + log.error("Parsing failed, but continue to the next line: {}", exception.getMessage()); + if (exception instanceof ExcelDataConvertException) { + ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception; + log.error("Parsing exception at line {}, column {}, data: {}", excelDataConvertException.getRowIndex(), + excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData()); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + log.info("All data parsing completed."); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysLogConfig.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysLogConfig.java new file mode 100644 index 000000000..a53a2ebc0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysLogConfig.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysLogConfig.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysLogConfig.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; + +/** + * System log config model + */ +@Data +public class SysLogConfig { + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + private String key; + + private String value; + +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysOperLog.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysOperLog.java new file mode 100644 index 000000000..4f6d8a496 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysOperLog.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysOperLog.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysOperLog.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.domain; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * OperLog Model + * + * @author xielibo + */ +@Data +public class SysOperLog { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(value = "oper_id", type = IdType.AUTO) + private Integer operId; + + /** + * title + */ + private String title; + + /** + * businessType(0:other 1:save 2:update 3:delete) + */ + private Integer businessType; + + /** + * businessTypes array + */ + @TableField(exist = false) + private Integer[] businessTypes; + + /** + * method + */ + private String method; + + /** + * requestMethod + */ + private String requestMethod; + + /** + * operatorType(0:other 1:SysUser) + */ + private Integer operatorType; + + /** + * operName + */ + private String operName; + + + /** + * operUrl + */ + private String operUrl; + + /** + * operIp + */ + private String operIp; + + /** + * operLocation + */ + private String operLocation; + + /** + * operParam + */ + private String operParam; + + /** + * jsonResult + */ + private String jsonResult; + + /** + * status(0: success 1:error) + */ + private Integer status; + + /** + * errorMsg + */ + private String errorMsg; + + /** + * operTime + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + private Date operTime; + + /** + * createBy + */ + @TableField(exist = false) + private String createBy; + + /** + * createTime + */ + @TableField(exist = false) + private Date createTime; + + /** + * updateBy + */ + @TableField(exist = false) + private String updateBy; + + /** + * updateTime + */ + @TableField(exist = false) + private Date updateTime; + + /** + * remark + */ + @TableField(exist = false) + private String remark; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPlugin.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPlugin.java new file mode 100644 index 000000000..ce3e143d6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPlugin.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPlugin.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPlugin.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Tolerate; + +/** + * SysPlugin model + * + * @author xielibo + */ +@Data +@Builder +@TableName("sys_plugins") +public class SysPlugin { + + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * pluginId + */ + private String pluginId; + + /** + * bootstrapClass + */ + private String bootstrapClass; + + /** + * pluginDesc + */ + private String pluginDesc; + + private String pluginDescEn; + + /** + * logoPath + */ + private String logoPath; + + /** + * pluginType + */ + private Integer pluginType; + + /** + * pluginVersion + */ + private String pluginVersion; + + /** + * pluginProvider + */ + private String pluginProvider; + + /** + * status;1:started;2:stoped + */ + private Integer pluginStatus; + + /** + * isNeedConfigured + */ + private Integer isNeedConfigured; + + /** + * theme; dark light,default light + */ + private String theme; + + + @Tolerate + public SysPlugin() { + + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginConfig.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginConfig.java new file mode 100644 index 000000000..0cbb4be74 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginConfig.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginConfig.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginConfig.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * Plugin Config Model + * @author xielibo + */ +@Data +@TableName("sys_plugin_config") +public class SysPluginConfig { + /** + * id + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + private String pluginId; + private String configJson; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginConfigData.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginConfigData.java new file mode 100644 index 000000000..f49de09b9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginConfigData.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginConfigData.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginConfigData.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * Plugin Config Data Model + * @author xielibo + */ +@Data +@TableName("sys_plugin_config_data") +public class SysPluginConfigData { + /** + * id + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + private String pluginId; + private String configData; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginLogo.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginLogo.java new file mode 100644 index 000000000..6993f9035 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginLogo.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginLogo.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginLogo.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * Plugin Logo Model + * @author xielibo + */ +@Data +@TableName("sys_plugin_logo") +public class SysPluginLogo { + /** + * id + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + private String pluginId; + private String logoPath; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginRepository.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginRepository.java new file mode 100644 index 000000000..4e7be5e1f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginRepository.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginRepository.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysPluginRepository.java + * + * -------------------------------------------------------------------------7.0.0-RC1 + */ + +package org.opengauss.admin.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Tolerate; + +/** + * SysPluginRepository model + * + * @author: zhaochen + * @since 2025-03-20 + * @version 7.0.0-RC1 + */ +@Data +@Builder +@TableName("sys_plugins_repository") +public class SysPluginRepository { + /** + * id + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * pluginId + */ + private String pluginId; + + /** + * isDownloaded + */ + private Integer isDownloaded = 0; + + /** + * pluginVersion + */ + private String pluginVersion; + + /** + * downloadUrl + */ + private String downloadUrl; + + /** + * pluginDesc + */ + private String pluginDesc; + + /** + * pluginDescEn + */ + private String pluginDescEn; + + @Tolerate + public SysPluginRepository() { + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysRoleMenu.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysRoleMenu.java new file mode 100644 index 000000000..9d019e291 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysRoleMenu.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysRoleMenu.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysRoleMenu.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.domain; + +import lombok.Data; + +/** + * Role Menu Relation Model + * + * @author xielibo + */ +@Data +public class SysRoleMenu { + /** + * roleId + */ + private Integer roleId; + + /** + * menuId + */ + private Integer menuId; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysTask.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysTask.java new file mode 100644 index 000000000..f23ade206 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysTask.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysTask.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysTask.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * Task Model + * + * @author xielibo + */ +@TableName(value ="sys_task") +@Data +public class SysTask { + + /** + * id + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * task name + */ + private String taskName; + + /** + * task type + */ + private Integer taskType; + + /** + * exec status + */ + private Integer execStatus; + + /** + * exec params + */ + private String execParams; + + /** + * exec progress + */ + private Float execProgress; + + /** + * exec hostId + */ + private String execHostId; + + /** + * plugin id + */ + private String pluginId; + + private Integer subTaskCount; + + /** + * create time + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * finish time + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date finishTime; + + /** + * execTime time + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date execTime; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysUserRole.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysUserRole.java new file mode 100644 index 000000000..65350d9fc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysUserRole.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysUserRole.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysUserRole.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.domain; + +import lombok.Data; + +/** + * User Role Relation Model + * + * @author xielibo + */ +@Data +public class SysUserRole { + + /** + * userId + */ + private Integer userId; + + /** + * roleId + */ + private Integer roleId; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysWhiteList.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysWhiteList.java new file mode 100644 index 000000000..a8557d30c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysWhiteList.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysWhiteList.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/domain/SysWhiteList.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; +import java.util.HashSet; + +/** + * White List Model + * + * @author xielibo + */ +@TableName(value ="sys_white_list") +@Data +public class SysWhiteList { + + /** + * id + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * title + */ + private String title; + + /** + * ipList + */ + private String ipList; + + /** + * createTime + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * check whether duplicate ip exist in the ipList + * + * @return result of has duplicate ip + */ + public boolean hasDuplicateIp() { + String[] ips = ipList.split(","); + HashSet set = new HashSet<>(); + for (String ip : ips) { + if (!set.add(ip)) { + return true; + } + } + return false; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysLogConfigMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysLogConfigMapper.java new file mode 100644 index 000000000..5c40c5630 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysLogConfigMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysLogConfigMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysLogConfigMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.system.domain.SysLogConfig; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SysLogConfigMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysMenuMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysMenuMapper.java new file mode 100644 index 000000000..3b7e4a6d3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysMenuMapper.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysMenuMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysMenuMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.common.core.domain.entity.SysMenu; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * Menu Mapper + * + * @author xielibo + */ +@Mapper +public interface SysMenuMapper extends BaseMapper { + /** + * selectMenuPerms + * + */ + public List selectMenuPerms(); + + /** + * Query the user system menu list + * + * @param menu menu + */ + public List selectMenuListByUserId(SysMenu menu); + + /** + * Query permissions based on user ID + * + * @param userId userId + */ + public List selectMenuPermsByUserId(Integer userId); + + /** + * Query menu by user ID + * + * @param userId userId + */ + public List selectMenuTreeByUserId(Integer userId); + + /** + * Query menu tree information based on role ID + * + * @param roleId roleId + * @param menuCheckStrictly menuCheckStrictly + */ + public List selectMenuListByRoleId(@Param("roleId") Integer roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); + + /** + * According to the plugin, get the parent menu that has other plugin submenus + * + * @param pluginId pluginId + * @return parentMenu num + */ + Integer countParentMenuHasOtherPluginSubmenusByPluginId(@Param("pluginId") String pluginId); + + /** + * According to the plugin, get the parent menu that has other enable plugin submenus + * + * @param pluginId pluginId + * @return parentMenu num + */ + Integer countParentMenuHasOtherEnablePluginSubmenusByPluginId(@Param("pluginId") String pluginId); + + /** + * Obtain the parent menu ID based on the plugin ID + * + * @param pluginId pluginId + * @return parent menuIDs + */ + List selectParentMenuIdByPluginId(@Param("pluginId") String pluginId); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysOperLogMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysOperLogMapper.java new file mode 100644 index 000000000..bb45c1304 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysOperLogMapper.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysOperLogMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysOperLogMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.system.domain.SysOperLog; +import org.apache.ibatis.annotations.Mapper; + +/** + * OperLog Mapper + * + * @author xielibo + */ +@Mapper +public interface SysOperLogMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginConfigDataMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginConfigDataMapper.java new file mode 100644 index 000000000..93abbac83 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginConfigDataMapper.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginConfigDataMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginConfigDataMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.system.domain.SysPluginConfigData; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author xielibo + */ +@Mapper +public interface SysPluginConfigDataMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginConfigMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginConfigMapper.java new file mode 100644 index 000000000..34a44bef3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginConfigMapper.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginConfigMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginConfigMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.system.domain.SysPluginConfig; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author xielibo + */ +@Mapper +public interface SysPluginConfigMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginLogoMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginLogoMapper.java new file mode 100644 index 000000000..41f2d6f0d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginLogoMapper.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginLogoMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginLogoMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.system.domain.SysPluginLogo; + +/** + * @author xielibo + */ +@Mapper +public interface SysPluginLogoMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginMapper.java new file mode 100644 index 000000000..e45caaf53 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginMapper.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.opengauss.admin.system.domain.SysPlugin; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + + +/** + * SysPluginMapper + * + * @author xielibo + */ +@Mapper +public interface SysPluginMapper extends BaseMapper { + + /** + * selectSysPluginListPage + * + */ + public IPage selectSysPluginListPage(IPage page, @Param("entity") SysPlugin sysPlugin); + + /** + * getPluginIds + * + * @return pluginIds + */ + List getPluginIds(); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginRepositoryMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginRepositoryMapper.java new file mode 100644 index 000000000..e1bcb8173 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysPluginRepositoryMapper.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginRepositoryMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/ + * mapper/SysPluginRepositoryMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.opengauss.admin.system.domain.SysPluginRepository; + +import java.util.List; + +/** + * SysPluginRepositoryMapper + * + * @author zhaochen + * @since 2025-03-20 + */ +@Mapper +public interface SysPluginRepositoryMapper extends BaseMapper { + /** + * Update plugins download status + * + * @param pluginIds pluginIds list + * @param isDownload download status 0-unloaded 1-loaded + */ + void updatePluginDownloadStatus( + @Param("pluginIds") List pluginIds, + @Param("isDownload") Integer isDownload); + + /** + * Gets a list of pluginIds that are not loaded + * + * @param uploadedPluginIds uploadedPluginIds + * @return List of unloaded pluginIds + */ + List getUnloadPluginListByRepository( + @Param("pluginIds") List uploadedPluginIds); + + /** + * Gets plugin download url by pluginId and version + * + * @param pluginId pluginId + * @param pluginVersion pluginVersion + * @return plugin download url + */ + String getPluginUrlByIdAndVersion( + @Param("pluginId") String pluginId, + @Param("pluginVersion") String pluginVersion); +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysRoleMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysRoleMapper.java new file mode 100644 index 000000000..3378ff3c0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysRoleMapper.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysRoleMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysRoleMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.opengauss.admin.common.core.domain.entity.SysRole; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * Role Mapper + * + * @author xielibo + */ +@Mapper +public interface SysRoleMapper extends BaseMapper { + /** + * Query role data by pagination according to conditions + * + * @param page page + * @param sysRole sysRole + */ + public IPage selectRoleList(IPage page, @Param("entity") SysRole sysRole); + + /** + * Query role data based on pagination + * + * @param sysRole sysRole + */ + public List selectRoleList(@Param("entity") SysRole sysRole); + + /** + * Get the role selection box list according to the user ID + * + * @param userId userId + */ + public List selectRoleListByUserId(Integer userId); + + /** + * Query roles based on user ID + * + * @param userId userId + */ + public List selectRolePermissionByUserId(Integer userId); + + /** + * Query role by condition + * + * @param queryWrapper queryWrapper + */ + public SysRole selectRole(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * Query role by condition + * + * @param queryWrapper queryWrapper + */ + public List selectRoles(@Param(Constants.WRAPPER) Wrapper queryWrapper); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysRoleMenuMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 000000000..3fc70da3f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysRoleMenuMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysRoleMenuMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.system.domain.SysRoleMenu; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * Role Menu Relation Mapper + * + * @author xielibo + */ +@Mapper +public interface SysRoleMenuMapper extends BaseMapper { + /** + * Add role menu association information in batches + * + * @param roleMenuList roleMenuList + */ + public int batchRoleMenu(List roleMenuList); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysSettingExtMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysSettingExtMapper.java new file mode 100644 index 000000000..1f271a7a0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysSettingExtMapper.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.common.core.domain.entity.SysSettingExtEntity; + +/** + * SysSettingExtMapper + * + * @author: wangchao + * @Date: 2025/9/8 15:48 + * @since 7.0.0-RC2 + **/ +@Mapper +public interface SysSettingExtMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysSettingMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysSettingMapper.java new file mode 100644 index 000000000..78fe6e36a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysSettingMapper.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysSettingMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysSettingMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.common.core.domain.entity.SysSettingEntity; + +/** + * System Setting Mapper + * + * @author wangyl + */ +@Mapper +public interface SysSettingMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysTaskMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysTaskMapper.java new file mode 100644 index 000000000..2d6d26cd4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysTaskMapper.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysTaskMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysTaskMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.opengauss.admin.common.core.dto.SysTaskDto; +import org.opengauss.admin.system.domain.SysTask; + +import java.util.List; + + +/** + * Task Mapper + * + * @author xielibo + */ +@Mapper +public interface SysTaskMapper extends BaseMapper { + + + /** + * Query the task list by page + * + * @param page page + * @param task task + */ + public IPage selectTaskPage(IPage page, @Param("entity") SysTaskDto task); + + /** + * Query the task list + * + * @param task task + */ + public List selectTaskList(SysTaskDto task); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysUserMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysUserMapper.java new file mode 100644 index 000000000..ba0e96fba --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysUserMapper.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysUserMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysUserMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.opengauss.admin.common.core.domain.entity.SysUser; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + + +/** + * User Mapper + * + * @author xielibo + */ +@Mapper +public interface SysUserMapper extends BaseMapper { + + /** + * Query users by condition + * + * @param queryWrapper queryWrapper + */ + public SysUser selectUserVo(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * Query user list by pagination according to conditions + * + * @param page page + * @param sysUser sysUser + */ + public IPage selectUserList(IPage page, @Param("entity") SysUser sysUser); + +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysUserRoleMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysUserRoleMapper.java new file mode 100644 index 000000000..c143d80d6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysUserRoleMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysUserRoleMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.system.domain.SysUserRole; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * User Role Relation Mapper + * + * @author xielibo + */ +@Mapper +public interface SysUserRoleMapper extends BaseMapper { + + /** + * Delete user and role associations by user ID + * + * @param userId userId + */ + public int deleteUserRoleByUserId(Integer userId); + + /** + * Add user role information in batches + * + * @param userRoleList userRoleList + */ + public int batchUserRole(List userRoleList); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysWhiteListMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysWhiteListMapper.java new file mode 100644 index 000000000..51d38a4a5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysWhiteListMapper.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysWhiteListMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/SysWhiteListMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.opengauss.admin.system.domain.SysWhiteList; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + + +/** + * White List Mapper + * + * @author xielibo + */ +@Mapper +public interface SysWhiteListMapper extends BaseMapper { + + + /** + * Query the whitelist list by page + * + * @param page page + * @param whiteList SysWhiteList + */ + public IPage selectWhiteListPage(IPage page, @Param("entity") SysWhiteList whiteList); + + /** + * Query the whitelist list + * + * @param whiteList whiteList + */ + public List selectWhiteListList(SysWhiteList whiteList); + + + public Integer countByIp(@Param("ip") String ip); + + public Integer countByTitle(SysWhiteList whiteList); + +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/AgentInstallMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/AgentInstallMapper.java new file mode 100644 index 000000000..50c753854 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/AgentInstallMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.mapper.agent; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.common.core.domain.entity.agent.AgentInstallEntity; + +/** + * AgentInstallMapper + * + * @author: wangchao + * @Date: 2025/4/10 10:01 + * @Description: AgentInstallMapper + * @since 7.0.0-RC2 + **/ +@Mapper +public interface AgentInstallMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/MetricHistoricalMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/MetricHistoricalMapper.java new file mode 100644 index 000000000..b0f7eb36b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/MetricHistoricalMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.mapper.agent; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.common.core.domain.entity.agent.MetricHistorical; + +/** + * MetricHistoricalMapper + * + * @author: wangchao + * @Date: 2025/4/25 16:12 + * @Description: MetricHistoricalMapper + * @since 7.0.0-RC2 + **/ +@Mapper +public interface MetricHistoricalMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/MetricRealTimeMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/MetricRealTimeMapper.java new file mode 100644 index 000000000..17db361cd --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/MetricRealTimeMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.mapper.agent; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.common.core.domain.entity.agent.MetricRealTime; + +/** + * MetricRealTimeMapper + * + * @author: wangchao + * @Date: 2025/4/25 16:12 + * @Description: AgentClusterRelationMapper + * @since 7.0.0-RC2 + **/ +@Mapper +public interface MetricRealTimeMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskInstanceMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskInstanceMapper.java new file mode 100644 index 000000000..e38df55c4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskInstanceMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.mapper.agent; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.common.core.domain.entity.agent.TaskInstanceEntity; + +/** + * TaskInstanceMapper + * + * @author: wangchao + * @Date: 2025/4/25 16:15 + * @Description: TaskInstanceMapper + * @since 7.0.0-RC2 + **/ +@Mapper +public interface TaskInstanceMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskMetricsDefinitionMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskMetricsDefinitionMapper.java new file mode 100644 index 000000000..81aebc9af --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskMetricsDefinitionMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.mapper.agent; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.common.core.domain.entity.agent.TaskMetricsDefinition; + +/** + * TaskMetricsDefinitionMapper + * + * @author: wangchao + * @Date: 2025/4/25 16:17 + * @Description: TaskMetricsDefinitionMapper + * @since 7.0.0-RC2 + **/ +@Mapper +public interface TaskMetricsDefinitionMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskSchemaDefinitionMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskSchemaDefinitionMapper.java new file mode 100644 index 000000000..f751994c4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskSchemaDefinitionMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.mapper.agent; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.common.core.domain.entity.agent.TaskSchemaDefinition; + +/** + * TaskSchemaDefinitionMapper + * + * @author: wangchao + * @Date: 2025/4/25 16:20 + * @Description: TaskSchemaDefinitionMapper + * @since 7.0.0-RC2 + **/ +@Mapper +public interface TaskSchemaDefinitionMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskTableRelationMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskTableRelationMapper.java new file mode 100644 index 000000000..e29f3b605 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskTableRelationMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.mapper.agent; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.common.core.domain.entity.agent.TaskTableRelation; + +/** + * TaskTableRelationMapper + * + * @author: wangchao + * @Date: 2025/5/27 16:43 + * @Description: TaskTableRelationMapper + * @since 7.0.0-RC2 + **/ +@Mapper +public interface TaskTableRelationMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskTemplateDefinitionMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskTemplateDefinitionMapper.java new file mode 100644 index 000000000..fb9fc724c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/agent/TaskTemplateDefinitionMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.mapper.agent; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.common.core.domain.entity.agent.TaskTemplateDefinition; + +/** + * TaskTemplateDefinitionMapper + * + * @author: wangchao + * @Date: 2025/4/25 16:22 + * @Description: TaskTemplateDefinitionMapper + * @since 7.0.0-RC2 + **/ +@Mapper +public interface TaskTemplateDefinitionMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsAzMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsAzMapper.java new file mode 100644 index 000000000..a338efcd6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsAzMapper.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsAzMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsAzMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.common.core.domain.entity.ops.OpsAzEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author lhf + * @date 2022/8/6 20:52 + **/ +@Mapper +public interface OpsAzMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsCheckMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsCheckMapper.java new file mode 100644 index 000000000..da19aeddf --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsCheckMapper.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsCheckMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsCheckMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.common.core.domain.entity.ops.OpsCheckEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author lhf + * @date 2022/11/13 17:16 + **/ +@Mapper +public interface OpsCheckMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsClusterMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsClusterMapper.java new file mode 100644 index 000000000..8f5f56b4c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsClusterMapper.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsClusterMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author lhf + * @date 2022/8/12 09:04 + **/ +@Mapper +public interface OpsClusterMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsClusterNodeMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsClusterNodeMapper.java new file mode 100644 index 000000000..234281024 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsClusterNodeMapper.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterNodeMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsClusterNodeMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author lhf + * @date 2022/8/18 09:14 + **/ +@Mapper +public interface OpsClusterNodeMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsEncryptionMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsEncryptionMapper.java new file mode 100644 index 000000000..59302a44b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsEncryptionMapper.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsEncryptionMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsEncryptionMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.common.core.domain.entity.ops.OpsEncryptionEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author lhf + * @date 2022/11/30 08:58 + **/ +@Mapper +public interface OpsEncryptionMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostMapper.java new file mode 100644 index 000000000..5190478d2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostMapper.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsHostMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.model.ops.host.OpsHostVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Set; + +/** + * @author lhf + * @date 2022/8/8 14:09 + **/ +@Mapper +public interface OpsHostMapper extends BaseMapper { + IPage pageHost(Page page, @Param("name") String name, @Param("tagIds") Set tagIds, @Param("os") String os, @Param("size") Integer size); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostTagMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostTagMapper.java new file mode 100644 index 000000000..8e830b8d7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostTagMapper.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsHostTagMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostTagMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostTagEntity; +import org.opengauss.admin.common.core.domain.model.ops.host.tag.HostTagPageVO; + +/** + * @author lhf + * @date 2023/3/14 23:37 + **/ +@Mapper +public interface OpsHostTagMapper extends BaseMapper { + IPage page(Page page, @Param("name") String name); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostTagRelMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostTagRelMapper.java new file mode 100644 index 000000000..a14e79126 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostTagRelMapper.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsHostTagRelMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostTagRelMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostTagRel; + +/** + * @author lhf + * @date 2023/3/14 23:38 + **/ +@Mapper +public interface OpsHostTagRelMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostUserMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostUserMapper.java new file mode 100644 index 000000000..7ff3803ba --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostUserMapper.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsHostUserMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsHostUserMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author lhf + * @date 2022/8/8 15:40 + **/ +@Mapper +public interface OpsHostUserMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsJdbcDbClusterMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsJdbcDbClusterMapper.java new file mode 100644 index 000000000..631286111 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsJdbcDbClusterMapper.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsJdbcDbClusterMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsJdbcDbClusterMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.common.core.domain.entity.ops.OpsJdbcDbClusterEntity; + +/** + * @author lhf + * @date 2023/1/13 11:06 + **/ +@Mapper +public interface OpsJdbcDbClusterMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsJdbcDbClusterNodeMapper.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsJdbcDbClusterNodeMapper.java new file mode 100644 index 000000000..952bfed55 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsJdbcDbClusterNodeMapper.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsJdbcDbClusterNodeMapper.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/mapper/ops/OpsJdbcDbClusterNodeMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.common.core.domain.entity.ops.OpsJdbcDbClusterNodeEntity; + +/** + * @author lhf + * @date 2023/1/13 11:06 + **/ +@Mapper +public interface OpsJdbcDbClusterNodeMapper extends BaseMapper { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/BasePluginTaskDetailDto.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/BasePluginTaskDetailDto.java new file mode 100644 index 000000000..8412f831b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/BasePluginTaskDetailDto.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BasePluginTaskDetailDto.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/BasePluginTaskDetailDto.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.beans; + +import lombok.Data; + +@Data +public abstract class BasePluginTaskDetailDto { + +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/MigrationTaskDetail.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/MigrationTaskDetail.java new file mode 100644 index 000000000..fa7fe2f49 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/MigrationTaskDetail.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MigrationTaskDetail.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/MigrationTaskDetail.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.beans; + +import lombok.Data; + +import java.util.Date; + +/** + * @className: MigrationTaskDetail + * @author: xielibo + * @date: 2023-01-18 13:58 + **/ +@Data +public class MigrationTaskDetail { + + private String sourceResourceId; + private String sourceResourceName; + private String sourceNodeId; + private String sourceDb; + private String sourceDbHost; + private String sourceDbPort; + private String sourceDbUser; + private String sourceDbPass; + private String sourceDbUrl; + + private String targetResourceId; + private String targetResourceName; + private String targetNodeId; + private String targetDb; + private String targetDbHost; + private String targetDbPort; + private String targetDbUser; + private String targetDbPass; + + private String runHostId; + private String runHost; + private String runPort; + private String runUser; + private String runPass; + + private String migrationOperations; + private String paramsConfig; + + private Integer execStatus; + private Integer mainTaskId; + + private Date createTime; + + private Date finishTime; + + private Date execTime; + + private String taskLog; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/MonitorToolsPromDto.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/MonitorToolsPromDto.java new file mode 100644 index 000000000..5a54bdf39 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/MonitorToolsPromDto.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MonitorToolsPromDto.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/MonitorToolsPromDto + * .java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.plugin.beans; + +import io.prometheus.metrics.core.metrics.Gauge; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * MonitorToolsPromDto + * + * @author: wangchao + * @Date: 2025/9/11 21:21 + * @since 7.0.0-RC2 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MonitorToolsPromDto { + private Gauge gauge; + private String gaugeName; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/PluginExtensionInfoDto.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/PluginExtensionInfoDto.java new file mode 100644 index 000000000..4f89c05c5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/PluginExtensionInfoDto.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2012-2022. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.plugin.beans; + +import java.util.Date; +import lombok.Data; +import org.opengauss.admin.common.enums.PluginLicenseType; + +/** + * PluginExtensionInfo + * + * @author YanHuan + * @since 2023-04-26 + */ +@Data +public class PluginExtensionInfoDto { + /** + * pluginId + */ + private String pluginId; + + /** + * pluginLicenseType + */ + private PluginLicenseType pluginLicenseType; + + /** + * pluginName + */ + private String pluginName; + + /** + * pluginIntroduction + */ + private String pluginIntroduction; + + /** + * pluginHome + */ + private String pluginHome; + + /** + * pluginActivationTime + */ + private Date pluginActivationTime; + + /** + * PluginExpirationTime + */ + private Date pluginExpirationTime; + + /** + * pluginDevelopmentCompany + */ + private String pluginDevelopmentCompany; + + /** + * phoneNumber + */ + private String phoneNumber; + + /** + * email + */ + private String email; + + /** + * companyAddress + */ + private String companyAddress; + + /** + * userGuide + */ + private String userGuide; + + /** + * demoAddress + */ + private String demoAddress; + + /** + * authAddress + */ + private String authAddress; + + /** + * customize + */ + private String customize; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/SshLogin.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/SshLogin.java new file mode 100644 index 000000000..bd5ef7fd3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/SshLogin.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SshLogin.java + * + * IDENTIFICATION + * visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/SshLogin.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.plugin.beans; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * HostLogin + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@Data +@AllArgsConstructor +public class SshLogin { + private String host; + private Integer port; + private String username; + private String password; + + @Override + public String toString() { + return "[host=" + host + ", username=" + username + "]"; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/TaskExecProgressDto.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/TaskExecProgressDto.java new file mode 100644 index 000000000..fa2098b11 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/TaskExecProgressDto.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * TaskExecProgressDto.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/beans/TaskExecProgressDto.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.beans; + +import lombok.Data; +import org.opengauss.admin.common.enums.SysTaskStatus; + +/** + * task execution progress + */ +@Data +public class TaskExecProgressDto { + + private Integer taskId; + + /** + * Task status + */ + private SysTaskStatus taskStatus; + + /** + * Task Execution Progress Percentage + */ + private Float execProgress; + +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/extract/PluginExtensionInfoExtract.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/extract/PluginExtensionInfoExtract.java new file mode 100644 index 000000000..012fc7c1d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/extract/PluginExtensionInfoExtract.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2012-2022. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.plugin.extract; + +import org.opengauss.admin.system.plugin.beans.PluginExtensionInfoDto; + +/** + * PluginExtensionInfoExtract + * + * @author YanHuan + * @since 2023-04-26 + */ +public interface PluginExtensionInfoExtract { + /** + * Get Plugin Extension Info + * + * @return 返回插件扩展信息 + */ + PluginExtensionInfoDto getPluginExtensionInfo(); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/extract/SocketExtract.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/extract/SocketExtract.java new file mode 100644 index 000000000..e1d5753e2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/extract/SocketExtract.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SocketExtract.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/extract/SocketExtract.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.extract; + +import jakarta.websocket.Session; + +/** + * @className: SocketExtract + * @description: Used to receive messages from clients + * @author: xielibo + * @date: 2022-11-14 21:33 + **/ +public interface SocketExtract { + + /** + * Connection success callback + * @param pluginId + * @param sessionId + * @param session + */ + public void onOpen(String pluginId, String sessionId, Session session); + + + /** + * Receive and process messages + * @param sessionId + * @param message + */ + public void processMessage(String sessionId, String message); + + /** + * Connection close callback + * @param sessionId + * @param sessionId + */ + public void onClose(String pluginId, String sessionId); + +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/extract/TaskExtract.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/extract/TaskExtract.java new file mode 100644 index 000000000..204eeae3e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/extract/TaskExtract.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * TaskExtract.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/extract/TaskExtract.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.extract; + +import org.opengauss.admin.system.plugin.beans.BasePluginTaskDetailDto; +import org.opengauss.admin.system.plugin.beans.MigrationTaskDetail; +import org.opengauss.admin.system.plugin.beans.TaskExecProgressDto; + +import java.util.List; + +/** + * @className: TaskExtract + * @description: Used to receive messages from clients + * @author: xielibo + * @date: 2022-11-14 21:33 + **/ +public interface TaskExtract { + + /** + * Get subtask details + * @param taskId + * @return + */ + public List getPluginTask(Integer taskId); + + /** + * Get the task status, summarize from the subtasks in the plug-in, + * the specific logic is implemented by the plug-in, and return the plug-in status enumeration defined by the platform + * @param taskId + * @return + */ + public TaskExecProgressDto getTaskStatus(Integer taskId); + + + /** + * start task + * @param taskId + * @return + */ + public void startTask(Integer taskId); + + + /** + * stop task + * @param taskId + * @return + */ + public void stopTask(Integer taskId); + + /** + * delete task + * @param taskId + * @return + */ + public void deleteTask(Integer taskId); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/AzFacade.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/AzFacade.java new file mode 100644 index 000000000..a51366b2f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/AzFacade.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AzFacade.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/AzFacade.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.facade; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsAzEntity; +import org.opengauss.admin.system.service.ops.IOpsAzService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; + +/** + * @author lhf + * @date 2022/11/20 21:09 + **/ +@Service +public class AzFacade { + @Autowired + private IOpsAzService opsAzService; + + public boolean add(OpsAzEntity az) { + return opsAzService.save(az); + } + + public List listByIds(Collection ids) { + return opsAzService.listByIds(ids); + } + + public OpsAzEntity getById(String id) { + return opsAzService.getById(id); + } + + /** + * List all AZs + * + * @return List of AZs + */ + public List listAll() { + return opsAzService.list(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/HostFacade.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/HostFacade.java new file mode 100644 index 000000000..e858f923e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/HostFacade.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostFacade.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/HostFacade.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.facade; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +import cn.hutool.core.collection.CollectionUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.model.ops.HostBody; +import org.opengauss.admin.common.core.domain.model.ops.host.OpsHostVO; +import org.opengauss.admin.common.core.dto.ops.ClusterNodeDto; +import org.opengauss.admin.system.service.ops.IHostService; +import org.opengauss.admin.system.service.ops.IOpsClusterNodeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; + +import static cn.hutool.core.util.StrUtil.isNotEmpty; + +/** + * @author lhf + * @date 2022/11/20 21:09 + **/ +@Slf4j +@Service +public class HostFacade { + @Autowired + private IHostService hostService; + @Autowired + private IOpsClusterNodeService opsClusterNodeService; + + public void add(HostBody hostBody) { + hostService.add(hostBody); + } + + public boolean ping(HostBody hostBody) { + return hostService.ping(hostBody); + } + + public boolean del(String hostId) { + return hostService.del(hostId); + } + + public boolean ping(String hostId, String rootPassword) { + return hostService.ping(hostId, rootPassword); + } + + public boolean edit(String hostId, HostBody hostBody) { + return hostService.edit(hostId, hostBody); + } + + public IPage pageHost(Page page, String name) { + return hostService.pageHost(page, name, null, null); + } + + public OpsHostEntity getById(String id) { + return hostService.getById(id); + } + + public List listByIds(Collection ids) { + return hostService.listByIds(ids); + } + + public Long count() { + return hostService.count(); + } + + public List listAll() { + return hostService.listAll(null); + } + + /** + * get host list + * + * @param hostname hostname + * @param publicIp public ip + * @return host list + */ + public List getHostList(String hostname, String publicIp) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostEntity.class); + queryWrapper.like(isNotEmpty(hostname), OpsHostEntity::getHostname, hostname); + queryWrapper.like(isNotEmpty(publicIp), OpsHostEntity::getPublicIp, publicIp); + return hostService.list(queryWrapper); + } + + /** + * query host list by condition + * + * @param os os + * @param osVersion osVersion + * @param cpuArch cpuArch + * @return host list + */ + public List getHostList(String os, String osVersion, String cpuArch) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostEntity.class); + queryWrapper.eq(isNotEmpty(os), OpsHostEntity::getOs, os); + queryWrapper.eq(isNotEmpty(osVersion), OpsHostEntity::getOsVersion, osVersion); + queryWrapper.eq(isNotEmpty(cpuArch), OpsHostEntity::getCpuArch, cpuArch); + List list = hostService.list(queryWrapper); + if (CollectionUtil.isNotEmpty(list)) { + list.forEach(item -> { + item.setDisplayIp(item.getDisplayIp()); + }); + } + return list; + } + + /** + * get ClusterNodeDto info by clusterNodeId + * + * @param clusterNodeId clusterNodeId + * @return ClusterNodeDto + */ + public ClusterNodeDto getClusterNodeByNodeId(String clusterNodeId) { + return opsClusterNodeService.getClusterNodeDtoByNodeId(clusterNodeId); + } + + /** + * get host info + * + * @param hostIp host public Ip + * @return host + */ + public OpsHostEntity getByPublicIp(String hostIp) { + return hostService.getByPublicIp(hostIp); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/HostMonitorFacade.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/HostMonitorFacade.java new file mode 100644 index 000000000..caac247d8 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/HostMonitorFacade.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostMonitorFacade.java + * + * IDENTIFICATION + * openGauss-visualtool + * /visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/HostMonitorFacade.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.plugin.facade; + +import org.opengauss.admin.system.service.HostMonitorCacheService; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; + +/** + * Host Monitor Cache Facade + * + * @author wangchao + * @since 2024/10/30 9:41 + **/ +@Service +public class HostMonitorFacade { + @Resource + private HostMonitorCacheService hostMonitorCacheService; + + /** + * Get the os of the remote machine + * + * @param hostId host id + * @return os + */ + public String getOs(String hostId) { + return hostMonitorCacheService.getOs(hostId); + } + + /** + * Get the os version of the remote machine + * + * @param hostId host id + * @return os version + */ + public String getOsVersion(String hostId) { + return hostMonitorCacheService.getOsVersion(hostId); + } + + /** + * Get the cpu architecture of the remote machine + * + * @param hostId host id + * @return cpu architecture + */ + public String getCpuArch(String hostId) { + return hostMonitorCacheService.getCpuArch(hostId); + } + + /** + * Get the cpu info of the remote machine + * + * @param hostId host id + * @param hasNetName true 包含网卡名称,false 不包含网卡名称 + * @return net info + */ + public String[] getNetMonitor(String hostId, boolean hasNetName) { + return hostMonitorCacheService.getNetMonitor(hostId, hasNetName); + } + + /** + * 获取cpu使用率 + * + * @param hostId host id + * @return cpu使用率 + */ + public String getCpuUsing(String hostId) { + return hostMonitorCacheService.getCpuUsing(hostId); + } + + /** + * 获取内存使用率 + * + * @param hostId host id + * @return 内存使用率 + */ + public String getMemoryUsing(String hostId) { + return hostMonitorCacheService.getMemoryUsing(hostId); + } + + /** + * get host cpu core number + * + * @param hostId host id + * @return cpu core num + */ + public String getCpuCoreNum(String hostId) { + return hostMonitorCacheService.getCpuCoreNum(hostId); + } + + /** + * get host cpu frequency eg. 2.4GHz + * + * @param hostId host id + * @return cpu frequency + */ + public String getCpuFrequency(String hostId) { + return hostMonitorCacheService.getCpuFrequency(hostId); + } + + /** + * 获取内存总量 + * + * @param hostId host id + * @return 内存总量 + */ + public String getMemoryTotal(String hostId) { + return hostMonitorCacheService.getMemoryTotal(hostId); + } + + /** + * 获取剩余内存 单位MB + * + * @param hostId hostId + * @return 剩余内存 + */ + public String getRemainingMemory(String hostId) { + return hostMonitorCacheService.getRemainingMemory(hostId); + } + + /** + * 获取可用磁盘总量 单位 GB + * + * @param hostId hostId + * @return 可用磁盘总量 + */ + public String getAvailableDiskSpace(String hostId) { + return hostMonitorCacheService.getAvailableDiskSpace(hostId); + } + + /** + * get migration host info cpu core num ,remaining memory, available disk space + * + * @param hostId host info + * @return migration host info + */ + public String getMigrationHostInfo(String hostId) { + return hostMonitorCacheService.getMigrationHostInfo(hostId); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/HostUserFacade.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/HostUserFacade.java new file mode 100644 index 000000000..8c4111cb1 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/HostUserFacade.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostUserFacade.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/HostUserFacade.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.facade; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.core.domain.model.ops.HostUserBody; +import org.opengauss.admin.system.service.ops.IHostService; +import org.opengauss.admin.system.service.ops.IHostUserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; + +/** + * @author lhf + * @date 2022/11/20 21:09 + **/ +@Slf4j +@Service +public class HostUserFacade { + + @Autowired + private IHostService hostService; + @Autowired + private IHostUserService hostUserService; + + public List listHostUserByHostId(String hostId) { + return hostUserService.listHostUserByHostId(hostId); + } + + public OpsHostUserEntity getHostUserByUsername(String hostId, String username) { + return hostUserService.getHostUserByUsername(hostId,username); + } + + public void removeByHostId(String hostId) { + hostUserService.removeByHostId(hostId); + } + + public List listHostUserByHostIdList(List hostIdList) { + return hostUserService.listHostUserByHostIdList(hostIdList); + } + + public void add(HostUserBody hostUserBody) { + OpsHostEntity host = hostService.getById(hostUserBody.getHostId()); + hostUserService.add(host, hostUserBody); + } + + public void edit(String hostUserId, HostUserBody hostUserBody) { + OpsHostEntity host = hostService.getById(hostUserBody.getHostId()); + hostUserService.edit(host, hostUserId, hostUserBody); + } + + public void del(String hostUserId) { + hostUserService.del(hostUserId); + } + + public OpsHostUserEntity getOmmUserByHostId(String hostId) { + return hostUserService.getOmmUserByHostId(hostId); + } + + public OpsHostUserEntity getRootUserByHostId(String hostId) { + return hostUserService.getRootUserByHostId(hostId); + } + + public void save(OpsHostUserEntity hostUserEntity) { + hostUserService.save(hostUserEntity); + } + + public OpsHostUserEntity getById(String id) { + return hostUserService.getById(id); + } + + public List listByIds(Collection ids) { + return hostUserService.listByIds(ids); + } + + public void removeByIds(Collection ids){ + hostUserService.removeByIds(ids); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/JdbcDbClusterFacade.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/JdbcDbClusterFacade.java new file mode 100644 index 000000000..eaed1648c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/JdbcDbClusterFacade.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JdbcDbClusterFacade.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/JdbcDbClusterFacade.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.facade; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterInputDto; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterVO; +import org.opengauss.admin.system.service.ops.IOpsJdbcDbClusterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author lhf + * @date 2023/1/14 22:22 + **/ +@Service +public class JdbcDbClusterFacade { + @Autowired + private IOpsJdbcDbClusterService opsJdbcDbClusterService; + + public void add(JdbcDbClusterInputDto clusterInput){ + opsJdbcDbClusterService.add(clusterInput); + } + + public Page page(String name, Page page) { + return opsJdbcDbClusterService.page(name, null, null, page); + } + + public Page page(String name,String ip,String type, Page page) { + return opsJdbcDbClusterService.page(name, ip, type, page); + } + + public List listAll(String type) { + return opsJdbcDbClusterService.listByType(type); + } + + public List listAll() { + return opsJdbcDbClusterService.listAll(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/JschExecutorFacade.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/JschExecutorFacade.java new file mode 100644 index 000000000..16c3bc99c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/JschExecutorFacade.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JschExecutorFacade.java + * + * IDENTIFICATION + * openGauss-visualtool + * /visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/JschExecutorFacade.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.plugin.facade; + +import org.opengauss.admin.system.plugin.beans.SshLogin; +import org.opengauss.admin.system.service.JschExecutorService; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; + +/** + * Jsch Executor Facade operations + * + * @author wangchao + * @since 2024/10/30 9:41 + **/ +@Service +public class JschExecutorFacade { + @Resource + private JschExecutorService jschExecutorService; + + /** + * 获取openGasuus版本号 + * + * @param sshLogin ssh会话 + * @param envPath 环境路径 + * @return openGasuus版本号 + */ + public String getOpenGaussMainVersionNum(SshLogin sshLogin, String envPath) { + return jschExecutorService.getOpenGaussMainVersionNum(sshLogin, envPath); + } + + /** + * 检查java版本号 + * + * @param sshLogin ssh登录信息 + * @return java版本号 + */ + public String checkJavaVersion(SshLogin sshLogin) { + return jschExecutorService.checkJavaVersion(sshLogin); + } + + /** + * 检查端口是否被占用 + * + * @param sshLogin ssh登录信息 + * @param port 端口号 + * @return true 未被占用,false 被占用 + * @author wangchao + * @date 2022/10/27 15:08 + * @since 7.0.0 + */ + public boolean checkOsPortConflict(SshLogin sshLogin, int port) { + return jschExecutorService.checkOsPortConflict(sshLogin, port); + } + + /** + * 执行命令 + * + * @param sshLogin ssh登录信息 + * @param command 命令 + * @return 命令执行结果 + */ + public String execCommand(SshLogin sshLogin, String command) { + return jschExecutorService.execCommand(sshLogin, command); + } + + /** + * 执行ssh远程命令,并获取返回值;执行命令时,需要指定环境变量 + * + * @param sshLogin ssh登录信息 + * @param command 命令 + * @param envPath 环境变量路径 + * @return 命令执行结果 + */ + public String execCommand(SshLogin sshLogin, String command, String envPath) { + return jschExecutorService.execCommand(sshLogin, command, envPath); + } + + /** + * 检查路径是否为空,为空返回true + * + * @param sshLogin ssh登录信息 + * @param path 路径 + * @return true 路径为空,false 路径不为空 + */ + public boolean checkPathEmpty(SshLogin sshLogin, String path) { + return jschExecutorService.checkPathEmpty(sshLogin, path); + } + + /** + * 检查文件是否存在,存在返回true + * + * @param sshLogin ssh登录信息 + * @param file 文件绝对路径 + * @return true 文件存在,false 文件不存在 + */ + public boolean checkFileExist(SshLogin sshLogin, String file) { + return jschExecutorService.checkFileExist(sshLogin, file); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/MenuFacade.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/MenuFacade.java new file mode 100644 index 000000000..1acd44ff2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/MenuFacade.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MenuFacade.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/MenuFacade.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.facade; + +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.constant.Constants; +import org.opengauss.admin.common.constant.UserConstants; +import org.opengauss.admin.common.core.domain.entity.SysMenu; +import org.opengauss.admin.common.core.vo.MenuVo; +import org.opengauss.admin.common.enums.SysMenuPluginComponent; +import org.opengauss.admin.common.enums.SysMenuPluginOpenWay; +import org.opengauss.admin.common.enums.SysMenuRouteOpenPosition; +import org.opengauss.admin.common.enums.SysMenuStatus; +import org.opengauss.admin.common.enums.SysMenuVisible; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.system.domain.SysPlugin; +import org.opengauss.admin.system.domain.SysPluginLogo; +import org.opengauss.admin.system.service.ISysMenuService; +import org.opengauss.admin.system.service.ISysPluginLogoService; +import org.opengauss.admin.system.service.ISysPluginService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; + +/** + * @className: MenuFacade + * @description: Menu service provided to plugins. + * @author: xielibo + * @date: 2022-09-05 6:59 PM + **/ +@Service +@Slf4j +public class MenuFacade { + + @Autowired + private ISysMenuService sysMenuService; + @Autowired + private ISysPluginService sysPluginService; + @Autowired + private ISysPluginLogoService sysPluginLogoService; + + private MenuVo saveMenu(String pluginId, String menuName,String menuEnName, Integer order, String path, Integer parentId, Integer openWay, String visible, Integer openPosition, String queryTemplate) { + if (path == null) { + path = ""; + } else { + path = Constants.PLUGIN_PATH_PREFIX + "/" + pluginId + "/" + path; + } + log.info("call save menu, pluginId={}, menuName={}, path={}, parentId={}", pluginId, menuName, path, parentId); + SysMenu menu = sysMenuService.existsMenu(parentId, null, null, menuName); + if (menu == null) { + menu = new SysMenu(); + menu.setVisible(visible); + menu.setMenuName(menuName); + menu.setMenuEnName(menuEnName); + if (parentId == null) { + parentId = 0; + } + menu.setPluginId(pluginId); + menu.setParentId(parentId); + menu.setOpenWay(openWay); + menu.setPath(path); + if (openWay == null || SysMenuPluginOpenWay.PAGE_OPEN.getCode().equals(openWay)) { + menu.setComponent(SysMenuPluginComponent.PAGE_IFRAME.getCode()); + } else { + menu.setComponent(SysMenuPluginComponent.WIN_IFRAME.getCode()); + } + menu.setOrderNum(order + 10000); + menu.setMenuType(UserConstants.TYPE_MENU); + menu.setOpenPosition(openPosition); + menu.setQueryTemplate(queryTemplate); + menu.setCreateTime(new Date()); + SysPlugin plugin = sysPluginService.getByPluginId(pluginId); + if (plugin != null) { + if (StringUtils.isNotBlank(plugin.getTheme())) { + menu.setPluginTheme(plugin.getTheme()); + } + SysPluginLogo logo = sysPluginLogoService.getByPluginId(pluginId); + if (logo != null && StringUtils.isNotBlank(logo.getLogoPath())) { + menu.setIcon(logo.getLogoPath()); + } + } + sysMenuService.save(menu); + if (parentId != 0) { + SysMenu sysMenu = new SysMenu(); + sysMenu.setMenuId(parentId); + sysMenu.setMenuType(UserConstants.TYPE_DIR); + sysMenuService.updateMenu(sysMenu); + } + } else { + menu.setStatus(SysMenuStatus.ENABLE.getCode()); + SysPlugin plugin = sysPluginService.getByPluginId(pluginId); + if (plugin != null) { + if (StringUtils.isNotBlank(plugin.getTheme())) { + menu.setPluginTheme(plugin.getTheme()); + } + SysPluginLogo logo = sysPluginLogoService.getByPluginId(pluginId); + if (logo != null && StringUtils.isNotBlank(logo.getLogoPath())) { + menu.setIcon(logo.getLogoPath()); + } + } + sysMenuService.updateMenu(menu); + } + return menu.toMenuVo(); + } + + /** + * save menu and router + * @param pluginId + * @param menuName + */ + public MenuVo savePluginMenu(String pluginId, String menuName, String menuEnName, Integer order) { + return saveMenu(pluginId, menuName, menuEnName, order, "index", 0, SysMenuPluginOpenWay.PAGE_OPEN.getCode(), SysMenuVisible.SHOW.getCode(), null, null); + } + + /** + * save menu and router + * @param pluginId + * @param menuName + * @param path + */ + public MenuVo savePluginMenu(String pluginId, String menuName, String menuEnName, Integer order, String path) { + return saveMenu(pluginId, menuName, menuEnName, order, path, 0, SysMenuPluginOpenWay.PAGE_OPEN.getCode(), SysMenuVisible.SHOW.getCode(), null, null); + } + + /** + * save menu and router + * @param pluginId + * @param menuName + * @param parentId + */ + public MenuVo savePluginMenu(String pluginId, String menuName, String menuEnName, Integer order, String path, Integer parentId) { + return saveMenu(pluginId, menuName, menuEnName, order, path, parentId, SysMenuPluginOpenWay.PAGE_OPEN.getCode(), SysMenuVisible.SHOW.getCode(), null, null); + } + /** + * save menu and router + * @param pluginId + * @param menuName + * @param parentId + * @param openWay + */ + public MenuVo savePluginMenu(String pluginId, String menuName, String menuEnName, Integer order, String path, Integer parentId, Integer openWay) { + return saveMenu(pluginId, menuName, menuEnName,order, path, parentId, openWay, SysMenuVisible.SHOW.getCode(), null, null); + } + + /** + * save router + * @param pluginId + * @param menuName + * @param position 2 index instance + */ + private MenuVo savePluginRoute(String pluginId, String menuName,String menuEnName, String path, Integer position,String queryTemplate) { + return saveMenu(pluginId, menuName,menuEnName, 500, path, 0, SysMenuPluginOpenWay.PAGE_OPEN.getCode(), SysMenuVisible.HIDE.getCode(), position, queryTemplate); + } + + /** + * save router + * @param pluginId + * @param menuName + * @param position 2 index instance + */ + public MenuVo savePluginRoute(String pluginId, String menuName, String path, Integer parentId) { + return saveMenu(pluginId, menuName,"", 500, path, parentId, SysMenuPluginOpenWay.PAGE_OPEN.getCode(), SysMenuVisible.HIDE.getCode(), SysMenuRouteOpenPosition.INDEX_INSTANCE_DATA.getCode(), null); + } + + /** + * save router + * @param pluginId + * @param menuName + * @param position 2 index instance + */ + public MenuVo savePluginRoute(String pluginId, String menuName,String menuEnName, String path, Integer parentId) { + return saveMenu(pluginId, menuName,menuEnName, 500, path, parentId, SysMenuPluginOpenWay.PAGE_OPEN.getCode(), SysMenuVisible.HIDE.getCode(), SysMenuRouteOpenPosition.INDEX_INSTANCE_DATA.getCode(), null); + } + + /** + * save index router + * @param pluginId + * @param menuName + * @param path + * @return + */ + public MenuVo saveIndexInstanceRoute(String pluginId, String menuName, String path) { + String queryTemplate = "?id=${id}"; + return savePluginRoute(pluginId, menuName,"", path, SysMenuRouteOpenPosition.INDEX_INSTANCE_DATA.getCode(),queryTemplate); + } + + /** + * delete menu + * + * @param pluginId pluginId + */ + public void deletePluginMenu(String pluginId) { + sysMenuService.updateDisableByPluginId(pluginId); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/OpsFacade.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/OpsFacade.java new file mode 100644 index 000000000..7c766e0a6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/OpsFacade.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsFacade.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/OpsFacade.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.facade; + +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO; +import org.opengauss.admin.common.core.domain.model.ops.check.CheckSummaryVO; +import org.opengauss.admin.system.service.ops.IOpsClusterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author lhf + * @date 2022/10/10 13:35 + **/ +@Service +public class OpsFacade { + + @Autowired + private IOpsClusterService opsClusterService; + + public List listCluster() { + return opsClusterService.listCluster(); + } + + public CheckSummaryVO check(String clusterId, String rootPassword){ + return opsClusterService.check(clusterId, rootPassword); + } + + /** + * determine whether the node is in ops cluster + * + * @param nodeId node id + * @return boolean + */ + public boolean isNodeInOpsCluster(String nodeId) { + return opsClusterService.listCluster().stream() + .map(OpsClusterVO::getClusterNodes) + .flatMap(List::stream) + .anyMatch(node -> nodeId.equals(node.getNodeId())); + } + + /** + * get opsClusterVO by node id + * + * @param nodeId node id + * @return OpsClusterVO + */ + public OpsClusterVO getOpsClusterVOByNodeId(String nodeId) { + for (OpsClusterVO opsClusterVO : listCluster()) { + if (opsClusterVO.getClusterNodes().stream().anyMatch(node -> nodeId.equals(node.getNodeId()))) { + return opsClusterVO; + } + } + return null; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/PluginFacade.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/PluginFacade.java new file mode 100644 index 000000000..a46dbfa7a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/PluginFacade.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * PluginFacade.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/PluginFacade.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.facade; + +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.system.service.ISysPluginConfigDataService; +import org.opengauss.admin.system.service.ISysPluginConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @className: PluginFacade + * @description: Plugin service provided to plugins. + * @author: xielibo + * @date: 2022-09-05 6:59 PM + **/ +@Service +public class PluginFacade { + + @Autowired + private ISysPluginConfigService sysPluginConfigService; + @Autowired + private ISysPluginConfigDataService sysPluginConfigDataService; + + /** + * Get plugin configuration data + * @param pluginId + * @return + */ + public String getPluginConfigData(String pluginId){ + if (StringUtils.isNotNull(pluginId)) { + return sysPluginConfigDataService.getDataByPluginId(pluginId); + } + return null; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/SysSettingFacade.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/SysSettingFacade.java new file mode 100644 index 000000000..96486730a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/SysSettingFacade.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysSettingFacade.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/SysSettingFacade.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.facade; + +import org.opengauss.admin.common.core.domain.entity.SysSettingEntity; +import org.opengauss.admin.system.service.ISysSettingService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.net.Proxy; + +/** + * @author wangyl + * @date 2023/3/8 09:22 + **/ +@Service +public class SysSettingFacade { + @Autowired + private ISysSettingService sysSettingService; + + public SysSettingEntity getSysSetting(Integer userId) { + return sysSettingService.getSetting(userId); + } + + /** + * create http proxy by system config: server.proxy.hostname server.proxy.port + * + * @return proxy + */ + public Proxy getSysNetProxy() { + return sysSettingService.getSysNetProxy(); + } + + public boolean checkUploadPath(String path, Integer userId) { + return sysSettingService.hasUploadPath(path, userId); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/TaskFacade.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/TaskFacade.java new file mode 100644 index 000000000..fa724bcda --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/TaskFacade.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * TaskFacade.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/TaskFacade.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.facade; + +import org.opengauss.admin.common.enums.SysTaskStatus; +import org.opengauss.admin.common.enums.SysTaskType; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.system.domain.SysTask; +import org.opengauss.admin.system.service.ISysTaskService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; + +/** + * @className: TaskFacade + * @description: Task service provided to plugins. + * @author: xielibo + * @date: 2023-01-13 8:59 PM + **/ +@Service +public class TaskFacade { + + @Autowired + private ISysTaskService sysTaskService; + + /** + * Add tasks to the platform task list + * @param pluginId + * @return taskId + */ + public Integer saveTask(String pluginId, String hostId, String taskName, SysTaskType taskType, Integer subTaskCount){ + SysTask task = new SysTask(); + task.setPluginId(pluginId); + task.setTaskType(taskType.getCode()); + task.setTaskName(taskName); + task.setExecHostId(hostId); + task.setCreateTime(new Date()); + task.setExecStatus(SysTaskStatus.CREATED.getCode()); + task.setSubTaskCount(subTaskCount); + sysTaskService.save(task); + return task.getId(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/WsFacade.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/WsFacade.java new file mode 100644 index 000000000..b1d26f989 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/WsFacade.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WsFacade.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/plugin/facade/WsFacade.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.plugin.facade; + +import org.opengauss.admin.system.service.IWebSocketService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @className: WsFacade + * @description: process websocket messages provided to plugins. + * @author: xielibo + * @date: 2022-11-15 12:49 + **/ +@Slf4j +@Service +public class WsFacade { + + @Autowired + private IWebSocketService wsService; + + /** + * send messages to clients + * @param pluginId + * @param sessionId + * @param message + */ + public void sendMessage(String pluginId, String sessionId, String message) { + log.debug("call to send message. plugin: {}, sessionId: {}, message: {}", pluginId, sessionId, message); + wsService.sendMessage(pluginId, sessionId, message); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostBasicService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostBasicService.java new file mode 100644 index 000000000..9055ea543 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostBasicService.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.service; + +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.constant.AgentConstants; +import org.opengauss.admin.common.core.domain.model.agent.HostBaseInfo; +import org.opengauss.admin.system.service.ops.IHostService; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +import jakarta.annotation.Resource; + +/** + * HostMonitorCacheService + * + * @author wangchao + * @since 2024/10/29 09:26 + */ +@Slf4j +@Service +public class HostBasicService { + private final Map hostBasicMap = new ConcurrentHashMap<>(); + private final CheckValueValid valueChecker = (key, value) -> switch (key) { + case AgentConstants.HostMetric.CPU_CORE_NUM -> value.matches("\\d+") && Integer.parseInt(value) > 0; + case AgentConstants.HostMetric.CPU_FREQUENCY -> value.matches("\\d+") && Long.parseLong(value) > 0; + default -> true; + }; + + @Resource + private IHostService hostService; + + /** + * get host basic info map by host id + * + * @param hostId host id + * @return host basic info map + */ + public Map getHostBasicInfoMap(String hostId) { + HostBaseInfo hostBasicInfo = getHostBasicInfo(hostId); + Map hostInfoMap = new HashMap<>(); + if (Objects.isNull(hostBasicInfo)) { + return hostInfoMap; + } + updateHostMonitorCache(hostInfoMap, hostBasicInfo); + return hostInfoMap; + } + + /** + * get host basic info by host id + * + * @param hostId host id + * @return host basic info + */ + public HostBaseInfo getHostBasicInfo(String hostId) { + return hostBasicMap.compute(hostId, (k, v) -> { + if (v == null) { + return convertHostBasicInfo(hostId).orElse(null); + } else { + return v; + } + }); + } + + /** + * refresh host basic info by host id + * + * @param hostId host id + */ + public void refreshCacheOfHostBasicInfo(String hostId) { + hostBasicMap.compute(hostId, (k, v) -> convertHostBasicInfo(hostId).orElse(null)); + } + + private Optional convertHostBasicInfo(String hostId) { + return Optional.ofNullable(hostService.getById(hostId)) + .map(host -> HostBaseInfo.builder() + .hostName(host.getHostname()) + .agentId(host.getHostId()) + .cpuArchitecture(host.getCpuArch()) + .cpuModel(host.getCpuModel()) + .cpuFreq(host.getCpuFreq()) + .physicalCores(host.getPhysicalCores()) + .logicalCores(host.getLogicalCores()) + .osName(host.getOs()) + .osVersion(host.getOsVersion()) + .osBuild(host.getOsBuild()) + .build()); + } + + /** + * update HostMonitorCacheService + * + * @param hostInfoMap host info map + * @param hostBaseInfo hostBaseInfo + */ + public void updateHostMonitorCache(Map hostInfoMap, HostBaseInfo hostBaseInfo) { + putIfValid(hostInfoMap, AgentConstants.HostMetric.CPU_ARCH, hostBaseInfo.getCpuArchitecture()); + putIfValid(hostInfoMap, AgentConstants.HostMetric.CPU_CORE_NUM, String.valueOf(hostBaseInfo.getLogicalCores())); + String cpuFreq = hostBaseInfo.getCpuFreq(); + if (StrUtil.isNotEmpty(cpuFreq) && cpuFreq.matches(AgentConstants.HostMetric.CPU_FREQ_REGEX)) { + String freqValue = hostBaseInfo.getCpuFreq() + "GHz"; + if (StrUtil.isNotEmpty(freqValue)) { + hostInfoMap.put(AgentConstants.HostMetric.CPU_FREQUENCY, freqValue); + } + } else { + hostInfoMap.put(AgentConstants.HostMetric.CPU_FREQUENCY, "Unknown Frequency Of lscpu cmd"); + } + putIfValid(hostInfoMap, AgentConstants.HostMetric.OS_NAME, hostBaseInfo.getOsName()); + putIfValid(hostInfoMap, AgentConstants.HostMetric.OS_VERSION, hostBaseInfo.getOsVersion()); + } + + private void putIfValid(Map map, String key, String value) { + if (StrUtil.isNotEmpty(value) && valueChecker.checked(key, value)) { + map.put(key, value); + } + } + + /** + * delete host cache + * + * @param hostId host id + */ + public void deleteHostCache(String hostId) { + hostBasicMap.remove(hostId); + } + + @FunctionalInterface + private interface CheckValueValid { + /** + * check value is valid + * + * @param key key + * @param value value + * @return true if valid + */ + boolean checked(String key, String value); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostMonitorAgentService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostMonitorAgentService.java new file mode 100644 index 000000000..e7cb212dc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostMonitorAgentService.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostMonitorCacheService.java + * + * IDENTIFICATION + * openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostMonitorCacheService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.service; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.constant.AgentConstants; +import org.opengauss.admin.common.core.domain.entity.agent.MetricRealTime; +import org.opengauss.admin.common.utils.MathUtils; +import org.opengauss.agent.repository.MetricRealTimeStorageService; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import jakarta.annotation.Resource; + +/** + * HostMonitorCacheService + * + * @author wangchao + * @since 2024/10/29 09:26 + */ +@Slf4j +@Service +public class HostMonitorAgentService { + @Resource + private MetricRealTimeStorageService metricRealTimeStorageService; + @Resource + private HostBasicService hostBasicService; + + /** + * start agent host monitor scheduled + * + * @param cacheMap cache map + * @return agent id set + */ + public Set startAgentHostMonitorScheduled(Map> cacheMap) { + // host dynamic info,support seconds level data refresh + List list = metricRealTimeStorageService.list(); + Map> agentMetricsMap = list.stream() + .collect(Collectors.groupingBy(metric -> String.valueOf(metric.getAgentId()), Collectors.toList())); + agentMetricsMap.forEach((hostId, metrics) -> { + cacheMap.compute(hostId, (k, v) -> { + if (Objects.isNull(v)) { + v = new HashMap<>(); + } + // cache host basic info from local db cache ops_host + v.putAll(hostBasicService.getHostBasicInfoMap(k)); + // if agent metrics has current host id, update cache + if (agentMetricsMap.containsKey(hostId)) { + v.putAll(refreshHostMonitorCache(agentMetricsMap.get(hostId))); + } + return v; + }); + }); + return agentMetricsMap.keySet(); + } + + private Map refreshHostMonitorCache(List metrics) { + Map cache = new HashMap<>(); + for (MetricRealTime metric : metrics) { + switch (metric.getMetric()) { + case AgentConstants.HostMetric.SYSTEM_CPU_USAGE, AgentConstants.HostMetric.SYSTEM_MEMORY_USAGE, + AgentConstants.HostMetric.SYSTEM_MEMORY_AVAILABLE, AgentConstants.HostMetric.SYSTEM_MEMORY_TOTAL -> + cache.put(metric.getMetric(), MathUtils.formatScaleTwo(metric.getValue())); + case AgentConstants.HostMetric.SYSTEM_NETWORK_RECEIVED -> + accMergesMetricsValue(cache, AgentConstants.HostMetric.SYSTEM_NETWORK_RECEIVED, metric.getValue()); + case AgentConstants.HostMetric.SYSTEM_NETWORK_SENT -> + accMergesMetricsValue(cache, AgentConstants.HostMetric.SYSTEM_NETWORK_SENT, metric.getValue()); + case AgentConstants.HostMetric.SYSTEM_DISK_TOTAL -> + accMergesMetricsValue(cache, AgentConstants.HostMetric.SYSTEM_DISK_TOTAL, metric.getValue()); + case AgentConstants.HostMetric.SYSTEM_DISK_USED -> + accMergesMetricsValue(cache, AgentConstants.HostMetric.SYSTEM_DISK_USED, metric.getValue()); + case AgentConstants.HostMetric.SYSTEM_DISK_FREE -> + accMergesMetricsValue(cache, AgentConstants.HostMetric.SYSTEM_DISK_FREE, metric.getValue()); + default -> { + log.debug("unknown metric:{}", metric.getMetric()); + } + } + } + String networkName = extractNetworkInterfaceName(metrics); + refreshHostNetWork(cache, networkName); + refreshHostDiskUsage(cache); + return cache; + } + + private String extractNetworkInterfaceName(List metrics) { + return metrics.stream() + .filter(metric -> metric.getMetric().equalsIgnoreCase(AgentConstants.HostMetric.SYSTEM_NETWORK_SENT)) + .map(MetricRealTime::getProperty) + .filter(prop -> StrUtil.isNotEmpty(prop) && !StrUtil.equalsIgnoreCase(prop, "none")) + .distinct() + .collect(Collectors.joining(":")); + } + + private void refreshHostNetWork(Map cache, String networkName) { + if (cache.containsKey(AgentConstants.HostMetric.SYSTEM_NETWORK_RECEIVED) && cache.containsKey( + AgentConstants.HostMetric.SYSTEM_NETWORK_SENT)) { + String netMonitor = networkName + "|" + cache.get(AgentConstants.HostMetric.SYSTEM_NETWORK_SENT) + "|" + + cache.get(AgentConstants.HostMetric.SYSTEM_NETWORK_RECEIVED); + cache.put(AgentConstants.HostMetric.NET_MONITOR, netMonitor); + } + } + + /** + * get agent host info + * + * @param hostId host id + * @return map + */ + public Map getAgentHostInfo(String hostId) { + List list = metricRealTimeStorageService.listAgentHostInfo(hostId); + if (CollUtil.isEmpty(list)) { + return new HashMap<>(); + } + return refreshHostMonitorCache(list); + } + + private static String refreshHostDiskUsage(Map cache) { + return cache.compute(AgentConstants.HostMetric.SYSTEM_DISK_USAGE, + (k, v) -> MathUtils.percent(cache.get(AgentConstants.HostMetric.SYSTEM_DISK_USED), + cache.get(AgentConstants.HostMetric.SYSTEM_DISK_TOTAL))); + } + + private static void accMergesMetricsValue(Map cache, String key, String metricValue) { + cache.compute(key, (k, v) -> { + if (v == null) { + return metricValue; + } else { + return MathUtils.add(v, metricValue); + } + }); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostMonitorCacheService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostMonitorCacheService.java new file mode 100644 index 000000000..bf9c13328 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostMonitorCacheService.java @@ -0,0 +1,334 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostMonitorCacheService.java + * + * IDENTIFICATION + * openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostMonitorCacheService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.service; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.constant.AgentConstants; +import org.opengauss.admin.common.core.domain.entity.agent.TaskMetricsDefinition; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import jakarta.annotation.Resource; + +/** + * HostMonitorCacheService + * + * @author wangchao + * @since 2024/10/29 09:26 + */ +@Slf4j +@Component +public class HostMonitorCacheService { + // cacheMap : hostId : item : value + private final Map> cacheMap = new ConcurrentHashMap<>(); + private volatile AtomicLong lastedFetch = new AtomicLong(0); + @Resource + private HostMonitorSshService hostMonitorSshService; + @Resource + private HostMonitorAgentService hostMonitorAgentService; + @Resource + private ScheduledExecutorService scheduledExecutorService; + @Resource + private HostBasicService hostBasicService; + private final List metricsDefinitionList = new LinkedList<>(); + + /** + * init host monitor cache environment + * + * @param metricsDefinitionList metricsDefinitionList + */ + public void initHostMonitorCacheEnvironment(List metricsDefinitionList) { + if (ArrayUtil.isEmpty(metricsDefinitionList)) { + return; + } + this.metricsDefinitionList.addAll(metricsDefinitionList); + } + + /** + * init host cache scheduled + *
+     * Initialization Dependency
+     * The scheduled task (startHostMonitorScheduled) requires HostMonitorAgentService to be fully initialized before
+     * execution.
+     * If triggered prematurely, cryptographic components (e.g., EncryptionUtils) may not be ready,
+     * leading to decryption failures.
+     *
+     * Authentication Cascade Failure
+     * Failed decryption → authentication errors during session pool creation.
+     * Repeated errors → security policies (e.g., account lockout after N attempts) may activate.
+     *
+     * Irreversible Session Lock
+     * Once an account is locked subsequent successful decryption cannot resolve the lock.
+     * Manual intervention (e.g., unlocking the account) is required
+     * 
+ */ + public void startHostMonitorScheduled() { + lastedFetch.set(System.currentTimeMillis() / 1000); + // host dynamic info,support seconds level data refresh + scheduledExecutorService.scheduleWithFixedDelay(() -> { + Thread.currentThread().setName("host-realtime-monitor"); + if (noUsing()) { + return; + } + try { + Set agentSet = hostMonitorAgentService.startAgentHostMonitorScheduled(cacheMap); + hostMonitorSshService.startHostMonitorScheduled(cacheMap, agentSet); + log.info("host monitor cache scheduled "); + } catch (Exception e) { + log.error("host monitor cache scheduled error", e); + } + }, 5, 5, TimeUnit.SECONDS); + } + + private synchronized String getCacheValue(String hostId, String key) { + lastedFetch.set(System.currentTimeMillis() / 1000); + // cache does not have host info, try to get from agent + if (!cacheMap.containsKey(hostId)) { + refreshCache(hostId, hostMonitorAgentService.getAgentHostInfo(hostId)); + } + // cache does not have host info, try to get from ssh + if (!cacheMap.containsKey(hostId)) { + refreshCache(hostId, hostBasicService.getHostBasicInfoMap(hostId)); + } + // cache does not have host info, return empty + if (!cacheMap.containsKey(hostId)) { + return ""; + } + // cache has host info, try to get value + Map hostInfoMap = cacheMap.get(hostId); + if (!hostInfoMap.containsKey(key)) { + refreshCache(hostId, key, hostMonitorSshService.getSshHostSingleInfo(hostId, key)); + refreshCache(hostId, hostMonitorSshService.getSshHostInfo(hostId, key)); + } + return cacheMap.get(hostId).getOrDefault(key, ""); + } + + private void refreshCache(String hostId, Map hostInfoMap) { + cacheMap.compute(hostId, (k, v) -> { + if (v == null) { + v = new HashMap<>(); + } + v.putAll(hostInfoMap); + return v; + }); + } + + private void refreshCache(String hostId, String key, String value) { + cacheMap.compute(hostId, (k, v) -> { + if (v == null) { + v = new HashMap<>(); + } + if (StrUtil.isNotEmpty(value)) { + v.put(key, value); + } + return v; + }); + } + + private boolean noUsing() { + long currentSecond = System.currentTimeMillis() / 1000; + return (currentSecond - lastedFetch.get()) > 60; + } + + /** + * delete host cache + * + * @param hostId host id + */ + public void deleteHostCache(String hostId) { + cacheMap.remove(hostId); + hostBasicService.deleteHostCache(hostId); + } + + /** + * get host os information + * + * @param hostId host id + * @return host os information + */ + public String getOs(String hostId) { + return getCacheValue(hostId, AgentConstants.HostMetric.OS_NAME); + } + + private boolean isNotCache(String hostId) { + return !cacheMap.containsKey(hostId); + } + + /** + * get host os version information + * + * @param hostId host id + * @return host os version information + */ + public String getOsVersion(String hostId) { + return getCacheValue(hostId, AgentConstants.HostMetric.OS_VERSION); + } + + /** + * get host cpu core number + * + * @param hostId host id + * @return host cpu core number + */ + public String getCpuCoreNum(String hostId) { + return getCacheValue(hostId, AgentConstants.HostMetric.CPU_CORE_NUM); + } + + /** + * get host cpu architecture information + * + * @param hostId host id + * @return host cpu architecture information + */ + public String getCpuArch(String hostId) { + return getCacheValue(hostId, AgentConstants.HostMetric.CPU_ARCH); + } + + /** + * get host cpu using + * + * @param hostId host id + * @return host cpu using + */ + public String getCpuUsing(String hostId) { + return getCacheValue(hostId, AgentConstants.HostMetric.CPU_USING); + } + + /** + * get host cpu frequency + * + * @param hostId host id + * @return host cpu frequency + */ + public String getCpuFrequency(String hostId) { + return getCacheValue(hostId, AgentConstants.HostMetric.CPU_FREQUENCY); + } + + /** + * get host network information tx and rx + * + * @param hostId host id + * @param hasNetName if has net name + * @return host network information tx and rx + */ + public String[] getNetMonitor(String hostId, boolean hasNetName) { + String[] res = hasNetName ? new String[3] : new String[2]; + Arrays.fill(res, "-1"); + String netMonitor = getCacheValue(hostId, AgentConstants.HostMetric.NET_MONITOR); + String[] netRes = netMonitor.split(CacheConstants.SPL); + if (netRes.length == 3) { + return hasNetName ? netRes : new String[] {netRes[1], netRes[2]}; + } else { + return res; + } + } + + /** + * get host memory using percent + * + * @param hostId host id + * @return host memory using + */ + public String getMemoryUsing(String hostId) { + return getCacheValue(hostId, AgentConstants.HostMetric.SYSTEM_MEMORY_USAGE); + } + + /** + * get host memory remaining unit G + * + * @param hostId host id + * @return host memory remaining + */ + public String getRemainingMemory(String hostId) { + return getCacheValue(hostId, AgentConstants.HostMetric.SYSTEM_MEMORY_AVAILABLE); + } + + /** + * get host memory total unit G + * + * @param hostId host id + * @return host memory total + */ + public String getMemoryTotal(String hostId) { + return getCacheValue(hostId, AgentConstants.HostMetric.SYSTEM_MEMORY_TOTAL); + } + + /** + * get migration host info cpu core num ,remaining memory, available disk space + * + * @param hostId hostId + * @return migration host info + */ + public String getMigrationHostInfo(String hostId) { + return getCacheValue(hostId, CacheConstants.MIGRATION_HOST).replaceAll("##", "\n"); + } + + /** + * get host disk available unit GB + * + * @param hostId host id + * @return host disk total + */ + public String getAvailableDiskSpace(String hostId) { + return getCacheValue(hostId, AgentConstants.HostMetric.SYSTEM_DISK_FREE); + } + + /** + * get host disk use percent + * + * @param hostId host id + * @return host disk percent + */ + public String getDiskUse(String hostId) { + return getCacheValue(hostId, AgentConstants.HostMetric.SYSTEM_DISK_USAGE); + } + + /** + * host cache item + */ + interface CacheConstants { + /** + * migration Host + */ + String MIGRATION_HOST = "migrationHost"; + + /** + * spliter | + */ + String SPL = "\\|"; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostMonitorSshService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostMonitorSshService.java new file mode 100644 index 000000000..fc7f387a1 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostMonitorSshService.java @@ -0,0 +1,421 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostMonitorCacheService.java + * + * IDENTIFICATION + * openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/HostMonitorCacheService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.service; + +import static org.opengauss.admin.common.constant.AgentConstants.HostMetric.CPU_ARCH; +import static org.opengauss.admin.common.constant.AgentConstants.HostMetric.CPU_CORE_NUM; +import static org.opengauss.admin.common.constant.AgentConstants.HostMetric.CPU_FREQUENCY; +import static org.opengauss.admin.common.constant.AgentConstants.HostMetric.MIGRATION_HOST; +import static org.opengauss.admin.common.constant.AgentConstants.HostMetric.OS_NAME; +import static org.opengauss.admin.common.constant.AgentConstants.HostMetric.OS_VERSION; +import static org.opengauss.admin.common.constant.AgentConstants.HostMetric.SYSTEM_DISK_FREE; +import static org.opengauss.admin.common.constant.AgentConstants.HostMetric.SYSTEM_DISK_TOTAL; +import static org.opengauss.admin.common.constant.AgentConstants.HostMetric.SYSTEM_DISK_USAGE; +import static org.opengauss.admin.common.constant.AgentConstants.HostMetric.SYSTEM_DISK_USED; +import static org.opengauss.admin.common.constant.AgentConstants.HostMetric.SYSTEM_MEMORY_AVAILABLE; +import static org.opengauss.admin.common.constant.AgentConstants.HostMetric.SYSTEM_MEMORY_TOTAL; +import static org.opengauss.admin.common.constant.AgentConstants.HostMetric.SYSTEM_MEMORY_USAGE; +import static org.opengauss.admin.system.service.HostMonitorSshService.CacheConstants.DISK_MONITOR; + +import com.alibaba.fastjson.JSONObject; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.constant.AgentConstants; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.core.domain.model.agent.HostBaseInfo; +import org.opengauss.admin.common.core.vo.HostRealtimeStatistics; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.system.plugin.beans.SshLogin; +import org.opengauss.admin.system.service.ops.IHostService; +import org.opengauss.admin.system.service.ops.IHostUserService; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.context.annotation.DependsOn; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; + +import jakarta.annotation.Resource; + +/** + * HostMonitorCacheService + * + * @author wangchao + * @since 2024/10/29 09:26 + */ +@Slf4j +@Component +@DependsOn("encryptionUtils") +public class HostMonitorSshService { + @Resource + private IHostService hostService; + @Resource + private IHostUserService hostUserService; + @Resource + private EncryptionUtils encryptionUtils; + @Resource + private JschExecutorService jschExecutorService; + @Resource + private HostBasicService hostBasicService; + private ExecutorService customPool = Executors.newFixedThreadPool(4); + private Semaphore semaphore = new Semaphore(4); + + /** + * start host monitor scheduled + * + * @param cacheMap host cache map + * @param agentSet agent set + */ + public void startHostMonitorScheduled(Map> cacheMap, Set agentSet) { + List hostList = hostService.list(); + for (OpsHostEntity host : hostList) { + String hostId = host.getHostId(); + if (agentSet.contains(hostId)) { + continue; + } + SshLogin sshLogin = getOpsHostSsh(host); + Map value = cacheMap.getOrDefault(hostId, new HashMap<>()); + value.putAll(executeHostMonitorBySshCommand(hostId, sshLogin)); + cacheMap.put(hostId, value); + } + } + + private Map executeHostMonitorBySshCommand(String hostId, SshLogin sshLogin) { + Map cache = new HashMap<>(); + try { + HostBaseInfo hostBasicInfo = hostBasicService.getHostBasicInfo(hostId); + if (Objects.isNull(hostBasicInfo)) { + cacheHostFixedInfo(hostId, cache, sshLogin); + } else { + hostBasicService.updateHostMonitorCache(cache, hostBasicInfo); + } + cacheHostRealtimeInfo(cache, sshLogin); + } catch (OpsException ex) { + log.error("executeHostMonitorBySshCommand error,hostId:{},error:{}", sshLogin.getHost(), ex.getMessage()); + } catch (Exception ex) { + log.error("execute unknown error,hostId:{},error:{}", sshLogin.getHost(), ex.getMessage()); + } + return cache; + } + + private void cacheHostFixedInfo(String hostId, Map hostInfoMap, SshLogin sshLogin) { + List> tasks = new LinkedList<>(); + tasks.add(createSshTask(() -> os(hostId, sshLogin, hostInfoMap))); + tasks.add(createSshTask(() -> osVersion(hostId, sshLogin, hostInfoMap))); + tasks.add(createSshTask(() -> cpu(hostId, sshLogin, hostInfoMap))); + tasks.add(createSshTask(() -> disk(sshLogin, hostInfoMap))); + CompletableFuture.allOf(tasks.toArray(new CompletableFuture[0])).join(); + } + + private void cacheHostRealtimeInfo(Map hostInfoMap, SshLogin sshLogin) { + List> tasks = new LinkedList<>(); + tasks.add(createSshTask(() -> cpuUsing(sshLogin, hostInfoMap))); + tasks.add(createSshTask(() -> memory(sshLogin, hostInfoMap))); + tasks.add(createSshTask(() -> hostForMigration(sshLogin, hostInfoMap))); + tasks.add(createSshTask(() -> netMonitor(sshLogin, hostInfoMap))); + CompletableFuture.allOf(tasks.toArray(new CompletableFuture[0])).join(); + } + + private CompletableFuture createSshTask(Runnable task) { + return CompletableFuture.runAsync(() -> { + try { + semaphore.acquire(); + task.run(); + } catch (Exception tr) { + log.warn("run ssh task has some error {}", tr.getMessage()); + } finally { + semaphore.release(); + } + }, customPool); + } + + /** + * cache host os name, os version, migration info + * + * @param hostId host id + * @param key key + * @return value + */ + public String getSshHostSingleInfo(String hostId, String key) { + Map hostInfoMap = hostBasicService.getHostBasicInfoMap(hostId); + SshLogin sshLogin = getOpsHostSsh(hostId); + switch (key) { + case OS_NAME -> os(hostId, sshLogin, hostInfoMap); + case OS_VERSION -> osVersion(hostId, sshLogin, hostInfoMap); + case MIGRATION_HOST -> hostForMigration(sshLogin, hostInfoMap); + default -> ignoreEmpty("getSshHostSingleInfo not found " + sshLogin + " key " + key); + } + return hostInfoMap.getOrDefault(key, ""); + } + + private void ignoreEmpty(String msg) { + log.debug("fetch host info {} ignore empty", msg); + } + + /** + * get host info , contains cpu, memory, disk + * + * @param hostId host id + * @param key key + * @return host info + */ + public Map getSshHostInfo(String hostId, String key) { + Map hostInfoMap = hostBasicService.getHostBasicInfoMap(hostId); + SshLogin sshLogin = getOpsHostSsh(hostId); + switch (key) { + case CPU_ARCH, CPU_CORE_NUM, CPU_FREQUENCY -> cpu(hostId, sshLogin, hostInfoMap); + case DISK_MONITOR, SYSTEM_DISK_TOTAL, SYSTEM_DISK_USED, SYSTEM_DISK_FREE, SYSTEM_DISK_USAGE -> + disk(sshLogin, hostInfoMap); + case SYSTEM_MEMORY_AVAILABLE, SYSTEM_MEMORY_TOTAL, SYSTEM_MEMORY_USAGE -> memory(sshLogin, hostInfoMap); + default -> ignoreEmpty("getSshHostInfo not found " + sshLogin.toString() + " key " + key); + } + return hostInfoMap; + } + + private void cpuUsing(SshLogin sshLogin, Map hostInfoMap) { + try { + // 获取CPU使用率 + String cpuUsing = jschExecutorService.getCpuUsing(sshLogin); + // 如果CPU使用率不为空,则将其放入hostInfoMap中 + if (StrUtil.isNotEmpty(cpuUsing)) { + hostInfoMap.put(AgentConstants.HostMetric.CPU_USING, cpuUsing); + } + } catch (OpsException ex) { + // 如果发生OpsException异常,则抛出新的OpsException异常,并附带异常信息 + throw new OpsException("get cpu using " + ex.getMessage()); + } + } + + private void netMonitor(SshLogin sshLogin, Map hostInfoMap) { + try { + String[] netMonitor2 = jschExecutorService.getNetMonitor(sshLogin, true); + if (ArrayUtil.isNotEmpty(netMonitor2)) { + hostInfoMap.put(AgentConstants.HostMetric.NET_MONITOR, ArrayUtil.join(netMonitor2, "|")); + } + } catch (OpsException ex) { + throw new OpsException("get net " + ex.getMessage()); + } + } + + private void hostForMigration(SshLogin sshLogin, Map hostInfoMap) { + try { + String baseInfo = jschExecutorService.getHostBaseInfo(sshLogin); + if (StrUtil.isNotEmpty(baseInfo)) { + hostInfoMap.put(MIGRATION_HOST, baseInfo.replaceAll("\n", "##")); + } + } catch (OpsException ex) { + throw new OpsException("get migration host info " + ex.getMessage()); + } + } + + /** + * get memory info : SYSTEM_MEMORY_AVAILABLE, SYSTEM_MEMORY_TOTAL, SYSTEM_MEMORY_USAGE + * + * @param sshLogin ssh login + * @param hostInfoMap host info map + */ + private void memory(SshLogin sshLogin, Map hostInfoMap) { + try { + String memory = jschExecutorService.getMemory(sshLogin); + if (StrUtil.isEmpty(memory)) { + return; + } + HostRealtimeStatistics ofMemory = HostRealtimeStatistics.ofMemory(memory); + hostInfoMap.put(SYSTEM_MEMORY_TOTAL, ofMemory.getTotal() + ""); + hostInfoMap.put(SYSTEM_MEMORY_AVAILABLE, ofMemory.getAvailable() + ""); + hostInfoMap.put(SYSTEM_MEMORY_USAGE, ofMemory.getUse()); + } catch (OpsException ex) { + throw new OpsException("get memory " + ex.getMessage()); + } + } + + /** + * get disk info : DISK_MONITOR,SYSTEM_DISK_TOTAL, SYSTEM_DISK_USED, SYSTEM_DISK_FREE, SYSTEM_DISK_USAGE + * + * @param sshLogin ssh login + * @param hostInfoMap host info map + */ + private void disk(SshLogin sshLogin, Map hostInfoMap) { + try { + String res = jschExecutorService.getDiskMonitor(sshLogin); + if (StrUtil.isNotEmpty(res)) { + HostRealtimeStatistics ofDisk = HostRealtimeStatistics.ofDisk(res); + hostInfoMap.put(DISK_MONITOR, JSONObject.toJSONString(ofDisk)); + hostInfoMap.put(SYSTEM_DISK_FREE, ofDisk.getAvailable() + ""); + hostInfoMap.put(SYSTEM_DISK_USED, ofDisk.getUsed() + ""); + hostInfoMap.put(SYSTEM_DISK_USAGE, ofDisk.getUse()); + hostInfoMap.put(SYSTEM_DISK_TOTAL, ofDisk.getTotal() + ""); + } + } catch (OpsException ex) { + throw new OpsException("get disk " + ex.getMessage()); + } + } + + /** + * get cpu info : CPU_ARCH, CPU_CORE_NUM, CPU_FREQUENCY + * + * @param hostId hostId + * @param sshLogin ssh login + * @param hostInfoMap host info map + */ + private void cpu(String hostId, SshLogin sshLogin, Map hostInfoMap) { + if (hostInfoMap.containsKey(CPU_ARCH) && hostInfoMap.containsKey(CPU_CORE_NUM) && hostInfoMap.containsKey( + CPU_FREQUENCY)) { + return; + } + try { + String ofCpu = jschExecutorService.getCpu(sshLogin); + if (StrUtil.isEmpty(ofCpu)) { + return; + } + String[] split = ofCpu.replaceAll(" ", "").split("\n"); + for (String line : split) { + if (line.contains("Architecture")) { + String[] arch = line.split(":"); + hostInfoMap.put(CPU_ARCH, arch[1].trim()); + continue; + } + if (line.contains("CPU(s):") && !line.contains("node")) { + String[] arch = line.split(":"); + hostInfoMap.put(CPU_CORE_NUM, arch[1].trim()); + continue; + } + // CPUmaxMHz arm frequency ; CPUMHz x86 frequency + if (line.contains("CPUmaxMHz:") || line.contains("CPUMHz:")) { + String[] arch = line.split(":"); + hostInfoMap.put(CPU_FREQUENCY, toCpuGHz(arch[1].trim())); + } + } + hostService.updateHostCpu(sshLogin.getHost(), hostInfoMap.get(CPU_ARCH), hostInfoMap.get(CPU_CORE_NUM), + hostInfoMap.get(CPU_FREQUENCY)); + hostBasicService.refreshCacheOfHostBasicInfo(hostId); + } catch (OpsException ex) { + throw new OpsException("get cpu " + ex.getMessage()); + } + } + + private String toCpuGHz(String cpuMHz) { + return String.format("%.2fGHz", Float.parseFloat(cpuMHz) / 1000); + } + + private void osVersion(String hostId, SshLogin sshLogin, Map hostInfoMap) { + if (hostInfoMap.containsKey(OS_VERSION)) { + return; + } + try { + String res = jschExecutorService.getOsVersion(sshLogin); + if (StrUtil.isNotEmpty(res)) { + hostInfoMap.put(OS_VERSION, res); + } + hostService.updateHostOsVersion(sshLogin.getHost(), res); + hostBasicService.refreshCacheOfHostBasicInfo(hostId); + } catch (OpsException ex) { + throw new OpsException("get os version " + ex.getMessage()); + } + } + + private void os(String hostId, SshLogin sshLogin, Map hostInfoMap) { + if (hostInfoMap.containsKey(OS_NAME)) { + return; + } + try { + String res = jschExecutorService.getOs(sshLogin); + if (StrUtil.isNotEmpty(res)) { + hostInfoMap.put(OS_NAME, res); + } + hostService.updateHostOsName(sshLogin.getHost(), res); + hostBasicService.refreshCacheOfHostBasicInfo(hostId); + } catch (OpsException ex) { + throw new OpsException("get os " + ex.getMessage()); + } + } + + /** + * get host user,when has root ,return root user,otherwise return normal user + * + * @param hostId host id + * @return host user + * @throws OpsException host user not found + */ + private OpsHostUserEntity getHostUserRootOrNormal(String hostId) { + List userList = hostUserService.listHostUserByHostId(hostId); + OpsHostUserEntity hostUser = userList.stream() + .filter(user -> StrUtil.isNotEmpty(user.getPassword())) + .filter(user -> StrUtil.equalsIgnoreCase(user.getUsername(), "root")) + .findFirst() + .orElse(null); + if (Objects.isNull(hostUser)) { + hostUser = userList.stream() + .filter(user -> StrUtil.isNotEmpty(user.getPassword())) + .findAny() + .orElseThrow(() -> new OpsException("host user does not exist")); + } + return hostUser; + } + + private void putIfValid(Map map, String key, String value) { + if (StrUtil.isNotEmpty(value)) { + map.put(key, value); + } + } + + private SshLogin getOpsHostSsh(String hostId) { + OpsHostEntity host = hostService.getById(hostId); + if (host == null) { + throw new OpsException("host not found,hostId:" + hostId); + } + return getOpsHostSsh(host); + } + + private SshLogin getOpsHostSsh(OpsHostEntity host) { + OpsHostUserEntity user = getHostUserRootOrNormal(host.getHostId()); + if (user == null) { + throw new OpsException("host user not found or invalid : " + host.getHostId() + ":" + host.getPublicIp()); + } + return new SshLogin(host.getPublicIp(), host.getPort(), user.getUsername(), + encryptionUtils.decrypt(user.getPassword())); + } + + /** + * host cache item + */ + interface CacheConstants { + /** + * host disk monitor + */ + String DISK_MONITOR = "diskMonitor"; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysLogService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysLogService.java new file mode 100644 index 000000000..bc8907eb9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysLogService.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ISysLogService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysLogService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service; + +import org.opengauss.admin.common.core.dto.SysLogConfigDto; +import org.opengauss.admin.common.core.vo.SysLogConfigVo; + +import java.io.File; +import java.util.List; +import java.util.Map; + + +/** + * System log interface + */ +public interface ISysLogService { + + public SysLogConfigVo getAllLogConfig(); + + // @PostConstruct + void init(); + + public void saveAllLogConfig(SysLogConfigDto sysLogConfigDto); + + public void saveLogConfig(String key, String value); + + public String getConfigByKey(String key); + + public List> listAllLogFile(); + + public File getLogFileByName(String filename); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysMenuService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysMenuService.java new file mode 100644 index 000000000..345f1960f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysMenuService.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ISysMenuService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysMenuService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.TreeSelect; +import org.opengauss.admin.common.core.domain.entity.SysMenu; +import org.opengauss.admin.common.core.vo.RouterVo; + +import java.util.List; +import java.util.Set; + +/** + * Menu Service Interface + * + * @author xielibo + */ +public interface ISysMenuService extends IService { + /** + * Query the system menu list according to the user + * + * @param userId userId + */ + public List selectMenuList(Integer userId); + + /** + * Query menu list + * + * @param menu menu + * @param userId user ID + * @return menu list + */ + public List selectMenuList(SysMenu menu, Integer userId); + + List selectSpecialRouteList(Integer openPosition); + + SysMenu existsMenu(Integer parentId, String pluginId, String path, String menuName); + + /** + * Query permission list based on user ID + * + * @param userId user ID + * @return permission list + */ + public Set selectMenuPermsByUserId(Integer userId); + + /** + * Query menu list by user ID + * + * @param userId user ID + * @return menu list + */ + public List selectMenuTreeByUserId(Integer userId); + + /** + * Query menu tree information based on role ID + * + * @param roleId role ID + * @return menu IDs + */ + public List selectMenuListByRoleId(Integer roleId); + + /** + * The menu needed to build the front-end routing + * + * @param menus menu list + * @param language current locale + * @return router list + */ + public List buildMenus(List menus, String language); + + /** + * Build the menu tree structure required by the front end + * + * @param menus menus + * @return menu tree list + */ + public List buildMenuTree(List menus); + + /** + * Build the menu tree select structure required by the front end + * + * @param menus menu list + * @return tree select + */ + public List buildMenuTreeSelect(List menus); + + /** + * get by ID + * + * @param menuId Menu ID + * @return menu + */ + public SysMenu selectMenuById(Integer menuId); + + /** + * Determine if a menu exists as a submenu + * + * @param menuId Menu ID + */ + public boolean hasChildByMenuId(Integer menuId); + + Integer countMenuHasOtherPluginSubmenuByPluginId(String pluginId); + + /** + * According to the plugin, get the parent menu that has other plugin submenus + * + * @param pluginId pluginId + * @return parentMenu num + */ + Integer countMenuHasOtherEnablePluginSubmenuByPluginId(String pluginId); + + /** + * Determine whether the menu is assigned + * + * @param menuId Menu ID + */ + public boolean checkMenuExistRole(Integer menuId); + + /** + * save menu + */ + public int insertMenu(SysMenu menu); + + /** + * update menu + */ + public int updateMenu(SysMenu menu); + + /** + * delete menu + */ + public int deleteMenuById(Integer menuId); + + int deleteByPluginId(String pluginId); + + /** + * Check if the menu is unique + */ + public String checkMenuNameUnique(SysMenu menu); + + void updatePluginMenuTheme(String pluginId, String theme); + + void updatePluginMenuIcon(String pluginId, String icon); + + /** + * update menu disable by pluginId + * + * @param pluginId pluginId + * @return result + */ + public int updateDisableByPluginId(String pluginId); + + /** + * update parent menu icon by pluginId + * + * @param pluginId pluginId + * @param icon icon + */ + void updatePluginFatherMenuIcon(String pluginId, String icon); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysOperLogService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysOperLogService.java new file mode 100644 index 000000000..2047c04ea --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysOperLogService.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ISysOperLogService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysOperLogService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.opengauss.admin.system.domain.SysOperLog; + +/** + * OperLog Service Interface + * + * @author xielibo + */ +public interface ISysOperLogService { + + /** + * Save Operlog + * + */ + public void insertOperlog(SysOperLog operLog); + + /** + * Query the collection of system operation logs + * + * @param operLog operLog + */ + public IPage selectOperLogList(IPage page, SysOperLog operLog); + + /** + * Delete System Operation Logs in Batches + * + * @param operIds operIds + */ + public int deleteOperLogByIds(String[] operIds); + + /** + * Query operation log details + * + * @param operId operId + */ + public SysOperLog selectOperLogById(String operId); + + /** + * clear operation log + */ + public void cleanOperLog(); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginConfigDataService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginConfigDataService.java new file mode 100644 index 000000000..93bff6d12 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginConfigDataService.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ISysPluginConfigDataService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginConfigDataService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.system.domain.SysPluginConfigData; + +/** + * @author xielibo + */ +public interface ISysPluginConfigDataService extends IService { + + public void savePluginConfigData(String pluginId, String data); + + String getDataByPluginId(String pluginId); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginConfigService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginConfigService.java new file mode 100644 index 000000000..4167de6a2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginConfigService.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ISysPluginConfigService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginConfigService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.system.domain.SysPluginConfig; + +/** + * @author xielibo + */ +public interface ISysPluginConfigService extends IService { + + public void savePluginConfig(String pluginId, String configJson); + + void deleteByPluginId(String pluginId); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginLogoService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginLogoService.java new file mode 100644 index 000000000..98f2b36d0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginLogoService.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ISysPluginLogoService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginLogoService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.system.domain.SysPluginLogo; + +/** + * @author xielibo + */ +public interface ISysPluginLogoService extends IService { + + public void savePluginConfig(String pluginId, String logoPath); + + SysPluginLogo getByPluginId(String pluginId); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginRepositoryService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginRepositoryService.java new file mode 100644 index 000000000..d2d6943ca --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginRepositoryService.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ISysPluginRepositoryService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/ + * service/ISysPluginRepositoryService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.system.domain.SysPluginRepository; +import java.util.List; + +/** + * ISysPluginRepositoryService + * + * @author zhaochen + * @since 2025-03-20 + * @version 7.0.0-RC1 + */ +public interface ISysPluginRepositoryService extends IService { + /** + * Update plugins download status + * + * @param pluginIds pluginIds list + * @param isDownload download status 0-unloaded 1-loaded + */ + void updatePluginDownloadStatus(List pluginIds, Integer isDownload); + + /** + * Gets a list of pluginIds that are not loaded + * + * @return List of unloaded pluginIds + */ + List getUnloadPluginIdsList(); + + /** + * Gets a list of pluginIds that are not loaded by pluginRepository + * + * @param uploadedPluginIds uploadedPluginIds + * @return List of unloaded pluginIds + */ + List getUnloadPluginListByRepository(List uploadedPluginIds); + + /** + * Gets plugin version + * + * @return plugin version + */ + String getCurrentVersion(); + + /** + * Gets plugin download url by pluginId and version + * + * @param pluginId pluginId + * @param pluginVersion pluginVersion + * @return plugin download url + */ + String getPluginUrlByIdAndVersion(String pluginId, String pluginVersion); + + /** + * save online plugin package + * + * @param downloadUrl downloadUrl + * @param fileName fileName + * @param wsBusinessId wsBusinessId + */ + void saveOnlinePackage(String downloadUrl, String fileName, String wsBusinessId); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginService.java new file mode 100644 index 000000000..6b632a42d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginService.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ISysPluginService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysPluginService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.system.domain.SysPlugin; + +import java.util.List; + +/** + * @author xielibo + */ +public interface ISysPluginService extends IService { + IPage selectList(IPage page, SysPlugin sysPlugin); + + void stopByPluginId(String pluginId); + + void startByPluginId(String pluginId); + + void uninstallPluginByPluginId(String pluginId); + + SysPlugin getByPluginId(String pluginId); + + /** + * getPluginIds + * + * @return pluginIds + */ + List getPluginList(); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysRoleService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysRoleService.java new file mode 100644 index 000000000..badde10ec --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysRoleService.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ISysRoleService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysRoleService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.SysRole; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Set; + +/** + * Role Service Interface + * + * @author xielibo + */ +public interface ISysRoleService extends IService { + + /** + * Query role data by pagination according to conditions + * + */ + public IPage selectRoleList(SysRole sysRole, IPage page); + + /** + * Query roles based on user ID + * @param userId userId + */ + List selectRolesByUserId(Integer userId); + + /** + * Query role data based on conditions + * + * @param sysRole role + */ + public List selectRoleList(SysRole sysRole); + + + /** + * Query permissions based on user ID + * + * @param userId userId + */ + public Set selectRolePermissionByUserId(Integer userId); + + /** + * Query All Role + * + */ + public List selectRoleAll(); + + /** + * Get the role selection box list according to the user ID + * + * @param userId userId + */ + public List selectRoleListByUserId(Integer userId); + + /** + * Query roles by role ID + * + */ + public SysRole selectRoleById(Integer roleId); + + /** + * Check if the role name is unique + * + * @param role role + */ + public String checkRoleNameUnique(SysRole role); + + /** + * Check if the role allows the operation + * + */ + public void checkRoleAllowed(SysRole role); + + /** + * Query the number of roles used by the role ID + * + */ + public int countUserRoleByRoleId(Integer roleId); + + /** + * Save Role + * + */ + public int insertRole(SysRole role); + + /** + * Update Role + * + * @param role role + */ + public int updateRole(SysRole role); + + /** + * Update Status + * + * @param role role + */ + public int updateRoleStatus(SysRole role); + + @Transactional(rollbackFor = Exception.class) + int authDataScope(SysRole role); + + @Transactional(rollbackFor = Exception.class) + int deleteRoleByIds(Integer[] roleIds); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysSettingExtService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysSettingExtService.java new file mode 100644 index 000000000..730b14bb6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysSettingExtService.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; + +import org.opengauss.admin.common.core.domain.entity.SysSettingEntity; +import org.opengauss.admin.common.core.domain.entity.SysSettingExtEntity; + +import java.util.List; + +/** + * ISysSettingExtService + * + * @author: wangchao + * @Date: 2025/9/8 15:47 + * @Description: ISysSettingExtService + * @since 7.0.0 + **/ +public interface ISysSettingExtService extends IService { + /** + * query current user ext param + * + * @param userId userId + * @return ext param list + */ + List queryCurrentUserExtParam(Integer userId); + + /** + * update ext setting of datakit server host + * + * @param setting setting + */ + void updateExtSettingDataKitServerHost(SysSettingEntity setting); + + /** + * query admin user of datakit server host + * + * @return default server host + */ + String queryAdminServerHost(); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysSettingService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysSettingService.java new file mode 100644 index 000000000..905c35d8e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysSettingService.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ISysSettingService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysSettingService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.SysSettingEntity; + +import java.net.Proxy; + +/** + * System Setting Interface + * + * @author wangyl + */ +public interface ISysSettingService extends IService { + boolean updateSetting(SysSettingEntity setting); + + SysSettingEntity getSetting(Integer userId); + + /** + * init http proxy + */ + void initHttpProxy(); + + /** + * create http proxy by system config: server.proxy.hostname server.proxy.port + * + * @return proxy + */ + Proxy getSysNetProxy(); + + boolean hasUploadPath(String path, Integer userId); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysTaskService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysTaskService.java new file mode 100644 index 000000000..433e0f68a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysTaskService.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ISysTaskService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysTaskService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.dto.SysTaskDto; +import org.opengauss.admin.common.enums.SysTaskStatus; +import org.opengauss.admin.system.domain.SysTask; + +import java.util.List; +import java.util.Map; + +/** + * Task Service Interface + * + * @author xielibo + */ +public interface ISysTaskService extends IService { + + IPage selectList(IPage page, SysTaskDto task); + + List selectListAll(SysTaskDto task); + + Map getDetailById(Integer taskId); + + void deleteTask(Integer[] ids); + + void updateStatus(Integer id, SysTaskStatus taskStatus); + + void startTask(Integer id); + + void stopTask(Integer id); + + void refreshTaskStatus(); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysUserService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysUserService.java new file mode 100644 index 000000000..17df26d99 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysUserService.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ISysUserService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysUserService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.SysUser; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * User Service Interface + * + * @author xielibo + */ +public interface ISysUserService extends IService { + + /** + * Query user page list + * + * @param user user param + * @return page list + */ + public IPage selectUserList(SysUser user, IPage page); + + /** + * Query by username + * + * @param userName username + * @return SysUser + */ + public SysUser selectUserByUserName(String userName); + + /** + * get By Id + * + * @param userId user ID + * @return SysUser + */ + public SysUser selectUserById(Integer userId); + + /** + * Query the user's role + * + * @param userName username + */ + public String selectUserRoleGroup(String userName); + + /** + * Check if username is unique + * + * @param userName username + */ + public String checkUserNameUnique(String userName); + + /** + * Check if phone is unique + * + * @param user user + * @return + */ + public String checkPhoneUnique(SysUser user); + + /** + * Check if email is unique + * + * @param user user + * @return + */ + public String checkEmailUnique(SysUser user); + + /** + * Check if the user allows the operation + * + * @param user user + */ + public void checkUserAllowed(SysUser user); + + /** + * save user + * + */ + public int insertUser(SysUser user); + + /** + * update user + * + */ + public int updateUser(SysUser user); + + + /** + * update status + * + */ + public int updateUserStatus(SysUser user); + + /** + * updat information + * + */ + public int updateUserProfile(SysUser user); + + /** + * update user avatar + * + */ + public boolean updateUserAvatar(String userName, String avatar); + + /** + * reset password + * + */ + public int resetPwd(SysUser user); + + /** + * reset password + * + */ + public int resetUserPwd(String userName, String password); + + public int deleteUserById(Integer userId); + + public int deleteUserByIds(Integer[] userIds); + + public List listUserByRemark(String remark); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysWhiteListService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysWhiteListService.java new file mode 100644 index 000000000..86b335c46 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysWhiteListService.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ISysWhiteListService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ISysWhiteListService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.system.domain.SysWhiteList; + +import java.util.List; + +/** + * White List Service Interface + * + * @author xielibo + */ +public interface ISysWhiteListService extends IService { + IPage selectList(IPage page, SysWhiteList whiteList); + + List selectListAll(SysWhiteList whiteList); + + /** + * check single ip exists in whiteList + * + * @param ip ip + * @return is single ip exists + */ + boolean checkSingleIpExistsInWhiteList(String ip); + + /** + * check ips exists in white list + * + * @param whiteList whitelist + * @return exists ip list + */ + List checkIpsExistsInWhiteList(SysWhiteList whiteList); + + public boolean checkTitleExists(SysWhiteList whiteList); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/IWebSocketService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/IWebSocketService.java new file mode 100644 index 000000000..16b75d063 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/IWebSocketService.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IWebSocketService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/IWebSocketService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service; + +import jakarta.websocket.Session; + +/** + * @author lhf + * @date 2022/8/6 18:14 + **/ +public interface IWebSocketService { + /** + * Called when a session is established + */ + void onOpen(String pluginId, String sessionId, Session session); + + /** + * When the session is closed + */ + void onClose(String pluginId, String sessionId); + + /** + * When there is a message in the conversation + */ + void onMessage(String pluginId, String sessionId, String message); + + void sendMessage(String pluginId, String sessionId, String message); + + /** + * Send message to client session + * @param session session object + * @param message message to send + */ + void sendMessage(Session session, String message); + +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/JschExecutorService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/JschExecutorService.java new file mode 100644 index 000000000..d76895e10 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/JschExecutorService.java @@ -0,0 +1,980 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JschExecutorService.java + * + * IDENTIFICATION + * openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/JschExecutorService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.service; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.ChannelShell; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpException; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.constant.CommonConstants; +import org.opengauss.admin.common.constant.ops.SshCommandConstants; +import org.opengauss.admin.common.core.domain.model.ops.WsSession; +import org.opengauss.admin.common.core.handler.ops.cache.WsConnectorManager; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.OpsAssert; +import org.opengauss.admin.system.plugin.beans.SshLogin; +import org.opengauss.jsch.pool.ExecOperations; +import org.opengauss.jsch.pool.JschSessionPool; +import org.opengauss.jsch.pool.config.SessionConfig; +import org.opengauss.jsch.pool.enums.ChannelType; +import org.springframework.stereotype.Service; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import jakarta.annotation.PreDestroy; + +/** + * JschExecutorService + * + * @author wangchao + * @since 2024/10/29 09:26 + */ +@Slf4j +@Service +public class JschExecutorService { + /** + * Get the os of the remote machine + * + * @param sshLogin ssh login info + * @return os + */ + public String getOs(SshLogin sshLogin) { + return execCommand(sshLogin, SshCommandConstants.OS); + } + + /** + * Get the os version of the remote machine + * + * @param sshLogin ssh login info + * @return os version + */ + public String getOsVersion(SshLogin sshLogin) { + return execCommand(sshLogin, SshCommandConstants.OS_VERSION); + } + + /** + * Get the os info of the remote machine + * + * @param session ssh session + * @return os info + */ + public String getOs(Session session) { + return execCommand(session, SshCommandConstants.OS); + } + + /** + * Get the os version of the remote machine + * + * @param session ssh session + * @return os version + */ + public String getOsVersion(Session session) { + return execCommand(session, SshCommandConstants.OS_VERSION); + } + + /** + * Get the hostname of the remote machine + * + * @param sshLogin ssh login info + * @return hostname + */ + public String getHostname(SshLogin sshLogin) { + return execCommand(sshLogin, SshCommandConstants.HOSTNAME); + } + + /** + * Get the cpu architecture of the remote machine + * + * @param session ssh session + * @return cpu architecture + */ + public String getCpuArch(Session session) { + return execCommand(session, SshCommandConstants.CPU_ARCH); + } + + /** + * Get the cpu architecture of the remote machine + * + * @param sshLogin ssh login info + * @return cpu architecture + */ + public String getCpuArch(SshLogin sshLogin) { + return execCommand(sshLogin, SshCommandConstants.CPU_ARCH); + } + + /** + *
+     * Get the cpu info of the remote machine
+     *
+     * eg. x86_64
+     * lscpu | grep "CPU(s):\|Architecture\|MHz"
+     * Architecture:          x86_64
+     * CPU(s):                8
+     * CPU MHz:               2799.974
+     * NUMA node0 CPU(s):     0-7
+     *
+     * eg. aarch64
+     * lscpu | grep "CPU(s):\|Architecture\|MHz"
+     * Architecture:                    aarch64
+     * CPU(s):                          12
+     * CPU max MHz:                     2400.0000
+     * CPU min MHz:                     2400.0000
+     * NUMA node0 CPU(s):               0-5
+     * NUMA node1 CPU(s):               6-11
+     * 
+ * + * @param sshLogin ssh login info + * @return cpu info + */ + public String getCpu(SshLogin sshLogin) { + return execCommand(sshLogin, SshCommandConstants.CPU); + } + + /** + * 检查端口是否被占用 + * + * @param sshLogin ssh登录信息 + * @param port 端口号 + * @return true 未被占用,false 被占用 + * @author wangchao + * @date 2022/10/27 15:08 + * @since 7.0.0 + */ + public boolean checkOsPortConflict(SshLogin sshLogin, int port) { + OpsAssert.isTrue(port > 1024 && port < 65536, "port must be between 1024 and 65535"); + String command = String.format(SshCommandConstants.CHECK_OS_PORT_CONFLICT, port); + String result = execCommand(sshLogin, command); + OpsAssert.isTrue(StrUtil.isNotEmpty(result) && NumberUtil.isInteger(result), "port conflict error :" + result); + return NumberUtil.parseInt(result) <= 0; + } + + /** + * check port conflict + * + * @param session session + * @param port port + * @return true- not conflict, false - conflict + */ + public boolean checkOsPortConflict(Session session, int port) { + OpsAssert.isTrue(port > 1024 && port < 65536, "port must be between 1024 and 65535"); + String command = String.format(SshCommandConstants.CHECK_OS_PORT_CONFLICT, port); + String result = execCommand(session, command); + OpsAssert.isTrue(StrUtil.isNotEmpty(result) && NumberUtil.isInteger(result), "port conflict error :" + result); + return NumberUtil.parseInt(result) <= 0; + } + + /** + * 检查当前用户是否存在 + * + * @param sshLogin ssh登录信息 + * @return true 存在,false 不存在 + */ + public boolean checkOsUserExist(SshLogin sshLogin) { + Session session = null; + try { + session = createSession(sshLogin); + return session.isConnected(); + } catch (OpsException ex) { + log.error("create session error ignore {}", ex.getMessage()); + } finally { + if (Objects.nonNull(session)) { + session.disconnect(); + } + } + return false; + } + + /** + * 创建用户 + * + * @param rootLogin root登录信息 + * @param username 用户名 + */ + public void createOsUser(SshLogin rootLogin, String username) { + String command = String.format(SshCommandConstants.CREATE_OS_USER, username); + execCommand(rootLogin, command); + } + + /** + * check user has sudo no password permission + * + * @param sshLogin ssh login info + * @return has permission or not + */ + public Boolean checkOsUserSudo(SshLogin sshLogin) { + String hasSudoResult = "true"; + String withoutSudoResult = "false"; + String command = String.format(SshCommandConstants.CREATE_OS_USER_SUDO, hasSudoResult, withoutSudoResult); + String result = execCommand(sshLogin, command); + + if (result.contains(withoutSudoResult) + || result.contains("a password is required") + || result.contains("must be owned") + || result.contains("command not found")) { + return false; + } + return result.contains(hasSudoResult); + } + + /** + * Get the cpu info of the remote machine + * + * @param sshLogin ssh login info + * @param hasNetName true 包含网卡名称,false 不包含网卡名称 + * @return net info + */ + public String[] getNetMonitor(SshLogin sshLogin, boolean hasNetName) { + String[] res = hasNetName ? new String[3] : new String[2]; + String netCardName = execCommand(sshLogin, + String.format(SshCommandConstants.NET_CARD_NAME, sshLogin.getHost())); + // rx_net|$tx_ne + String rxNet = execCommand(sshLogin, String.format(SshCommandConstants.NET_MONITOR, netCardName)); + String[] split = rxNet.split("\\|"); + if (split.length == 2) { + if (hasNetName) { + res[0] = netCardName; + res[1] = split[0]; + res[2] = split[1]; + } else { + res[0] = split[0]; + res[1] = split[1]; + } + } else { + Arrays.fill(res, "-1"); + } + return res; + } + + /** + * 获取cpu使用率 + * + * @param sshLogin ssh登录信息 + * @return cpu使用率 + */ + public String getCpuUsing(SshLogin sshLogin) { + return new JschExecutor().execCommand(sshLogin, SshCommandConstants.CPU_USING); + } + + /** + * get memory info of the remote machine + * + * @param sshLogin ssh login + * @return memory info + */ + public String getMemory(SshLogin sshLogin) { + return new JschExecutor().execCommand(sshLogin, SshCommandConstants.MEMORY); + } + + /** + * get host base info : contains cpu core number, remaining memory, available disk space + * + * @param sshLogin ssh login + * @return base info + */ + public String getHostBaseInfo(SshLogin sshLogin) { + return new JschExecutor().execCommand(sshLogin, SshCommandConstants.HOST_BASE_INFO); + } + + /** + *
+     * Obtain the usage information of each partition of the server disk
+     * eg.  df -T --total | tail -n +2| tr -s " "
+     *
+     * devtmpfs devtmpfs 15893580 0 15893580 0% /dev
+     * tmpfs tmpfs 15904136 1396 15902740 1% /dev/shm
+     * tmpfs tmpfs 15904136 1583488 14320648 10% /run
+     * tmpfs tmpfs 15904136 0 15904136 0% /sys/fs/cgroup
+     * /dev/sda1 ext4 61795096 57479008 1363804 98% /
+     * /dev/sdb ext4 206292968 98393248 97397576 51% /data
+     * /dev/sdc ext4 103080888 35819960 62001664 37% /data2
+     * tmpfs tmpfs 3180828 0 3180828 0% /run/user/0
+     * tmpfs tmpfs 3180828 0 3180828 0% /run/user/54355
+     * tmpfs tmpfs 3180828 0 3180828 0% /run/user/54360
+     * tmpfs tmpfs 3180828 0 3180828 0% /run/user/54354
+     * total - 469764048 193277100 257773256 43% -
+     *
+     * eg.  df -T --total | tail -n +2| tr -s " " | grep -v total
+     *
+     * total - 469764048 193277100 257773256 43% -
+     * 
+ * + * @param sshLogin ssh login + * @return base info + */ + public String getDiskMonitor(SshLogin sshLogin) { + return new JschExecutor().execCommand(sshLogin, SshCommandConstants.DISK_TOTAL_MONITOR); + } + + /** + * 检查操作系统用户是否存在 + * + * @param sshLogin ssh登录信息 + * @param username 用户名 + * @return true-存在,false-不存在 + */ + public boolean checkOsUserExist(SshLogin sshLogin, String username) { + String command = String.format(SshCommandConstants.CHECK_OS_USER_EXIST, username); + String result = execCommand(sshLogin, command); + return !result.isEmpty() && !result.contains(SshCommandConstants.NO_SUCH_USER); + } + + /** + * 执行shell命令,该方法会主动关闭会话 + * + * @param sshLogin ssh登录信息 + * @param command shell命令 + * @return 命令执行结果 + */ + public String execCommand(SshLogin sshLogin, String command) { + return new JschExecutor().execCommand(sshLogin, command); + } + + /** + * 执行shell命令,该方法不主动关闭会话 + * + * @param session ssh会话 + * @param command shell命令 + * @return 命令执行结果 + */ + public String execCommand(Session session, String command) { + return new JschExecutor().execCommand(session, command); + } + + /** + * 执行shell命令,envPath为环境变量配置文件路径,如果envPath为空,则不加载环境变量配置文件 + * + * @param sshLogin ssh登录信息 + * @param command shell命令 + * @param envPath 环境变量配置文件路径 + * @return 命令执行结果 + */ + public String execCommand(SshLogin sshLogin, String command, String envPath) { + String envCommand = StrUtil.isNotEmpty(envPath) ? "source " + envPath + " && " + command : command; + return new JschExecutor().execCommand(sshLogin, envCommand); + } + + /** + * 执行shell命令,envPath为环境变量配置文件路径,如果envPath为空,则不加载环境变量配置文件 + * + * @param session ssh会话 + * @param command shell命令 + * @param envPath 环境变量配置文件路径 + * @return 命令执行结果 + */ + public String execCommand(Session session, String command, String envPath) { + String envCommand = StrUtil.isNotEmpty(envPath) ? "source " + envPath + " && " + command : command; + return new JschExecutor().execCommand(session, envCommand); + } + + /** + * 打开一个ChannelShell + * + * @param session ssh会话 + * @return ChannelShell + */ + public ChannelShell openChannelShell(Session session) { + return new JschExecutor().openChannelShell(session); + } + + /** + * 将ChannelShell绑定到WsSession + * + * @param channelShell channelShell + * @param wsSession wsSession + */ + public void channelToWsSession(ChannelShell channelShell, WsSession wsSession) { + new JschExecutor().channelToWsSession(channelShell, wsSession); + } + + /** + * 创建一个ssh会话 + * + * @param sshLogin ssh登录信息 + * @return ssh会话 + */ + public Session createSession(SshLogin sshLogin) { + return new JschExecutor().createSession(sshLogin); + } + + /** + * 执行shell命令并自动填充人机交互响应 + * + * @param sshLogin ssh登录信息 + * @param command command + * @param envPath 环境变量配置文件路径 + * @param response 人机交互响应 + * @return 命令执行结果 + */ + public String execCommandAutoResponse(SshLogin sshLogin, String command, String envPath, + Map response) { + String envCommand = StrUtil.isNotEmpty(envPath) ? "source " + envPath + " && " + command : command; + return new JschExecutor().execCommandAutoResponse(sshLogin, envCommand, response); + } + + /** + * 获取openGasuus版本号 + * + * @param sshLogin ssh会话 + * @param envPath 环境路径 + * @return openGauss版本号 + */ + public String getOpenGaussMainVersionNum(SshLogin sshLogin, String envPath) { + String command = SshCommandConstants.OPENGAUSS_MAIN_VERSION_NUM; + String envCommand = StrUtil.isNotEmpty(envPath) ? "source " + envPath + " && " + command : command; + return new JschExecutor().execCommand(sshLogin, envCommand); + } + + /** + * 检查java版本号;java版本信息查询返回信息流JSCH ChannelExec ErrStream + * + * @param sshLogin ssh登录信息 + * @return java版本号 + */ + public String checkJavaVersion(SshLogin sshLogin) { + String command = SshCommandConstants.JAVA_VERSION; + return new JschExecutor().execCommandErrorStream(sshLogin, command); + } + + /** + * 检查路径是否为空,为空返回true + * + * @param sshLogin ssh登录信息 + * @param path 路径 + * @return true 路径为空,false 路径不为空 + */ + public boolean checkPathEmpty(SshLogin sshLogin, String path) { + String command = String.format(SshCommandConstants.CHECK_PATH_EMPTY, path); + String result = new JschExecutor().execCommandErrorStream(sshLogin, command); + if (result.contains("ls: cannot access") && result.contains("Permission denied")) { + throw new OpsException("checkPathEmpty error: ls cannot access, permission denied"); + } + // ls: cannot access '/data/software/openGauss': No such file or directory + boolean isEmpty = StrUtil.contains(result, SshCommandConstants.CHECK_RESULT_PATH_EMPTY) + || result.contains("ls: cannot access") && result.contains("No such file or directory"); + log.info("check {} path {} is empty: {}", sshLogin, path, isEmpty); + return isEmpty; + } + + /** + * 检查文件是否存在,存在返回true + * + * @param sshLogin ssh登录信息 + * @param file 文件绝对路径 + * @return true 文件存在,false 文件不存在 + */ + public boolean checkFileExist(SshLogin sshLogin, String file) { + String command = String.format(SshCommandConstants.CHECK_FILE_EXIST, file); + String result = new JschExecutor().execCommandErrorStream(sshLogin, command); + return StrUtil.contains(result, SshCommandConstants.CHECK_RESULT_FILE_EXIST); + } + + /** + * sftp file copy + * + * @param session session + * @param targetFile target file + * @param installPath file copy path + * @throws OpsException JSchException + */ + public void scp(Session session, String targetFile, String installPath) { + ChannelSftp sftpChannel = null; + try { + Channel channel = session.openChannel("sftp"); + if (channel instanceof ChannelSftp) { + sftpChannel = (ChannelSftp) channel; + sftpChannel.connect(5000); // 5秒超时 + sftpChannel.put(targetFile, installPath, ChannelSftp.OVERWRITE); + String jarName = new File(targetFile).getName(); + String verifyCmd = String.format("[ -f \"%s/%s\" ] && echo 'ok' || echo 'fail'", installPath, jarName); + String verifyResult = execCommand(session, verifyCmd); + if (!"ok".equals(verifyResult.trim())) { + throw new OpsException("file check failed :" + session.getHost() + " fileName:" + targetFile); + } + } else { + if (channel != null) { + channel.disconnect(); + } + throw new JSchException("cannot open sftp channel" + session.getHost()); + } + } catch (JSchException | SftpException e) { + throw new OpsException(e.getMessage()); + } finally { + if (sftpChannel != null) { + sftpChannel.disconnect(); + } + } + } + + /** + * sftp file copy + * + * @param session session + * @param content file content + * @param installPath install path + */ + public void remoteWrite(Session session, String content, String installPath) { + ChannelSftp sftpChannel = null; + try { + Channel channel = session.openChannel("sftp"); + if (channel instanceof ChannelSftp) { + sftpChannel = (ChannelSftp) channel; + sftpChannel.connect(5000); + ByteArrayInputStream inputStream = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); + sftpChannel.put(inputStream, installPath, ChannelSftp.OVERWRITE); + } else { + if (channel != null) { + channel.disconnect(); + } + throw new JSchException("cannot open sftp channel" + session.getHost()); + } + } catch (JSchException | SftpException e) { + throw new OpsException(e.getMessage()); + } finally { + if (sftpChannel != null) { + sftpChannel.disconnect(); + } + } + } + + /** + * execute with session + * + * @param sshLogin ssh login info + * @param action action + */ + public void executeWithSession(SshLogin sshLogin, Consumer action) { + Session session = null; + try { + session = createSession(sshLogin); + if (!session.isConnected()) { + session.connect(3000); + } + action.accept(session); + } catch (JSchException | OpsException e) { + log.error("SSH create session error:", e); + throw new OpsException("AGENT_UPDATE_ERROR: execute jsch task failed " + e.getMessage()); + } finally { + if (session != null) { + session.disconnect(); + } + } + } + + /** + * Jsch 执行命令器 + */ + static class JschExecutor { + private static final int SESSION_TIMEOUT = 10000; + private static final int CHANNEL_TIMEOUT = 50000; + + private WsConnectorManager wsConnectorManager = null; + + /** + * Jsch 执行命令器 构造函数 + */ + public JschExecutor() { + } + + /** + * Jsch 执行命令器 构造函数 + * + * @param wsConnectorManager wsConnectorManager + */ + public JschExecutor(WsConnectorManager wsConnectorManager) { + this.wsConnectorManager = wsConnectorManager; + } + + /** + * create session + * + * @param sshLogin sshLogin + * @return session session + * @throws OpsException OpsException + */ + public Session createSession(SshLogin sshLogin) throws OpsException { + Session session = null; + try { + JSch jsch = new JSch(); + // 创建与远程服务器的会话 + session = jsch.getSession(sshLogin.getUsername(), sshLogin.getHost(), sshLogin.getPort()); + // 设置会话的密码 + session.setPassword(sshLogin.getPassword()); + // 设置一些配置,例如不检查主机密钥 + Properties config = new Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config); + // 连接会话 + session.connect(SESSION_TIMEOUT); + } catch (JSchException e) { + String errorMessage = String.format("create jsch session error: %s", sshLogin); + throw new OpsException(errorMessage); + } + return session; + } + + /** + * open channel shell + * + * @param session session + * @return ChannelShell + */ + public ChannelShell openChannelShell(Session session) { + ChannelShell channel = null; + try { + Channel channelShell = session.openChannel("shell"); + if (channelShell instanceof ChannelShell) { + channel = (ChannelShell) channelShell; + } + channel.setPtySize(200, 40, 200 * 8, 40 * 8); + channel.connect(CHANNEL_TIMEOUT); + } catch (JSchException e) { + log.error("Channel establishment Exception", e); + throw new OpsException("Channel establishment Exception"); + } + return channel; + } + + /** + * channel to ws session + * + * @param channel channel + * @param wsSession ws session + */ + public void channelToWsSession(Channel channel, WsSession wsSession) { + try (InputStream inputStream = channel.getInputStream()) { + // Cyclic reading + byte[] buffer = new byte[1024]; + int len; + // If there is no data coming, the thread will remain blocked at this point waiting for data. + while ((len = inputStream.read(buffer)) != -1) { + byte[] bytes = Arrays.copyOfRange(buffer, 0, len); + wsConnectorManager.getSession(wsSession.getBusinessId()).ifPresent(newSession -> { + try { + newSession.getSession() + .getBasicRemote() + .sendText(new String(bytes, StandardCharsets.UTF_8)); + } catch (IOException e) { + log.error("Failed to send message [{}] to websocket:", + new String(bytes, StandardCharsets.UTF_8), e); + } + }); + } + } catch (IOException e) { + log.error("Description Failed to send a message to the websocket:", e); + } finally { + wsSession.close(); + channel.disconnect(); + } + } + + /** + * exec command and session will be created and closed + * + * @param sshLogin sshLogin + * @param command command + * @return result + */ + public String execCommand(SshLogin sshLogin, String command) { + SessionConfig execConfig = new SessionConfig(sshLogin.getHost(), sshLogin.getUsername(), + sshLogin.getPassword()).withChannelType(ChannelType.EXEC).withTimeout(15000); + try { + ExecOperations.ExecResult result = ExecOperations.executeCommand(execConfig, command); + return result.getResult(); + } catch (Exception ex) { + throw new OpsException( + "exec command failed, sshLogin:" + sshLogin + " command:" + command + " ex:" + ex.getMessage()); + } + } + + /** + * exec commands and session will be created and closed + * + * @param sshLogin sshLogin + * @param commands commands + * @return result + */ + public String execCommands(SshLogin sshLogin, String commands) { + SessionConfig execConfig = new SessionConfig(sshLogin.getHost(), sshLogin.getUsername(), + sshLogin.getPassword()).withChannelType(ChannelType.EXEC).withTimeout(15000); + try { + List results = ExecOperations.executeCommands(execConfig, commands); + results.stream().filter(result -> result.getExitCode() != 0).findFirst().map(result -> { + throw new OpsException( + "exec command failed, sshLogin:" + sshLogin + " command:" + commands + " ex: " + + result.getError().toString()); + }); + return results.stream().map(ExecOperations.ExecResult::getOutput).collect(Collectors.joining()); + } catch (Exception ex) { + throw new OpsException( + "exec command failed, sshLogin:" + sshLogin + " command:" + commands + " ex:" + ex.getMessage()); + } + } + + /** + * exec command and fetch result from error stream + * + * @param sshLogin sshLogin + * @param command command + * @return result + */ + public String execCommandErrorStream(SshLogin sshLogin, String command) { + Session session = null; + try { + session = createSession(sshLogin); + return execCommandErrorStream(session, command); + } catch (OpsException e) { + throw new OpsException(e.getMessage()); + } finally { + // 断开会话 + if (Objects.nonNull(session)) { + session.disconnect(); + } + } + } + + /** + * exec command by session + * + * @param session session + * @param command command + * @return result + */ + public String execCommand(Session session, String command) { + Channel channel = null; + StringBuilder result = new StringBuilder(); + try { + if (!session.isConnected()) { + Properties config = new Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config); + session.connect(SESSION_TIMEOUT); + } + channel = session.openChannel("exec"); + if (channel instanceof ChannelExec) { + ((ChannelExec) channel).setCommand(command); + } + try (InputStream in = channel.getInputStream(); + InputStream err = ((ChannelExec) channel).getErrStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); + BufferedReader errorReader = new BufferedReader( + new InputStreamReader(err, StandardCharsets.UTF_8))) { + channel.connect(); + String line; + while ((line = reader.readLine()) != null) { + result.append(line.trim()).append(CommonConstants.LINE_SPLITTER); + } + deleteCharAtEndOfStringBuffer(result); + StringBuilder errorResult = new StringBuilder(); + String errorLine; + while ((errorLine = errorReader.readLine()) != null) { + errorResult.append(errorLine.trim()).append(CommonConstants.LINE_SPLITTER); + } + deleteCharAtEndOfStringBuffer(errorResult); + int exitStatus = channel.getExitStatus(); + if (exitStatus != 0) { + log.error("Command: {} execution failed with exit status: {}", command, exitStatus); + log.error("Error output: {}", errorResult.toString().trim()); + } + } + } catch (JSchException | IOException e) { + throw new OpsException(e.getMessage()); + } finally { + if (Objects.nonNull(channel)) { + channel.disconnect(); + } + } + return result.toString().trim(); + } + + /** + * exec command and fetch result from error stream + * + * @param session session + * @param command command + * @return result + */ + public String execCommandErrorStream(Session session, String command) { + Channel channel = null; + StringBuilder result = new StringBuilder(); + try { + if (!session.isConnected()) { + Properties config = new Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config); + session.connect(SESSION_TIMEOUT); + } + channel = session.openChannel("exec"); + if (channel instanceof ChannelExec) { + ((ChannelExec) channel).setCommand(command); + } + try (InputStream in = channel.getInputStream(); + InputStream err = ((ChannelExec) channel).getErrStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); + BufferedReader errorReader = new BufferedReader( + new InputStreamReader(err, StandardCharsets.UTF_8))) { + channel.connect(); + String errorLine; + while ((errorLine = reader.readLine()) != null) { + result.append(errorLine.trim()).append(CommonConstants.LINE_SPLITTER); + } + while ((errorLine = errorReader.readLine()) != null) { + result.append(errorLine.trim()).append(CommonConstants.LINE_SPLITTER); + } + deleteCharAtEndOfStringBuffer(result); + int exitStatus = channel.getExitStatus(); + if (exitStatus != 0) { + log.error("Command: {} execution failed with exit status: {}", command, exitStatus); + } + } + } catch (JSchException | IOException e) { + throw new OpsException(e.getMessage()); + } finally { + if (Objects.nonNull(channel)) { + channel.disconnect(); + } + } + return result.toString().trim(); + } + + private void deleteCharAtEndOfStringBuffer(StringBuilder buffer) { + if (buffer.length() > 0) { + buffer.deleteCharAt(buffer.length() - 1); + } + } + + /** + * SSH执行命令 + * + * @param sshLogin ssh连接信息 + * @param command 命令 + * @param response 人机交互响应 + * @return 命令执行结果 + */ + public String execCommandAutoResponse(SshLogin sshLogin, String command, Map response) { + ChannelExec channel = null; + Session session = null; + try { + session = createSession(sshLogin); + Channel exeChannel = session.openChannel("exec"); + if (exeChannel instanceof ChannelExec) { + channel = (ChannelExec) exeChannel; + } + channel.setPtyType("dump"); + channel.setPty(true); + channel.setCommand(command); + channel.connect(CHANNEL_TIMEOUT); + return buildJschResult(channel, response); + } catch (JSchException | IOException e) { + log.error("execCommandAutoResponse error :", e); + throw new OpsException("Command execution exception"); + } finally { + if (channel != null) { + channel.disconnect(); + } + if (session != null) { + session.disconnect(); + } + } + } + + private String buildJschResult(ChannelExec channelExec, Map autoResponse) throws IOException { + StringBuilder resultStrBuilder = new StringBuilder(); + // The output of the script execution is an input stream to the program + InputStream in = channelExec.getInputStream(); + OutputStream out = channelExec.getOutputStream(); + // Read the input stream from the remote host and get the script execution results + byte[] tmp = new byte[1024]; + while (true) { + while (in.available() > 0) { + int len = in.read(tmp, 0, 1024); + if (len < 0) { + break; + } + resultStrBuilder.append(new String(tmp, 0, len, StandardCharsets.UTF_8)); + } + if (channelExec.isClosed()) { + if (in.available() > 0) { + continue; + } + in.close(); + out.close(); + int exitStatus = channelExec.getExitStatus(); + if (exitStatus != 0) { + throw new OpsException("Remote command execution failed, exit status: " + exitStatus); + } + break; + } + autoResponse(autoResponse, resultStrBuilder, out); + } + channelExec.disconnect(); + return resultStrBuilder.toString().trim(); + } + + private void autoResponse(Map autoResponse, StringBuilder resultStrBuilder, OutputStream out) { + if (CollUtil.isNotEmpty(autoResponse)) { + autoResponse.forEach((k, v) -> { + if (resultStrBuilder.toString().trim().endsWith(k.trim())) { + try { + out.write( + (v.trim() + System.getProperty("line.separator")).getBytes(StandardCharsets.UTF_8)); + out.flush(); + resultStrBuilder.append(v.trim() + System.getProperty("line.separator")); + } catch (IOException e) { + log.error("Automatic response exception", e); + } + } + }); + } + } + } + + @PreDestroy + private void destroy() { + JschSessionPool.closeAllPools(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysLogServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysLogServiceImpl.java new file mode 100644 index 000000000..1d1ced2c4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysLogServiceImpl.java @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysLogServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysLogServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.appender.RollingFileAppender; +import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy; +import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.core.config.LoggerConfig; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.opengauss.admin.common.constant.LogConstants; +import org.opengauss.admin.common.core.dto.SysLogConfigDto; +import org.opengauss.admin.common.core.vo.SysLogConfigVo; +import org.opengauss.admin.system.domain.SysLogConfig; +import org.opengauss.admin.system.mapper.SysLogConfigMapper; +import org.opengauss.admin.system.service.ISysLogService; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * System log service + */ +@Slf4j +@Service +public class SysLogServiceImpl implements ISysLogService { + @Value("${logging.file.path}") + private String loggingFilePath; + + @Autowired + private SysLogConfigMapper sysLogConfigMapper; + + /** + * Override log config from table sys_log_config + */ + @Override + public void init() { + String level = getConfigByKey(LogConstants.SYS_LOG_CONFIG_KEY_LEVEL); + String maxFileSize = getConfigByKey(LogConstants.SYS_LOG_CONFIG_KEY_MAX_FILE_SIZE); + String totalSizeCap = getConfigByKey(LogConstants.SYS_LOG_CONFIG_KEY_TOTAL_SIZE_CAP); + String maxHistory = getConfigByKey(LogConstants.SYS_LOG_CONFIG_KEY_MAX_HISTORY); + if (level == null) { + level = LogConstants.DEFAULT_LEVEL; + saveLogConfig(LogConstants.SYS_LOG_CONFIG_KEY_LEVEL, level); + } + if (maxHistory == null) { + maxHistory = LogConstants.DEFAULT_MAX_HISTORY.toString(); + saveLogConfig(LogConstants.SYS_LOG_CONFIG_KEY_MAX_HISTORY, maxHistory); + } + if (maxFileSize == null) { + maxFileSize = LogConstants.DEFAULT_MAX_FILE_SIZE; + saveLogConfig(LogConstants.SYS_LOG_CONFIG_KEY_MAX_FILE_SIZE, maxFileSize); + } + if (totalSizeCap == null) { + totalSizeCap = LogConstants.DEFAULT_TOTAL_SIZE_CAP; + saveLogConfig(LogConstants.SYS_LOG_CONFIG_KEY_TOTAL_SIZE_CAP, totalSizeCap); + } + changeLogLevel(level); + changeLogAppender(totalSizeCap, maxFileSize, Integer.parseInt(maxHistory)); + } + + /** + * Change logback print level + * + * @param level the log level + */ + public void changeLogLevel(String level) { + Configurator.setRootLevel(org.apache.logging.log4j.Level.valueOf(level)); + if (LogManager.getContext(false) instanceof LoggerContext ctx) { + for (LoggerConfig loggerConfig : ctx.getConfiguration().getLoggers().values()) { + loggerConfig.setLevel(org.apache.logging.log4j.Level.valueOf(level)); + } + ctx.updateLoggers(); + } + } + + /** + * Change default logback appender + * + * @param totalSizeCap + * @param maxFileSize + * @param maxHistory + */ + public void changeLogAppender(String totalSizeCap, String maxFileSize, Integer maxHistory) { + if (LogManager.getContext(false) instanceof LoggerContext ctx) { + Configuration config = ctx.getConfiguration(); + SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy(maxFileSize); + DefaultRolloverStrategy rolloverStrategy = DefaultRolloverStrategy.newBuilder() + .withMax(Integer.toString(maxHistory)) + .build(); + String filePattern = loggingFilePath + File.separator + "sys-%d{yyyy-MM-dd}-%i.log"; + RollingFileAppender appender = RollingFileAppender.newBuilder() + .withFileName(loggingFilePath + File.separator + "sys.log") + .withFilePattern(filePattern) + .withPolicy(sizePolicy) + .withStrategy(rolloverStrategy) + .setLayout(PatternLayout.createDefaultLayout(config)) + .setName("RollingFile") + .setConfiguration(config) + .build(); + appender.start(); + config.addAppender(appender); + LoggerConfig rootLogger = config.getRootLogger(); + rootLogger.addAppender(appender, null, null); + ctx.updateLoggers(); + } + } + + /** + * Save and apply all log config + * @param dto + */ + public void saveAllLogConfig(SysLogConfigDto dto) { + changeLogLevel(dto.getLevel()); + saveLogConfig(LogConstants.SYS_LOG_CONFIG_KEY_LEVEL, dto.getLevel()); + changeLogAppender(dto.getTotalSizeCap(), dto.getMaxFileSize(), dto.getMaxHistory()); + saveLogConfig(LogConstants.SYS_LOG_CONFIG_KEY_MAX_FILE_SIZE, dto.getMaxFileSize()); + saveLogConfig(LogConstants.SYS_LOG_CONFIG_KEY_TOTAL_SIZE_CAP, dto.getTotalSizeCap()); + saveLogConfig(LogConstants.SYS_LOG_CONFIG_KEY_MAX_HISTORY, dto.getMaxHistory().toString()); + } + + /** + * Get all log config + * @return SysLogConfigVo + */ + public SysLogConfigVo getAllLogConfig() { + String level = getConfigByKey(LogConstants.SYS_LOG_CONFIG_KEY_LEVEL); + String maxFileSize = getConfigByKey(LogConstants.SYS_LOG_CONFIG_KEY_MAX_FILE_SIZE); + String totalSizeCap = getConfigByKey(LogConstants.SYS_LOG_CONFIG_KEY_TOTAL_SIZE_CAP); + String maxHistory = getConfigByKey(LogConstants.SYS_LOG_CONFIG_KEY_MAX_HISTORY); + SysLogConfigVo sysLogConfigVo = new SysLogConfigVo(); + sysLogConfigVo.setLevel(level); + sysLogConfigVo.setMaxFileSize(maxFileSize); + sysLogConfigVo.setTotalSizeCap(totalSizeCap); + if(null != maxHistory){ + sysLogConfigVo.setMaxHistory(Integer.parseInt(maxHistory)); + } + return sysLogConfigVo; + } + + /** + * save system config data + * @param key the config key + * @param value the config value + */ + @Override + public void saveLogConfig(String key, String value) { + SysLogConfig config = new SysLogConfig(); + config.setKey(key); + config.setValue(value); + String exist = getConfigByKey(key); + if(null != exist){ + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysLogConfig::getKey,key); + sysLogConfigMapper.update(config, queryWrapper); + }else { + sysLogConfigMapper.insert(config); + } + } + + /** + * get the config value by config key + * @param key the config key + * @return the config value + */ + @Override + public String getConfigByKey(String key) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysLogConfig::getKey,key); + SysLogConfig config = sysLogConfigMapper.selectOne(queryWrapper); + if(config == null){ + return null; + } + return config.getValue(); + } + + /** + * List all log files in the log directory + * @return FileList + */ + @Override + public List> listAllLogFile() { + File dir = new File(loggingFilePath); + File[] childrenFiles = dir.listFiles(); + List> files = new ArrayList<>(); + if(null != childrenFiles){ + for (File childFile : childrenFiles) { + if (childFile.isFile()) { + Map file = new HashMap<>(); + file.put("name",childFile.getName()); + file.put("size", childFile.length()); + try { + BasicFileAttributes attrs = Files.readAttributes(childFile.toPath(), BasicFileAttributes.class); + file.put("createdAt", attrs.creationTime().toMillis()); + file.put("updatedAt", attrs.lastModifiedTime().toMillis()); + } catch (IOException e) { + log.error("list all log error, message: {}", e.getMessage()); + } + files.add(file); + } + } + } + return files; + } + + /** + * Get single log file by filename + * @param filename the filename + * @return File + */ + @Override + public File getLogFileByName(String filename) { + return new File(loggingFilePath + File.separator + filename); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysMenuServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 000000000..7ce707257 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,552 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysMenuServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysMenuServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.constant.UserConstants; +import org.opengauss.admin.common.core.domain.TreeSelect; +import org.opengauss.admin.common.core.domain.entity.SysMenu; +import org.opengauss.admin.common.core.domain.entity.SysRole; +import org.opengauss.admin.common.core.domain.entity.SysUser; +import org.opengauss.admin.common.enums.SysLanguage; +import org.opengauss.admin.common.enums.SysMenuPluginComponent; +import org.opengauss.admin.common.enums.SysMenuStatus; +import org.opengauss.admin.common.enums.SysMenuVisible; +import org.opengauss.admin.system.domain.SysRoleMenu; +import org.opengauss.admin.common.core.vo.MetaVo; +import org.opengauss.admin.common.core.vo.RouterVo; +import org.opengauss.admin.system.mapper.SysMenuMapper; +import org.opengauss.admin.system.mapper.SysRoleMapper; +import org.opengauss.admin.system.mapper.SysRoleMenuMapper; +import org.opengauss.admin.system.service.ISysMenuService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * menu service + * + * @author xielibo + */ +@Service +@Slf4j +public class SysMenuServiceImpl extends ServiceImpl implements ISysMenuService { + @Autowired + private SysMenuMapper menuMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + /** + * Query the system menu list according to the user + * + * @param userId user ID + * @return menu list + */ + @Override + public List selectMenuList(Integer userId) { + return selectMenuList(new SysMenu(), userId); + } + + /** + * Query menu list + * + * @param menu menu + * @param userId user ID + * @return menu list + */ + @Override + public List selectMenuList(SysMenu menu, Integer userId) { + List menuList = null; + if (SysUser.isAdmin(userId)) { + LambdaQueryWrapper query = new QueryWrapper().lambda(); + query.like(StringUtils.isNotEmpty(menu.getMenuName()), SysMenu::getMenuName, menu.getMenuName()); + query.eq(StringUtils.isNotEmpty(menu.getVisible()), SysMenu::getVisible, menu.getVisible()); + query.eq(StringUtils.isNotEmpty(menu.getStatus()), SysMenu::getStatus, menu.getStatus()); + query.orderByAsc(SysMenu::getParentId, SysMenu::getOrderNum); + menuList = menuMapper.selectList(query); + } else { + menu.getParams().put("userId", userId); + menuList = menuMapper.selectMenuListByUserId(menu); + } + return menuList; + } + + + /** + * get special routers + */ + @Override + public List selectSpecialRouteList(Integer openPosition) { + LambdaQueryWrapper query = new QueryWrapper().lambda(); + query.eq(SysMenu::getVisible, SysMenuVisible.HIDE.getCode()); + query.eq(openPosition != null, SysMenu::getOpenPosition, openPosition); + query.orderByDesc(SysMenu::getCreateTime); + return menuMapper.selectList(query); + } + + /** + * Determine whether the menu exists + * @param parentId + * @param pluginId + * @param path + * @param menuName + * @return + */ + @Override + public SysMenu existsMenu(Integer parentId, String pluginId, String path, String menuName) { + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(parentId != null, SysMenu::getParentId, parentId); + query.eq(StringUtils.isNotBlank(pluginId), SysMenu::getPluginId, pluginId); + query.eq(StringUtils.isNotBlank(path), SysMenu::getPath, path); + query.eq(StringUtils.isNotBlank(menuName), SysMenu::getMenuName, menuName); + List list = menuMapper.selectList(query); + return list.size() > 0 ? list.get(0) : null; + } + + /** + * Query permission list based on user ID + * + * @param userId user ID + * @return permission list + */ + @Override + public Set selectMenuPermsByUserId(Integer userId) { + List perms = menuMapper.selectMenuPermsByUserId(userId); + Set permsSet = new HashSet<>(); + for (String perm : perms) { + if (StringUtils.isNotEmpty(perm)) { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * Query menu list by user ID + * + * @param userId user ID + * @return menu list + */ + @Override + public List selectMenuTreeByUserId(Integer userId) { + List menus = null; + if (SysUser.isAdmin(userId)) { + LambdaQueryWrapper q = new QueryWrapper().lambda().in(SysMenu::getMenuType, UserConstants.TYPE_DIR, UserConstants.TYPE_MENU).eq(SysMenu::getStatus, UserConstants.NORMAL).orderByAsc(SysMenu::getParentId, SysMenu::getOrderNum); + menus = menuMapper.selectList(q); + } else { + menus = menuMapper.selectMenuTreeByUserId(userId); + menus.addAll(selectSpecialRouteList(null)); + } + return getChildPerms(menus, 0); + } + + /** + * Query menu tree information based on role ID + * + * @param roleId role ID + * @return menu IDs + */ + @Override + public List selectMenuListByRoleId(Integer roleId) { + SysRole role = roleMapper.selectRole(new QueryWrapper().eq("r.role_id", roleId)); + return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly()); + } + + /** + * The menu needed to build the front-end routing + * + * @param menus menu list + * @param language current locale + * @return router list + */ + @Override + public List buildMenus(List menus, String language) { + List routers = new LinkedList(); + for (SysMenu menu : menus) { + RouterVo router = new RouterVo(); + router.setName(getRouteName(menu)); + router.setPath(getRouterPath(menu)); + router.setComponent(getComponent(menu)); + List cMenus = menu.getChildren(); + router.setIsPluginMenu(StringUtils.isNotBlank(menu.getPluginId())); + if (StringUtils.isNotBlank(menu.getPluginTheme())) { + Map routeQuery = Maps.newHashMap(); + routeQuery.put("theme", menu.getPluginTheme()); + router.setQuery(routeQuery); + } + String menuName = SysLanguage.EN.getCode().equals(language) ? menu.getMenuEnName() : menu.getMenuName(); + router.setMeta(new MetaVo(menuName, menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getOrderNum(), cMenus.size() == 0,"1".equals(menu.getVisible()))); + router.setMenuClassify(menu.getMenuClassify()); + if (!cMenus.isEmpty() && cMenus.size() > 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType())) { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus,language)); + } else if (isMeunFrame(menu)) { + router.setRedirect(menu.getPath()); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponent()); + children.setName(getRouteName(menu)); + children.setMeta(new MetaVo(menuName, menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getOrderNum(), cMenus.size() == 0,"1".equals(menu.getVisible()))); + children.setIsPluginMenu(StringUtils.isNotBlank(menu.getPluginId())); + children.setMenuClassify(menu.getMenuClassify()); + if (StringUtils.isNotBlank(menu.getPluginTheme())) { + Map routeQuery = Maps.newHashMap(); + routeQuery.put("theme", menu.getPluginTheme()); + children.setQuery(routeQuery); + } + childrenList.add(children); + router.setChildren(childrenList); + } + routers.add(router); + } + return routers; + } + + /** + * Build the menu tree structure required by the front end + * + * @param menus menus + * @return menu tree list + */ + @Override + public List buildMenuTree(List menus) { + List returnList = new ArrayList(); + List tempList = new ArrayList(); + for (SysMenu hospital : menus) { + tempList.add(hospital.getMenuId()); + } + for (Iterator iterator = menus.iterator(); iterator.hasNext(); ) { + SysMenu menu = (SysMenu) iterator.next(); + if (!tempList.contains(menu.getParentId())) { + recursionFn(menus, menu); + returnList.add(menu); + } + } + if (returnList.isEmpty()) { + returnList = menus; + } + return returnList; + } + + /** + * Build the menu tree select structure required by the front end + * + * @param menus menu list + * @return tree select + */ + @Override + public List buildMenuTreeSelect(List menus) { + List menuTrees = buildMenuTree(menus); + return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * get by ID + * + * @param menuId Menu ID + * @return menu + */ + @Override + public SysMenu selectMenuById(Integer menuId) { + return menuMapper.selectById(menuId); + } + + /** + * Determine if a menu exists as a submenu + * + * @param menuId Menu ID + */ + @Override + public boolean hasChildByMenuId(Integer menuId) { + int result = menuMapper.selectCount(new QueryWrapper().lambda().eq(SysMenu::getParentId, menuId)).intValue(); + return result > 0 ? true : false; + } + + /** + * According to the plugin, get the parent menu that has other plugin submenus + * @param pluginId + * @return + */ + @Override + public Integer countMenuHasOtherPluginSubmenuByPluginId(String pluginId) { + return menuMapper.countParentMenuHasOtherPluginSubmenusByPluginId(pluginId); + } + + @Override + public Integer countMenuHasOtherEnablePluginSubmenuByPluginId(String pluginId) { + return menuMapper.countParentMenuHasOtherEnablePluginSubmenusByPluginId(pluginId); + } + + /** + * Determine whether the menu is assigned + * + * @param menuId Menu ID + */ + @Override + public boolean checkMenuExistRole(Integer menuId) { + int result = roleMenuMapper.selectCount(new QueryWrapper().lambda().eq(SysRoleMenu::getMenuId, menuId)).intValue(); + return result > 0 ? true : false; + } + + /** + * save menu + */ + @Override + public int insertMenu(SysMenu menu) { + return menuMapper.insert(menu); + } + + /** + * update menu + */ + @Override + public int updateMenu(SysMenu menu) { + return menuMapper.updateById(menu); + } + + /** + * delete menu + */ + @Override + public int deleteMenuById(Integer menuId) { + return menuMapper.deleteById(menuId); + } + + + /** + * delete by pluginId + * + * @param pluginId pluginId + * @return result + */ + @Override + public int deleteByPluginId(String pluginId) { + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(StringUtils.isNotBlank(pluginId), SysMenu::getPluginId, pluginId); + return menuMapper.delete(query); + } + + /** + * update menu disable by pluginId + * + * @param pluginId pluginId + * @return result + */ + @Override + public int updateDisableByPluginId(String pluginId) { + System.out.println("pluginId==" + pluginId); + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(StringUtils.isNotBlank(pluginId), SysMenu::getPluginId, pluginId); + SysMenu update = new SysMenu(); + update.setStatus(SysMenuStatus.DISABLE.getCode()); + return menuMapper.update(update, query); + } + + /** + * Check if the menu is unique + */ + @Override + public String checkMenuNameUnique(SysMenu menu) { + Integer menuId = menu.getMenuId() == null ? 1 : menu.getMenuId(); + SysMenu info = menuMapper.selectOne(new QueryWrapper().lambda().eq(SysMenu::getMenuName, menu.getMenuName()).eq(SysMenu::getParentId, menu.getParentId())); + if (!Objects.isNull(info) && !info.getMenuId().equals(menuId)) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * get router name + */ + public String getRouteName(SysMenu menu) { + String[] paths = menu.getPath().split("\\/"); + StringBuffer sb = new StringBuffer(); + for (String path : paths) { + if (StringUtils.isNotBlank(path)) { + sb.append(StringUtils.capitalize(path)); + } + } + return sb.toString(); + } + + /** + * get router path + * + * @param menu + */ + public String getRouterPath(SysMenu menu) { + String routerPath = menu.getPath(); + if (0 == menu.getParentId() && UserConstants.TYPE_DIR.equals(menu.getMenuType()) + && UserConstants.NO_FRAME.equals(menu.getIsFrame())) { + routerPath = menu.getPath(); + } + return routerPath; + } + + /** + * get menu component + */ + public String getComponent(SysMenu menu) { + String component = UserConstants.LAYOUT; + if(!isPluginMenu(menu)){ + if (StringUtils.isNotEmpty(menu.getComponent()) && !isMeunFrame(menu)) { + component = menu.getComponent(); + } else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) { + component = UserConstants.PARENT_VIEW; + } + } else { + if(0 != menu.getParentId() && UserConstants.TYPE_MENU.equals(menu.getMenuType())){ + component = menu.getComponent(); + } + } + return component; + } + + public boolean isMeunFrame(SysMenu menu) { + return 0 == menu.getParentId() && UserConstants.TYPE_MENU.equals(menu.getMenuType()) + && menu.getIsFrame().equals(UserConstants.NO_FRAME); + } + + public boolean isPluginMenu(SysMenu menu) { + return SysMenuPluginComponent.PAGE_IFRAME.getCode().equals(menu.getComponent()); + } + + public boolean isParentView(SysMenu menu) { + return 0 != menu.getParentId() && UserConstants.TYPE_DIR.equals(menu.getMenuType()); + } + + /** + * get submenu + */ + public List getChildPerms(List list, Integer parentId) { + List returnList = new ArrayList(); + for (Iterator iterator = list.iterator(); iterator.hasNext(); ) { + SysMenu t = (SysMenu) iterator.next(); + if (t.getParentId().equals(parentId)) { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + private void recursionFn(List list, SysMenu t) { + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysMenu tChild : childList) { + if (hasChild(list, tChild)) { + recursionFn(list, tChild); + } + } + } + + private List getChildList(List list, SysMenu t) { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) { + SysMenu n = (SysMenu) it.next(); + if (n.getParentId().equals(t.getMenuId())) { + tlist.add(n); + } + } + return tlist; + } + + private boolean hasChild(List list, SysMenu t) { + return getChildList(list, t).size() > 0 ? true : false; + } + + /** + * update plugin menu theme + * + * @param pluginId pluginId + * @param theme theme + */ + @Override + public void updatePluginMenuTheme(String pluginId, String theme) { + if (StringUtils.isNotBlank(pluginId) && StringUtils.isNotBlank(theme)) { + SysMenu menu = new SysMenu(); + menu.setPluginTheme(theme); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysMenu::getPluginId, pluginId); + this.update(menu, queryWrapper); + } + } + + /** + * update plugin menu icon + * + * @param pluginId pluginId + * @param icon icon + */ + @Override + public void updatePluginMenuIcon(String pluginId, String icon) { + if (StringUtils.isNotBlank(pluginId) && StringUtils.isNotBlank(icon)) { + SysMenu menu = new SysMenu(); + menu.setIcon(icon); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysMenu::getPluginId, pluginId); + this.update(menu, queryWrapper); + } + } + + /** + * update plugin menu icon + * + * @param pluginId pluginId + * @param icon icon + */ + @Override + public void updatePluginFatherMenuIcon(String pluginId, String icon) { + if (StringUtils.isNotBlank(pluginId) && StringUtils.isNotBlank(icon)) { + List menuIds = menuMapper.selectParentMenuIdByPluginId(pluginId); + log.info("pluginId:{},menuids:{}", pluginId, menuIds); + if (menuIds.isEmpty()) { + return; + } + menuIds.forEach(menuId -> { + SysMenu menu = new SysMenu(); + menu.setIcon(icon); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysMenu::getMenuId, menuId); + this.update(menu, queryWrapper); + }); + } + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysOperLogServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 000000000..158631436 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysOperLogServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysOperLogServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.opengauss.admin.common.constant.UserConstants; +import org.opengauss.admin.system.domain.SysOperLog; +import org.opengauss.admin.system.mapper.SysOperLogMapper; +import org.opengauss.admin.system.service.ISysOperLogService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Arrays; + +/** + * OperLog Service + * + * @author xielibo + */ +@Service +public class SysOperLogServiceImpl implements ISysOperLogService { + + @Autowired + private SysOperLogMapper operLogMapper; + + /** + * Save Operlog + * + */ + @Override + public void insertOperlog(SysOperLog operLog) { + operLogMapper.insert(operLog); + } + + /** + * Query the collection of system operation logs + * + * @param operLog operLog + */ + @Override + public IPage selectOperLogList(IPage page, SysOperLog operLog) { + LambdaQueryWrapper query = new QueryWrapper().lambda(); + query.like(StringUtils.isNotEmpty(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle()); + query.eq(operLog.getBusinessType() != null, SysOperLog::getBusinessType, operLog.getBusinessType()); + query.in(operLog.getBusinessTypes() != null, SysOperLog::getBusinessType, operLog.getBusinessTypes()); + query.eq(operLog.getStatus() != null, SysOperLog::getStatus, operLog.getStatus()); + query.like(StringUtils.isNotEmpty(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName()); + query.orderByDesc(SysOperLog::getOperId); + return operLogMapper.selectPage(page, query); + } + + /** + * Delete System Operation Logs in Batches + * + * @param operIds operIds + */ + @Override + public int deleteOperLogByIds(String[] operIds) { + return operLogMapper.deleteBatchIds(Arrays.asList(operIds)); + } + + /** + * Query operation log details + * + * @param operId operId + */ + @Override + public SysOperLog selectOperLogById(String operId) { + return operLogMapper.selectById(operId); + } + + /** + * clear operation log + */ + @Override + public void cleanOperLog() { + operLogMapper.delete(new QueryWrapper().lambda().eq(SysOperLog::getStatus, UserConstants.NORMAL).or().eq(SysOperLog::getStatus, UserConstants.EXCEPTION)); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginConfigDataServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginConfigDataServiceImpl.java new file mode 100644 index 000000000..fd24b567a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginConfigDataServiceImpl.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginConfigDataServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginConfigDataServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.system.domain.SysPluginConfigData; +import org.opengauss.admin.system.mapper.SysPluginConfigDataMapper; +import org.opengauss.admin.system.service.ISysPluginConfigDataService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author xielibo + */ +@Service +public class SysPluginConfigDataServiceImpl extends ServiceImpl implements ISysPluginConfigDataService { + + @Autowired + private SysPluginConfigDataMapper sysPluginConfigDataMapper; + + @Override + public void savePluginConfigData(String pluginId, String data) { + SysPluginConfigData configData = getByPluginId(pluginId); + if (configData == null) { + configData = new SysPluginConfigData(); + } + configData.setConfigData(data); + configData.setPluginId(pluginId); + this.saveOrUpdate(configData); + } + + + public SysPluginConfigData getByPluginId(String pluginId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysPluginConfigData::getPluginId, pluginId); + List list = sysPluginConfigDataMapper.selectList(queryWrapper); + if (list.size() > 0) { + return list.get(0); + } + return null; + } + + @Override + public String getDataByPluginId(String pluginId) { + SysPluginConfigData configData = getByPluginId(pluginId); + if (configData == null) { + return null; + } + return configData.getConfigData(); + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginConfigServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginConfigServiceImpl.java new file mode 100644 index 000000000..68b6274e4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginConfigServiceImpl.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginConfigServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginConfigServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.system.domain.SysPluginConfig; +import org.opengauss.admin.system.mapper.SysPluginConfigMapper; +import org.opengauss.admin.system.service.ISysPluginConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @author xielibo + */ +@Service +public class SysPluginConfigServiceImpl extends ServiceImpl implements ISysPluginConfigService { + + @Autowired + private SysPluginConfigMapper sysPluginConfigMapper; + + @Override + public void savePluginConfig(String pluginId, String configJson) { + SysPluginConfig config = new SysPluginConfig(); + config.setConfigJson(configJson); + config.setPluginId(pluginId); + sysPluginConfigMapper.insert(config); + } + + @Override + public void deleteByPluginId(String pluginId) { + if (StringUtils.isNotNull(pluginId)) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysPluginConfig::getPluginId, pluginId); + sysPluginConfigMapper.delete(queryWrapper); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginLogoServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginLogoServiceImpl.java new file mode 100644 index 000000000..16f951b12 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginLogoServiceImpl.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginLogoServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginLogoServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.system.domain.SysPluginLogo; +import org.opengauss.admin.system.mapper.SysPluginLogoMapper; +import org.opengauss.admin.system.service.ISysPluginLogoService; +import org.springframework.stereotype.Service; + +/** + * @author xielibo + */ +@Service +public class SysPluginLogoServiceImpl extends ServiceImpl implements ISysPluginLogoService { + + + @Override + public void savePluginConfig(String pluginId, String logoPath) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysPluginLogo::getPluginId, pluginId); + SysPluginLogo one = this.getOne(queryWrapper); + if (one == null) { + one = new SysPluginLogo(); + one.setPluginId(pluginId); + } + one.setLogoPath(logoPath); + this.saveOrUpdate(one); + } + + @Override + public SysPluginLogo getByPluginId(String pluginId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysPluginLogo::getPluginId, pluginId).last("limit 1"); + SysPluginLogo one = this.getOne(queryWrapper); + return one; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginRepositoryServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginRepositoryServiceImpl.java new file mode 100644 index 000000000..c1f5cc625 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginRepositoryServiceImpl.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginRepositoryServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/ + * system/service/impl/SysPluginRepositoryServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.model.ops.WsSession; +import org.opengauss.admin.common.core.handler.ops.cache.TaskManager; +import org.opengauss.admin.common.core.handler.ops.cache.WsConnectorManager; +import org.opengauss.admin.common.enums.SysPluginDownloadStatus; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.system.domain.SysPluginRepository; +import org.opengauss.admin.system.mapper.SysPluginMapper; +import org.opengauss.admin.system.mapper.SysPluginRepositoryMapper; +import org.opengauss.admin.system.service.ISysPluginRepositoryService; +import org.opengauss.admin.system.utils.OnlinePluginloadUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * SysPluginRepositoryServiceImpl + * + * @author zhaochen + * @since 2025-03-20 + * @version 7.0.0-RC1 + */ +@Service +@Slf4j +public class SysPluginRepositoryServiceImpl + extends ServiceImpl + implements ISysPluginRepositoryService { + @Autowired + private SysPluginMapper sysPluginMapper; + @Autowired + private SysPluginRepositoryMapper sysPluginRepositoryMapper; + @Autowired + private WsConnectorManager wsConnectorManager; + @Autowired + private OnlinePluginloadUtil onlinePluginloadUtil; + @Autowired + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + @Value("${version}") + private String adminVersion; + + public String getAdminVersion() { + return adminVersion; + } + + @Override + public void updatePluginDownloadStatus(List pluginIds, Integer isDownload) { + sysPluginRepositoryMapper.updatePluginDownloadStatus(pluginIds, isDownload); + } + + @Override + public List getUnloadPluginIdsList() { + List loadedPluginIds = sysPluginMapper.getPluginIds(); + if (!loadedPluginIds.isEmpty()) { + updatePluginDownloadStatus(loadedPluginIds, SysPluginDownloadStatus.DOWNLOADED_STATUS.getCode()); + } + + List unloadedPluginIds = this.getUnloadPluginListByRepository(loadedPluginIds); + + if (!unloadedPluginIds.isEmpty()) { + updatePluginDownloadStatus(unloadedPluginIds, SysPluginDownloadStatus.UN_DOWNLOAD_STATUS.getCode()); + } + + return unloadedPluginIds; + } + + @Override + public List getUnloadPluginListByRepository(List pluginIds) { + return sysPluginRepositoryMapper.getUnloadPluginListByRepository(pluginIds); + } + + @Override + public String getCurrentVersion() { + return this.getAdminVersion(); + } + + @Override + public String getPluginUrlByIdAndVersion(String pluginId, String pluginVersion) { + return sysPluginRepositoryMapper.getPluginUrlByIdAndVersion(pluginId, pluginVersion); + } + + @Override + public void saveOnlinePackage(String downloadUrl, String fileName, String wsBusinessId) { + if (wsBusinessId.isEmpty()) { + log.error("wsBusinessId is null in saveOnlinePackage"); + } + // Attempt to get the session, if not found, throw OpsException + WsSession wsSession = wsConnectorManager.getSession(wsBusinessId) + .orElseThrow(() -> new OpsException( + String.format("No WebSocket session found for businessId: %s", wsBusinessId))); + // The download task is submitted asynchronously + Future future = threadPoolTaskExecutor.submit(() -> { + onlinePluginloadUtil.download(downloadUrl, fileName, wsSession); + }); + + // Register download progress + TaskManager.registry(wsBusinessId, future); + try { + future.get(); // Block until the task is completed + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginServiceImpl.java new file mode 100644 index 000000000..5c0deb44e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginServiceImpl.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysPluginServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysPluginServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.common.enums.SysPluginStatus; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.system.domain.SysPlugin; +import org.opengauss.admin.system.mapper.SysPluginMapper; +import org.opengauss.admin.system.service.ISysMenuService; +import org.opengauss.admin.system.service.ISysPluginConfigService; +import org.opengauss.admin.system.service.ISysPluginService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author xielibo + */ +@Service +public class SysPluginServiceImpl extends ServiceImpl implements ISysPluginService { + + @Autowired + private SysPluginMapper sysPluginMapper; + @Autowired + private ISysPluginConfigService sysPluginConfigService; + @Autowired + private ISysMenuService sysMenuService; + + /** + * Paging query plugin list + * + * @param sysPlugin + * @return SysPlugin + */ + @Override + public IPage selectList(IPage page, SysPlugin sysPlugin) { + return sysPluginMapper.selectSysPluginListPage(page, sysPlugin); + } + + public void updateByPluginId(String pluginId,Integer pluginStatus) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + queryWrapper.eq(pluginId != null, SysPlugin::getPluginId, pluginId); + SysPlugin update = new SysPlugin(); + update.setPluginStatus(pluginStatus); + sysPluginMapper.update(update, queryWrapper); + } + + /** + * Stop a plugin by plugin ID + * @param pluginId + */ + @Override + public void stopByPluginId(String pluginId) { + this.updateByPluginId(pluginId, SysPluginStatus.DISABLE.getCode()); + } + + /** + * Start the plugin according to the plugin ID + * @param pluginId + */ + @Override + public void startByPluginId(String pluginId) { + this.updateByPluginId(pluginId, SysPluginStatus.START.getCode()); + } + + /** + * Delete plugins by plugin ID + * @param pluginId + */ + @Override + public void uninstallPluginByPluginId(String pluginId) { + if (StringUtils.isNotNull(pluginId)) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + queryWrapper.eq(pluginId != null, SysPlugin::getPluginId, pluginId); + sysPluginMapper.delete(queryWrapper); + sysPluginConfigService.deleteByPluginId(pluginId); + sysMenuService.deleteByPluginId(pluginId); + } + } + + /** + * get one by pluginId + * @param pluginId + * @return + */ + @Override + public SysPlugin getByPluginId(String pluginId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + queryWrapper.eq(pluginId != null, SysPlugin::getPluginId, pluginId); + return sysPluginMapper.selectOne(queryWrapper); + } + + @Override + public List getPluginList() { + return sysPluginMapper.getPluginIds(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysRoleServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 000000000..74a3b5f7d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysRoleServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysRoleServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.common.constant.UserConstants; +import org.opengauss.admin.common.core.domain.entity.SysRole; +import org.opengauss.admin.common.exception.CustomException; +import org.opengauss.admin.system.domain.SysRoleMenu; +import org.opengauss.admin.system.domain.SysUserRole; +import org.opengauss.admin.system.mapper.SysRoleMapper; +import org.opengauss.admin.system.mapper.SysRoleMenuMapper; +import org.opengauss.admin.system.mapper.SysUserRoleMapper; +import org.opengauss.admin.system.service.ISysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Role Service + * + * @author xielibo + */ +@Service +public class SysRoleServiceImpl extends ServiceImpl implements ISysRoleService { + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + /** + * Query role data by pagination according to conditions + * + * @param role role + */ + @Override + public IPage selectRoleList(SysRole role, IPage page) { + return roleMapper.selectRoleList(page, role); + } + + /** + * Query roles based on user ID + * + * @param userId userId + */ + @Override + public List selectRolesByUserId(Integer userId) { + List userRoles = roleMapper.selectRolePermissionByUserId(userId); + List roles = selectRoleAll(); + for (SysRole role : roles) { + for (SysRole userRole : userRoles) { + if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) { + role.setFlag(true); + break; + } + } + } + return roles; + } + + /** + * Query role data based on conditions + * + * @param role role + */ + @Override + public List selectRoleList(SysRole role) { + return roleMapper.selectRoleList(role); + } + + /** + * Query permissions based on user ID + * + * @param userId userId + */ + @Override + public Set selectRolePermissionByUserId(Integer userId) { + List perms = roleMapper.selectRoles(new QueryWrapper().eq("ur.user_id", userId).eq("r.del_flag", UserConstants.UN_DELETE)); + Set permsSet = new HashSet<>(); + for (SysRole perm : perms) { + if (perm != null) { + permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(","))); + } + } + return permsSet; + } + + /** + * Query All Role + * + */ + @Override + public List selectRoleAll() { + return this.selectRoleList(new SysRole()); + } + + /** + * Get the role selection box list according to the user ID + * + * @param userId userId + */ + @Override + public List selectRoleListByUserId(Integer userId) { + return roleMapper.selectRoleListByUserId(userId); + } + + /** + * Query roles by role ID + * + */ + @Override + public SysRole selectRoleById(Integer roleId) { + + SysRole role = roleMapper.selectRole(new QueryWrapper().eq("r.role_id", roleId)); + return role; + } + + /** + * Check if the role name is unique + * + * @param role role + */ + @Override + public String checkRoleNameUnique(SysRole role) { + Integer roleId = role.getRoleId() == null ? 1 : role.getRoleId(); + SysRole info = roleMapper.selectRole(new QueryWrapper() + .eq("r.role_name", role.getRoleName()).eq("r.del_flag", 0)); + if (info != null && !info.getRoleId().equals(roleId)) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * Check if the role allows the operation + * + */ + @Override + public void checkRoleAllowed(SysRole role) { + if (role.getRoleId() != null && role.isAdmin()) { + throw new RuntimeException("operation not allowed"); + } + } + + /** + * Query the number of roles used by the role ID + * + */ + @Override + public int countUserRoleByRoleId(Integer roleId) { + return userRoleMapper.selectCount(new QueryWrapper().lambda().eq(SysUserRole::getRoleId, roleId)).intValue(); + } + + /** + * Save Role + * + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertRole(SysRole role) { + role.setRoleId(null); + roleMapper.insert(role); + return insertRoleMenu(role); + } + + /** + * Update Role + * + * @param role role + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateRole(SysRole role) { + roleMapper.updateById(role); + roleMenuMapper.delete(new QueryWrapper().lambda().eq(SysRoleMenu::getRoleId, role.getRoleId())); + return insertRoleMenu(role); + } + + /** + * Update Status + * + * @param role role + */ + @Override + public int updateRoleStatus(SysRole role) { + return roleMapper.updateById(role); + } + + /** + * Modify data permission information + * + * @param role role + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int authDataScope(SysRole role) { + return roleMapper.updateById(role); + } + + /** + * Added role menu relationship information + * + */ + public int insertRoleMenu(SysRole role) { + int rows = 1; + List list = new ArrayList(); + Set menuIds = Arrays.stream(role.getMenuIds()).collect(Collectors.toSet()); + menuIds.add(6); + menuIds.add(605); + for (Integer menuId : menuIds) { + SysRoleMenu rm = new SysRoleMenu(); + rm.setRoleId(role.getRoleId()); + rm.setMenuId(menuId); + list.add(rm); + } + if (list.size() > 0) { + rows = roleMenuMapper.batchRoleMenu(list); + } + return rows; + } + + + /** + * Batch delete role information + * + * @param roleIds roleIds + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteRoleByIds(Integer[] roleIds) { + for (Integer roleId : roleIds) { + checkRoleAllowed(new SysRole(roleId)); + SysRole role = selectRoleById(roleId); + if (countUserRoleByRoleId(roleId) > 0) { + throw new CustomException(String.format("%1$s allocated, cannot be deleted", role.getRoleName())); + } + } + roleMenuMapper.delete(new QueryWrapper().lambda().in(SysRoleMenu::getRoleId, roleIds)); + return roleMapper.deleteBatchIds(Arrays.asList(roleIds)); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysSettingExtServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysSettingExtServiceImpl.java new file mode 100644 index 000000000..712fa28d5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysSettingExtServiceImpl.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import org.opengauss.admin.common.constant.CommonConstants; +import org.opengauss.admin.common.core.domain.entity.SysSettingEntity; +import org.opengauss.admin.common.core.domain.entity.SysSettingExtEntity; +import org.opengauss.admin.system.mapper.SysSettingExtMapper; +import org.opengauss.admin.system.service.ISysSettingExtService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Objects; + +/** + * SysSettingExtServiceImpl + * + * @author: wangchao + * @Date: 2025/9/8 15:48 + * @since 7.0.0-RC2 + **/ +@Service +public class SysSettingExtServiceImpl extends ServiceImpl + implements ISysSettingExtService { + @Override + public List queryCurrentUserExtParam(Integer userId) { + LambdaQueryWrapper query = Wrappers.lambdaQuery(SysSettingExtEntity.class); + query.eq(SysSettingExtEntity::getUserId, userId); + return list(query); + } + + @Override + public void updateExtSettingDataKitServerHost(SysSettingEntity setting) { + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(SysSettingExtEntity.class); + updateWrapper.eq(SysSettingExtEntity::getId, setting.getId()) + .eq(SysSettingExtEntity::getUserId, setting.getUserId()) + .eq(SysSettingExtEntity::getParamName, CommonConstants.SETTING_SERVER_HOST_NAME) + .set(SysSettingExtEntity::getParamValue, setting.getServerHost()); + update(updateWrapper); + } + + @Override + public String queryAdminServerHost() { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(SysSettingExtEntity.class); + queryWrapper.eq(SysSettingExtEntity::getParamName, CommonConstants.SETTING_SERVER_HOST_NAME); + queryWrapper.eq(SysSettingExtEntity::getUserId, CommonConstants.ADMIN_USER_ID); + SysSettingExtEntity sysExtParam = getOne(queryWrapper); + return Objects.isNull(sysExtParam) ? "" : sysExtParam.getParamValue(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysSettingServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysSettingServiceImpl.java new file mode 100644 index 000000000..39c96444b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysSettingServiceImpl.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysSettingServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysSettingServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import jakarta.annotation.Resource; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import org.opengauss.admin.common.constant.CommonConstants; +import org.opengauss.admin.common.core.domain.entity.SysSettingEntity; +import org.opengauss.admin.common.core.domain.entity.SysSettingExtEntity; +import org.opengauss.admin.common.core.domain.entity.SysUser; +import org.opengauss.admin.common.utils.http.HttpUtils; +import org.opengauss.admin.system.mapper.SysSettingMapper; +import org.opengauss.admin.system.service.ISysSettingExtService; +import org.opengauss.admin.system.service.ISysSettingService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.File; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.util.List; +import java.util.Optional; + +/** + * System Setting Service + * + * @author: wangchao + * @Date: 2025/9/11 21:21 + * @since 7.0.0-RC2 + */ +@Service +public class SysSettingServiceImpl extends ServiceImpl + implements ISysSettingService { + @Value("${server.proxy.hostname}") + private String proxyHostname; + @Value("${server.proxy.port}") + private Integer proxyPort; + @Resource + private ISysSettingExtService sysSettingExtService; + + @Override + public void initHttpProxy() { + HttpUtils.setProxy(getSysNetProxy()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateSetting(SysSettingEntity setting) { + // create folder if not exist + String uploadPath = setting.getUploadPath(); + File file = new File(uploadPath); + // if create failed + if (!(!file.exists() && file.mkdirs())) { + log.error(String.format("System setting update failed: folder %s create failed", uploadPath)); + } + sysSettingExtService.updateExtSettingDataKitServerHost(setting); + return saveOrUpdate(setting); + } + + @Override + public SysSettingEntity getSetting(Integer userId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysSettingEntity::getUserId, SysUser.getAdminUserId()); + SysSettingEntity adminResult = getOne(queryWrapper); + List extList = sysSettingExtService.queryCurrentUserExtParam(userId); + Optional sysSettingExt = extList.stream() + .filter(ext -> ext.getParamName().equals(CommonConstants.SETTING_SERVER_HOST_NAME)) + .findFirst(); + refreshServerHostSetting(sysSettingExt, adminResult); + if (SysUser.isAdmin(userId)) { + return adminResult; + } + LambdaQueryWrapper userQueryWrapper = new LambdaQueryWrapper<>(); + userQueryWrapper.eq(SysSettingEntity::getUserId, userId); + SysSettingEntity userResult = getOne(userQueryWrapper); + refreshServerHostSetting(sysSettingExt, userResult); + // if current user is not admin and there is no setting data + // return admin setting to user for save + if (ObjectUtil.isNull(userResult)) { + adminResult.setId(null); + adminResult.setUserId(userId); + return adminResult; + } + return userResult; + } + + private void refreshServerHostSetting(Optional sysSettingExt, SysSettingEntity adminResult) { + sysSettingExt.ifPresent(ext -> { + if (ext.getParamValue() == null) { + adminResult.setServerHost(""); + } else { + adminResult.setServerHost(ext.getParamValue()); + } + }); + } + + @Override + public Proxy getSysNetProxy() { + Proxy proxy = null; + try { + if (StrUtil.isEmpty(proxyHostname) || StrUtil.equalsIgnoreCase(proxyHostname, "localhost") + || StrUtil.equalsIgnoreCase(proxyHostname, "127.0.0.1")) { + return proxy; + } + proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHostname, proxyPort)); + } catch (IllegalArgumentException | SecurityException ex) { + log.error(String.format("Failed to create proxy: %s %s ", proxyHostname, proxyPort), ex); + } + return proxy; + } + + @Override + public boolean hasUploadPath(String path, Integer userId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysSettingEntity::getUploadPath, path); + List result = list(queryWrapper); + if (CollUtil.isNotEmpty(result)) { + for (SysSettingEntity entity: result) { + if (!entity.getUserId().equals(userId)) { + return true; + } + } + } + return false; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysTaskServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysTaskServiceImpl.java new file mode 100644 index 000000000..9298c964e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysTaskServiceImpl.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysTaskServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysTaskServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gitee.starblues.spring.extract.ExtractFactory; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.dto.SysTaskDto; +import org.opengauss.admin.common.enums.SysTaskStatus; +import org.opengauss.admin.system.domain.SysTask; +import org.opengauss.admin.system.mapper.SysTaskMapper; +import org.opengauss.admin.system.plugin.beans.MigrationTaskDetail; +import org.opengauss.admin.system.plugin.beans.TaskExecProgressDto; +import org.opengauss.admin.system.plugin.extract.TaskExtract; +import org.opengauss.admin.system.service.ISysTaskService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * Task Service + * + * @author xielibo + */ +@Service +@Slf4j +public class SysTaskServiceImpl extends ServiceImpl implements ISysTaskService { + + @Autowired + private SysTaskMapper sysTaskMapper; + + @Autowired + private ExtractFactory extractFactory; + + /** + * Query the task list by page + * + * @param task SysTask + * @return SysTask + */ + @Override + public IPage selectList(IPage page, SysTaskDto task) { + return sysTaskMapper.selectTaskPage(page, task); + } + + /** + * Query all task + * + * @param task SysTask + * @return SysTask + */ + @Override + public List selectListAll(SysTaskDto task) { + return sysTaskMapper.selectTaskList(task); + } + + public Integer countByProcessing() { + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(SysTask::getExecStatus, SysTaskStatus.PROCESSING.getCode()); + return Math.toIntExact(this.count(query)); + } + + public List listByProcessing() { + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(SysTask::getExecStatus, SysTaskStatus.PROCESSING.getCode()); + return list(query); + } + + + @Override + public Map getDetailById(Integer taskId) { + Map result = new HashMap<>(); + SysTask task = this.getById(taskId); + List extractByInterClass = extractFactory.getExtractByInterClass(task.getPluginId(), TaskExtract.class); + if (extractByInterClass.size() > 0) { + log.info("Get the TaskExtract implementation class. {}", extractByInterClass.get(0).getClass().getPackage()); + List pluginTask = extractByInterClass.get(0).getPluginTask(taskId); + result.put("subTask", pluginTask); + } else { + log.error("No implementation found"); + } + result.put("mainTask", task); + return result; + } + + + @Override + public void deleteTask(Integer[] ids) { + Arrays.asList(ids).stream().forEach(i -> { + SysTask task = this.getById(i); + List extractByInterClass = extractFactory.getExtractByInterClass(task.getPluginId(), TaskExtract.class); + if (extractByInterClass.size() > 0) { + log.info("Get the TaskExtract implementation class. {}", extractByInterClass.get(0).getClass().getPackage()); + extractByInterClass.get(0).deleteTask(i); + this.removeById(i); + } else { + log.error("No implementation found"); + } + }); + } + + + @Override + public void updateStatus(Integer id, SysTaskStatus taskStatus) { + SysTask task = new SysTask(); + task.setId(id); + task.setExecStatus(taskStatus.getCode()); + this.updateById(task); + } + + @Override + public void startTask(Integer id) { + SysTask task = this.getById(id); + List extractByInterClass = extractFactory.getExtractByInterClass(task.getPluginId(), TaskExtract.class); + if (extractByInterClass.size() > 0) { + log.info("Get the TaskExtract implementation class. {}", extractByInterClass.get(0).getClass().getPackage()); + extractByInterClass.get(0).startTask(id); + } else { + log.error("No implementation found"); + } + SysTask updateTask = new SysTask(); + updateTask.setId(id); + updateTask.setExecTime(new Date()); + updateTask.setExecStatus(SysTaskStatus.PROCESSING.getCode()); + this.updateById(updateTask); + } + + @Override + public void stopTask(Integer id) { + SysTask task = this.getById(id); + List extractByInterClass = extractFactory.getExtractByInterClass(task.getPluginId(), TaskExtract.class); + if (extractByInterClass.size() > 0) { + log.info("Get the TaskExtract implementation class. {}", extractByInterClass.get(0).getClass().getPackage()); + extractByInterClass.get(0).stopTask(id); + } else { + log.error("No implementation found"); + } + this.updateStatus(id, SysTaskStatus.STOP); + } + + + @Override + public void refreshTaskStatus() { + Integer staringCount = this.countByProcessing(); + if (staringCount > 0) { + List migrationTasks = this.listByProcessing(); + migrationTasks.stream().forEach((x -> { + List extractByInterClass = extractFactory.getExtractByInterClass(x.getPluginId(), TaskExtract.class); + if (extractByInterClass.size() > 0) { + log.info("Get the TaskExtract implementation class. {}", extractByInterClass.get(0).getClass().getPackage()); + TaskExecProgressDto taskStatus = extractByInterClass.get(0).getTaskStatus(x.getId()); + SysTask task = new SysTask(); + task.setId(x.getId()); + task.setExecStatus(taskStatus.getTaskStatus().getCode()); + task.setExecProgress(taskStatus.getExecProgress()); + if (taskStatus.getTaskStatus().getCode().equals(SysTaskStatus.FINISH.getCode())) { + task.setFinishTime(new Date()); + } + this.updateById(task); + } else { + log.error("No implementation found"); + } + })); + } + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysUserServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 000000000..08c155002 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysUserServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysUserServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.common.constant.UserConstants; +import org.opengauss.admin.common.core.domain.entity.SysRole; +import org.opengauss.admin.common.core.domain.entity.SysUser; +import org.opengauss.admin.common.exception.CustomException; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.system.domain.SysUserRole; +import org.opengauss.admin.system.mapper.SysRoleMapper; +import org.opengauss.admin.system.mapper.SysUserMapper; +import org.opengauss.admin.system.mapper.SysUserRoleMapper; +import org.opengauss.admin.system.service.ISysUserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * User Service + * + * @author xielibo + */ +@Slf4j +@Service +public class SysUserServiceImpl extends ServiceImpl implements ISysUserService { + + @Autowired + private SysUserMapper userMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + /** + * Query user page list + * + * @param user user param + * @return page list + */ + @Override + public IPage selectUserList(SysUser user, IPage page) { + IPage result = userMapper.selectUserList(page, user); + return result; + } + + /** + * Query by username + * + * @param userName username + * @return SysUser + */ + @Override + public SysUser selectUserByUserName(String userName) { + return userMapper.selectUserVo(new QueryWrapper().eq("u.user_name", userName).eq("u.del_flag",0)); + } + + /** + * get By Id + * + * @param userId user ID + * @return SysUser + */ + @Override + public SysUser selectUserById(Integer userId) { + return userMapper.selectUserVo(new QueryWrapper().eq("u.user_id", userId)); + } + + /** + * Query the user's role + * + * @param userName username + */ + @Override + public String selectUserRoleGroup(String userName) { + QueryWrapper eq = new QueryWrapper().eq("u.user_name", userName).eq("r.del_flag", UserConstants.UN_DELETE); + List list = roleMapper.selectRoles(eq); + StringBuffer idsStr = new StringBuffer(); + for (SysRole role : list) { + idsStr.append(role.getRoleName()).append(","); + } + if (StringUtils.isNotEmpty(idsStr.toString())) { + return idsStr.substring(0, idsStr.length() - 1); + } + return idsStr.toString(); + } + /** + * Check if username is unique + * + * @param userName username + */ + @Override + public String checkUserNameUnique(String userName) { + int count = userMapper.selectCount(new QueryWrapper().lambda().eq(SysUser::getUserName, userName)).intValue(); + if (count > 0) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * Check if phone is unique + * + * @param user user + * @return + */ + @Override + public String checkPhoneUnique(SysUser user) { + Integer userId = user.getUserId(); + LambdaQueryWrapper queryWrapper = new QueryWrapper().lambda().eq(SysUser::getPhonenumber, user.getPhonenumber()); + if (StringUtils.isNull(userId)) { + userId = 1; + } else { + queryWrapper.ne(SysUser::getUserId, user.getUserId()); + } + + SysUser info = userMapper.selectOne(queryWrapper); + + if (StringUtils.isNotNull(info) && !info.getUserId().equals(userId)) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * Check if email is unique + * + * @param user user + * @return + */ + @Override + public String checkEmailUnique(SysUser user) { + Integer userId = user.getUserId(); + LambdaQueryWrapper queryWrapper = new QueryWrapper().lambda().eq(SysUser::getEmail, user.getEmail()); + + if (StringUtils.isNull(userId)) { + userId = 1; + } else { + queryWrapper.ne(SysUser::getUserId, user.getUserId()); + } + + SysUser info = userMapper.selectOne(queryWrapper); + + if (StringUtils.isNotNull(info) && !info.getUserId().equals(userId)) { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * Check if the user allows the operation + * + * @param user user + */ + @Override + public void checkUserAllowed(SysUser user) { + if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) { + throw new CustomException("Operation super administrator user not allowed"); + } + } + + /** + * save user + * + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertUser(SysUser user) { + user.setUserId(null); + int rows = userMapper.insert(user); + insertUserRole(user); + return rows; + } + + /** + * update user + * + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateUser(SysUser user) { + Integer userId = user.getUserId(); + userRoleMapper.delete(new QueryWrapper().lambda().eq(SysUserRole::getUserId, userId)); + insertUserRole(user); + return userMapper.updateById(user); + } + + /** + * update status + * + */ + @Override + public int updateUserStatus(SysUser user) { + return userMapper.updateById(user); + } + + /** + * updat information + * + */ + @Override + public int updateUserProfile(SysUser user) { + return userMapper.updateById(user); + } + + /** + * update user avatar + * + */ + @Override + public boolean updateUserAvatar(String userName, String avatar) { + boolean result = this.update(new SysUser(), new UpdateWrapper().lambda().set(SysUser::getAvatar, avatar).eq(SysUser::getUserName, userName)); + return result; + } + + /** + * reset password + * + */ + @Override + public int resetPwd(SysUser user) { + return this.updateById(user) ? 1 : 0; + } + + /** + * reset password + * + */ + @Override + public int resetUserPwd(String userName, String password) { + boolean result = this.update(new SysUser(), new UpdateWrapper().lambda().set(SysUser::getPassword, password).set(SysUser::getUpdatePwd, 1).eq(SysUser::getUserName, userName)); + return result ? 1 : 0; + } + + /** + * Add user and role association + * + */ + public void insertUserRole(SysUser user) { + Integer roleId = user.getRoleIds(); + if (roleId != null) { + SysUserRole ur = new SysUserRole(); + ur.setUserId(user.getUserId()); + ur.setRoleId(roleId); + userRoleMapper.insert(ur); + } + } + + /** + * delete by ID + * + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteUserById(Integer userId) { + userRoleMapper.delete(new QueryWrapper().lambda().eq(SysUserRole::getUserId, userId)); + return userMapper.deleteById(userId); + } + + /** + * Batch delete user + * + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteUserByIds(Integer[] userIds) { + for (Integer userId : userIds) { + checkUserAllowed(new SysUser(userId)); + } + userRoleMapper.delete(new QueryWrapper().lambda().in(SysUserRole::getUserId, userIds)); + return userMapper.deleteBatchIds(Arrays.asList(userIds)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public List listUserByRemark(String remark) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysUser::getRemark, remark); + return userMapper.selectList(queryWrapper); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysWhiteListServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysWhiteListServiceImpl.java new file mode 100644 index 000000000..15387b4bf --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysWhiteListServiceImpl.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SysWhiteListServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/SysWhiteListServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.system.domain.SysWhiteList; +import org.opengauss.admin.system.mapper.SysWhiteListMapper; +import org.opengauss.admin.system.service.ISysWhiteListService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * White List Service + * + * @author xielibo + */ +@Service +public class SysWhiteListServiceImpl extends ServiceImpl implements ISysWhiteListService { + + @Autowired + private SysWhiteListMapper sysWhiteListMapper; + + /** + * Query the whitelist list by page + * + * @param whiteList SysWhiteList + * @return SysWhiteList + */ + @Override + public IPage selectList(IPage page, SysWhiteList whiteList) { + return sysWhiteListMapper.selectWhiteListPage(page, whiteList); + } + + /** + * Query all whitelists + * + * @param whiteList SysWhiteList + * @return SysWhiteList + */ + @Override + public List selectListAll(SysWhiteList whiteList) { + return sysWhiteListMapper.selectWhiteListList(whiteList); + } + + /** + * Check whether the ip exists in the whitelist + */ + @Override + public boolean checkSingleIpExistsInWhiteList(String ip) { + Integer count = sysWhiteListMapper.countByIp(ip); + return count > 0; + } + + @Override + public List checkIpsExistsInWhiteList(SysWhiteList whiteList) { + List sysWhiteLists = (whiteList.getId() != null) + ? list(new LambdaQueryWrapper().ne(SysWhiteList::getId, whiteList.getId())) + : list(); + + Set whiteListIps = new HashSet<>(); + for (SysWhiteList sysWhiteList : sysWhiteLists) { + whiteListIps.addAll(Arrays.asList(sysWhiteList.getIpList().split(","))); + } + + return Arrays.stream(whiteList.getIpList().split(",")) + .filter(whiteListIps::contains) + .collect(Collectors.toList()); + } + + /** + * Check whether the title exists + */ + public boolean checkTitleExists(SysWhiteList whiteList) { + Integer count = sysWhiteListMapper.countByTitle(whiteList); + return count > 0; + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/WebSocketServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/WebSocketServiceImpl.java new file mode 100644 index 000000000..5e2cafe1e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/WebSocketServiceImpl.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WebSocketServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/impl/WebSocketServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.impl; +import com.gitee.starblues.spring.extract.ExtractFactory; +import org.opengauss.admin.common.core.ws.WsConnectorManager; +import org.opengauss.admin.system.plugin.extract.SocketExtract; +import org.opengauss.admin.system.service.IWebSocketService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import jakarta.websocket.Session; +import java.io.IOException; +import java.util.List; + +/** + * @className: WsServiceImpl + * @description: process websocket messages service + * @author: xielibo + * @date: 2022-11-15 12:49 + **/ +@Slf4j +@Component +public class WebSocketServiceImpl implements IWebSocketService { + @Autowired + private ExtractFactory extractFactory; + + @Override + public void onOpen(String pluginId, String sessionId, Session session) { + WsConnectorManager.register(pluginId,sessionId,session); + log.info("callback plugin. plugin is {}", pluginId); + List extractByInterClass = extractFactory.getExtractByInterClass(pluginId, SocketExtract.class); + if (extractByInterClass.size() > 0) { + log.info("Get the SocketExtract implementation class. {}" , extractByInterClass.get(0).getClass().getPackage()); + extractByInterClass.get(0).onOpen(pluginId, sessionId, session); + } else { + log.error("No implementation found"); + } + } + + @Override + public void onClose(String pluginId, String sessionId) { + WsConnectorManager.remove(pluginId, sessionId); + log.info("callback plugin. plugin is {}", pluginId); + List extractByInterClass = extractFactory.getExtractByInterClass(pluginId, SocketExtract.class); + if (extractByInterClass.size() > 0) { + log.info("Get the SocketExtract implementation class. {}" , extractByInterClass.get(0).getClass().getPackage()); + extractByInterClass.get(0).onClose(pluginId,sessionId); + } else { + log.error("No implementation found"); + } + } + + /** + * After receiving the message, forward it to the plugin for processing + * @param pluginId + * @param sessionId + * @param message + */ + @Override + public void onMessage(String pluginId, String sessionId, String message) { + log.info("forward to plugin for processing. plugin is {}", pluginId); + List extractByInterClass = extractFactory.getExtractByInterClass(pluginId, SocketExtract.class); + if (extractByInterClass.size() > 0) { + log.info("Get the SocketExtract implementation class. {}" , extractByInterClass.get(0).getClass().getPackage()); + extractByInterClass.get(0).processMessage(sessionId,message); + } else { + log.error("No implementation found"); + } + } + + @Override + public void sendMessage(String pluginId, String sessionId, String message){ + sendMessage(WsConnectorManager.getSession(pluginId, sessionId), message); + } + + @Override + public void sendMessage(Session session, String message) { + try { + session.getBasicRemote().sendText(message); + } catch (IOException e) { + log.error("Failed to send ws message", e); + } + } + +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/ClusterOpsProvider.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/ClusterOpsProvider.java new file mode 100644 index 000000000..9d7b52e54 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/ClusterOpsProvider.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterOpsProvider.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/ClusterOpsProvider.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops; + +import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum; + +/** + * Cluster Installation Service Provider Specification + * + * @author lhf + * @date 2022/8/12 09:09 + **/ +public interface ClusterOpsProvider { + /** + * Installed openGauss version + * + * @return openGauss version + */ + OpenGaussVersionEnum version(); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IEncryptionService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IEncryptionService.java new file mode 100644 index 000000000..25a37ef79 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IEncryptionService.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IEncryptionService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IEncryptionService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.ops.OpsEncryptionEntity; + +/** + * @author lhf + * @date 2022/12/2 18:37 + **/ +public interface IEncryptionService extends IService { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IHostService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IHostService.java new file mode 100644 index 000000000..ffecad03b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IHostService.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IHostService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IHostService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.model.ops.HostBody; +import org.opengauss.admin.common.core.domain.model.ops.host.OpsHostVO; +import org.opengauss.admin.common.core.domain.model.ops.host.SSHBody; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.InputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author lhf + * @date 2022/8/7 22:27 + **/ +public interface IHostService extends IService { + /** + * Add host + * + * @param hostBody host information + */ + boolean add(HostBody hostBody); + + /** + * test connectivity + * + * @param hostBody host information + * @return whether succeed + */ + boolean ping(HostBody hostBody); + + /** + * delete host + * + * @param hostId HostId + * @return Whether the deletion is successful + */ + boolean del(String hostId); + + /** + * test connectivity + * + * @param hostId host ID + * @param rootPassword + * @return whether succeed + */ + boolean ping(String hostId, String rootPassword); + + /** + * Edit host information + * + * @param hostId host ID + * @param hostBody host information + * @return whether succeed + */ + boolean edit(String hostId, HostBody hostBody); + + /** + * Parse the Excel file. + * + * @param uuid The unique identifier of the file. + * @param fileStreamMap Excel file + * @param currentLocale current language + */ + void invokeFile(String uuid, HashMap fileStreamMap, String currentLocale); + + /** + * download template + * + * @param response HTTP response object + * @param currentLocale current language + */ + void downloadTemplate(HttpServletResponse response, String currentLocale); + + void downloadErrorExcel(HttpServletResponse response, String uuid); + + IPage pageHost(Page page, String name, Set tagIds, String os); + + void ssh(SSHBody sshBody); + + void ssh(String hostId, SSHBody sshBody); + + Map mapOsByIps(Set ipSet); + + List listAll(String azId); + + /** + * page hosts monitor + * + * @param hostIds host ids + * @param businessId business id + * @return host status + */ + Map pageMonitor(List hostIds, String businessId); + + void updateHostOsVersion(OpsHostEntity opsHostEntity); + + OpsHostEntity getMappedHostEntityById(String hostId); + + /** + * get host info + * + * @param hostIp host public Ip + * @return host + */ + OpsHostEntity getByPublicIp(String hostIp); + + /** + * update host os name + * + * @param host host ip + * @param osName os name + */ + void updateHostOsName(String host, String osName); + + /** + * update host os version + * + * @param host host ip + * @param osVersion os version + */ + void updateHostOsVersion(String host, String osVersion); + + /** + * update host cpu info by host ip + * + * @param host host ip + * @param cpuArch cpu arch + * @param cpuCoreNum cpu core num + * @param cpuFreq cpu freq + */ + void updateHostCpu(String host, String cpuArch, String cpuCoreNum, String cpuFreq); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IHostUserService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IHostUserService.java new file mode 100644 index 000000000..26c3343b6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IHostUserService.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IHostUserService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IHostUserService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.core.domain.model.ops.HostUserBody; + +import java.util.List; + +/** + * @author lhf + * @date 2022/8/8 15:39 + **/ +public interface IHostUserService extends IService { + List listHostUserByHostId(String hostId); + + boolean removeByHostId(String hostId); + + List listHostUserByHostIdList(List hostIdList); + + /** + * add host user + * + * @param host host + * @param hostUserBody host user + * @return boolean + */ + boolean add(OpsHostEntity host, HostUserBody hostUserBody); + + /** + * edit host user + * + * @param host host + * @param hostUserId host user id + * @param hostUserBody host user info + * @return boolean + */ + boolean edit(OpsHostEntity host, String hostUserId, HostUserBody hostUserBody); + + boolean del(String hostUserId); + + OpsHostUserEntity getOmmUserByHostId(String hostId); + + OpsHostUserEntity getRootUserByHostId(String hostId); + + /** + * get any user by hostId + * + * @param hostId hostId + * @return user + */ + OpsHostUserEntity getAnyUserByHostId(String hostId); + + void cleanPassword(String hostUserId); + + OpsHostUserEntity getHostUserByUsername(String hostId, String sshUsername); + + /** + * check if the user has root permission + * + * @param host host + * @param userId user ID + * @return has root permission or not + */ + boolean hasRootPermission(OpsHostEntity host, String userId); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsAzService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsAzService.java new file mode 100644 index 000000000..a345b5835 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsAzService.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsAzService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsAzService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.ops.OpsAzEntity; + +/** + * O&M Availability Zone Service Class + * + * @author lhf + * @date 2022/8/6 20:49 + **/ +public interface IOpsAzService extends IService { + /** + * Add Availability Zone + * + * @param az + * @return + */ + boolean add(OpsAzEntity az); + + boolean hasName(String name); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsCheckService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsCheckService.java new file mode 100644 index 000000000..7966f1c21 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsCheckService.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsCheckService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsCheckService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.ops.OpsCheckEntity; + +import java.util.List; +import java.util.Map; + +/** + * @author lhf + * @date 2022/11/13 17:17 + **/ +public interface IOpsCheckService extends IService { + OpsCheckEntity getLastResByClusterId(String clusterId); + + Map mapLastResByClusterIds(List clusterIds); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsClusterNodeService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsClusterNodeService.java new file mode 100644 index 000000000..b3f578fd8 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsClusterNodeService.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsClusterNodeService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsClusterNodeService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.common.core.dto.ops.ClusterNodeDto; + +import java.util.List; +import java.util.Map; + +/** + * @author lhf + * @date 2022/8/18 09:15 + **/ +public interface IOpsClusterNodeService extends IService { + List listClusterNodeByClusterId(String clusterId); + + long countByHostId(String hostId); + + long countByHostUserId(String hostUserId); + + Map> listClusterNodeByClusterIds(List clusterIds); + + /** + * get ClusterNodeDto info by clusterNodeId + * + * @param clusterNodeId clusterNodeId + * @return ClusterNodeDto + */ + ClusterNodeDto getClusterNodeDtoByNodeId(String clusterNodeId); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsClusterService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsClusterService.java new file mode 100644 index 000000000..608fbbf5b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsClusterService.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsClusterService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsClusterService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.common.core.domain.model.ops.ClusterSummaryVO; +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO; +import org.opengauss.admin.common.core.domain.model.ops.check.CheckSummaryVO; + +import java.sql.Connection; +import java.util.List; +import java.util.Map; + +/** + * + * @author lhf + * @date 2022/8/6 17:37 + **/ +public interface IOpsClusterService extends IService { + + List listCluster(); + + ClusterSummaryVO summary(); + + void monitor(String clusterId, String hostId, String businessId); + + Map threadPoolMonitor(); + + long countByHostId(String hostId); + + String connectNum(Connection connection); + + String session(Connection connection); + + String lock(Connection connection); + + CheckSummaryVO check(String clusterId, String rootPassword); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsHostTagRelService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsHostTagRelService.java new file mode 100644 index 000000000..2941c4d78 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsHostTagRelService.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsHostTagRelService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsHostTagRelService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostTagEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostTagRel; +import org.opengauss.admin.common.core.domain.model.ops.host.tag.HostTagInputDto; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author lhf + * @date 2023/3/14 23:38 + **/ +public interface IOpsHostTagRelService extends IService { + void addHostTagRel(List hostIds, List tags); + + Map> mapByHostIds(List hostIds); + + void cleanHostTag(String hostId); + + void delByTagId(String tagId); + + void delTagRelation(HostTagInputDto hostTagInputDto); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsHostTagService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsHostTagService.java new file mode 100644 index 000000000..81d9d81cb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsHostTagService.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsHostTagService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsHostTagService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostTagEntity; +import org.opengauss.admin.common.core.domain.model.ops.host.tag.HostTagInputDto; +import org.opengauss.admin.common.core.domain.model.ops.host.tag.HostTagPageVO; + +/** + * @author lhf + * @date 2023/3/14 23:36 + **/ +public interface IOpsHostTagService extends IService { + void addTag(HostTagInputDto hostTagInputDto); + + IPage page(Page page, String name); + + void add(String name); + + void update(String tagId, String name); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsJdbcDbClusterNodeService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsJdbcDbClusterNodeService.java new file mode 100644 index 000000000..6e1e4b00f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsJdbcDbClusterNodeService.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsJdbcDbClusterNodeService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsJdbcDbClusterNodeService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.ops.OpsJdbcDbClusterNodeEntity; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterNodeInputDto; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author lhf + * @date 2023/1/13 11:08 + **/ +public interface IOpsJdbcDbClusterNodeService extends IService { + void del(String clusterNodeId); + + /** + * find jdbc cluster by ip, port and username + * + * @param ip ip + * @param port port + * @param username username + * @return jdbc cluster info + */ + OpsJdbcDbClusterNodeEntity getClusterNodeByIpAndPort(String ip, String port, String username); + + Set fuzzyQueryClusterIdsByIp(String ip); + + Map> mapClusterNodesByClusterId(Set clusterIds); + + void delByClusterId(String clusterId); + + void update(String clusterNodeId, JdbcDbClusterNodeInputDto clusterNodeInput); + + void add(String clusterId, JdbcDbClusterNodeInputDto clusterNodeInput); + + boolean ping(JdbcDbClusterNodeInputDto clusterNodeInput); + + Map monitor(String clusterNodeId, String businessId); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsJdbcDbClusterService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsJdbcDbClusterService.java new file mode 100644 index 000000000..5325bf569 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsJdbcDbClusterService.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsJdbcDbClusterService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IOpsJdbcDbClusterService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.ops.OpsJdbcDbClusterEntity; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterImportAnalysisVO; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterInputDto; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterVO; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * @author lhf + * @date 2023/1/13 11:07 + **/ +public interface IOpsJdbcDbClusterService extends IService { + void add(JdbcDbClusterInputDto clusterInput); + + Page page(String name, String ip, String type, Page page); + + void del(String clusterId); + + void update(String clusterId, JdbcDbClusterInputDto clusterInput); + + JdbcDbClusterImportAnalysisVO importAnalysis(MultipartFile file); + + void importCluster(MultipartFile file); + + List listAll(); + + List listByType(String type); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IWsService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IWsService.java new file mode 100644 index 000000000..8f963d292 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IWsService.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IWsService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/IWsService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops; + +import org.opengauss.admin.common.core.domain.model.ops.WsSession; + +import jakarta.websocket.Session; +import java.util.Optional; + +/** + * @author lhf + * @date 2022/8/6 18:14 + **/ +public interface IWsService { + /** + * Called when a session is established + * + * @param type session type + * @param businessId session id + * @param session session object + */ + void onOpen(String businessId, Session session); + + /** + * When the session is closed + * + * @param type session type + * @param businessId session id + */ + void onClose(String businessId); + + /** + * When there is a message in the conversation + * + * @param type session type + * @param businessId session id + * @param message message in conversation + */ + void onMessage(String businessId, String message); + + /** + * Send message to client session + * @param session session object + * @param message message to send + */ + void sendMessage(Session session, byte[] message); + + /** + * Get the websocket object of the response + * + * @param wsConnectType session type + * @param businessId session id + * @return + */ + WsSession getRetWsSession(String businessId); + + /** + * Obtain a refreshed websocket session object. When using the WsSession object, it is recommended to obtain a refreshed one to avoid cache expiration. + * @param wsSession websocket session object + * @return + */ + Optional obtainFreshSession(WsSession wsSession); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/EncryptionServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/EncryptionServiceImpl.java new file mode 100644 index 000000000..821d7ee2e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/EncryptionServiceImpl.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * EncryptionServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/EncryptionServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops.impl; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.common.core.domain.entity.ops.OpsEncryptionEntity; +import org.opengauss.admin.system.mapper.ops.OpsEncryptionMapper; +import org.opengauss.admin.system.service.ops.IEncryptionService; +import org.springframework.stereotype.Service; + +/** + * @author lhf + * @date 2022/12/2 18:37 + **/ +@Service +public class EncryptionServiceImpl extends ServiceImpl implements IEncryptionService { +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/EncryptionUtils.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/EncryptionUtils.java new file mode 100644 index 000000000..f97930b69 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/EncryptionUtils.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * EncryptionUtils.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/EncryptionUtils + * .java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops.impl; + +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.CryptoException; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.codec.binary.Base64; +import org.opengauss.admin.common.core.domain.entity.ops.OpsEncryptionEntity; +import org.opengauss.admin.common.utils.AesGcmUtils; +import org.opengauss.admin.system.mapper.ops.OpsEncryptionMapper; +import org.opengauss.admin.system.service.ops.IEncryptionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.security.*; +import java.util.Objects; + +/** + * @author lhf + * @date 2022/11/28 14:20 + **/ +@Slf4j +@Component +public class EncryptionUtils { + private static final String SECRET = "secret"; + private static String publicKey; + private static String privateKey; + + @Autowired + private OpsEncryptionMapper encryptionMapper; + + @Autowired + private IEncryptionService encryptionService; + + public String encrypt(String plainText) { + RSA rsa = new RSA(null, publicKey); + byte[] encrypt = rsa.encrypt(StrUtil.bytes(plainText, CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey); + return StrUtil.str(Base64.encodeBase64(encrypt), CharsetUtil.CHARSET_UTF_8); + } + + public String decrypt(String cipherText) { + try { + RSA rsa = new RSA(privateKey, null); + byte[] decrypt = rsa.decrypt(Base64.decodeBase64(cipherText), KeyType.PrivateKey); + return StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8); + } catch (CryptoException e) { + return cipherText; + } + } + + public String getKey() { + if (StrUtil.isEmpty(publicKey)) { + refreshKeyPair(); + } + return publicKey; + } + + public static synchronized void generateKeyPair() { + try { + KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA"); + gen.initialize(4096); + KeyPair keyPair = gen.generateKeyPair(); + PublicKey pubKey = keyPair.getPublic(); + PrivateKey priKey = keyPair.getPrivate(); + publicKey = Base64.encodeBase64String(pubKey.getEncoded()); + privateKey = Base64.encodeBase64String(priKey.getEncoded()); + } catch (Exception e) { + log.error("gen key pair fail", e); + } + } + + public void refreshKeyPair() { + this.refreshKeyPair(true); + } + + /** + * 刷新公钥和私钥 + * + * @param isEnforce 是否强制 + */ + public void refreshKeyPair(boolean isEnforce) { + OpsEncryptionEntity opsEncryptionEntity = encryptionMapper.selectOne(null); + if (Objects.isNull(opsEncryptionEntity)) { + opsEncryptionEntity = new OpsEncryptionEntity(); + log.info("encryption key not found, generate it."); + generateKeyPair(); + saveKeyPair(opsEncryptionEntity); + } else if (StrUtil.isEmpty(publicKey) || isEnforce) { + log.info("refresh encryption key."); + publicKey = AesGcmUtils.decrypt(opsEncryptionEntity.getPublicKey()); + privateKey = AesGcmUtils.decrypt(opsEncryptionEntity.getPrivateKey()); + } else { + log.info("dont refresh encryption key."); + } + } + + private void saveKeyPair(OpsEncryptionEntity opsEncryptionEntity) { + opsEncryptionEntity.setEncryptionId("1"); + opsEncryptionEntity.setPublicKey(AesGcmUtils.encrypt(publicKey)); + opsEncryptionEntity.setPrivateKey(AesGcmUtils.encrypt(privateKey)); + opsEncryptionEntity.setKeySecurity(SECRET); + encryptionService.save(opsEncryptionEntity); + } + + /** + * update key pair secret + */ + public void updateKeyPairSecret() { + OpsEncryptionEntity opsEncryptionEntity = encryptionMapper.selectOne(null); + if (Objects.nonNull(opsEncryptionEntity) && !StrUtil.equalsIgnoreCase(SECRET, + opsEncryptionEntity.getKeySecurity())) { + opsEncryptionEntity.setKeySecurity(SECRET); + opsEncryptionEntity.setPublicKey(AesGcmUtils.encrypt(opsEncryptionEntity.getPublicKey())); + opsEncryptionEntity.setPrivateKey(AesGcmUtils.encrypt(opsEncryptionEntity.getPrivateKey())); + encryptionMapper.updateById(opsEncryptionEntity); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/HostServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/HostServiceImpl.java new file mode 100644 index 000000000..c79fbd1b3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/HostServiceImpl.java @@ -0,0 +1,815 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/HostServiceImpl + * .java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.StrUtil; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.jcraft.jsch.ChannelShell; +import com.jcraft.jsch.Session; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.agent.AgentInstallEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.core.domain.model.ops.*; +import org.opengauss.admin.common.core.domain.model.ops.host.HostMonitorVO; +import org.opengauss.admin.common.core.domain.model.ops.host.OpsHostVO; +import org.opengauss.admin.common.core.domain.model.ops.host.SSHBody; +import org.opengauss.admin.common.core.domain.model.ops.host.tag.HostTagInputDto; +import org.opengauss.admin.common.core.handler.ops.cache.SSHChannelManager; +import org.opengauss.admin.common.core.handler.ops.cache.TaskManager; +import org.opengauss.admin.common.core.handler.ops.cache.WsConnectorManager; +import org.opengauss.admin.common.core.vo.HostInfoVo; +import org.opengauss.admin.common.enums.OsSupportMap; +import org.opengauss.admin.common.enums.agent.AgentStatus; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.OpsAssert; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.common.utils.excel.ImportAsynInfoUtils; +import org.opengauss.admin.common.utils.ops.JschUtil; +import org.opengauss.admin.common.utils.ops.WsUtil; +import org.opengauss.admin.system.domain.HostRecordDataListener; +import org.opengauss.admin.system.mapper.ops.OpsHostMapper; +import org.opengauss.admin.system.plugin.beans.SshLogin; +import org.opengauss.admin.system.service.HostMonitorCacheService; +import org.opengauss.admin.system.service.JschExecutorService; +import org.opengauss.admin.system.service.ops.*; +import org.opengauss.agent.service.IAgentInstallService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.*; +import java.util.stream.Collectors; + +/** + * HostService + * + * @author lhf + * @date 2022/8/7 22:28 + **/ +@Slf4j +@Service +public class HostServiceImpl extends ServiceImpl implements IHostService { + private final Object sessionLock = new Object(); + private String isSwitchingLanguage; + @Autowired + private IHostUserService hostUserService; + @Autowired + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + @Autowired + private OpsHostMapper hostMapper; + @Autowired + private JschExecutorService jschExecutorService; + @Autowired + private EncryptionUtils encryptionUtils; + @Autowired + @Lazy + private IOpsClusterService clusterService; + @Autowired + private WsConnectorManager wsConnectorManager; + @Autowired + private WsUtil wsUtil; + @Autowired + private JschUtil jschUtil; + @Autowired + @Lazy + private IOpsHostTagRelService opsHostTagRelService; + @Autowired + @Lazy + private IOpsHostTagService opsHostTagService; + @Autowired + @Lazy + private HostMonitorCacheService hostMonitorCacheService; + @Autowired + @Lazy + private IAgentInstallService agentInstallService; + + private Cache> errorExcel = CacheBuilder.newBuilder() + .maximumSize(1000) + .expireAfterWrite(1, TimeUnit.DAYS) + .build(); + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean add(HostBody hostBody) { + OpsHostEntity hostEntity = getByPublicIp(hostBody.getPublicIp()); + if (Objects.nonNull(hostEntity) && hostEntity.getPort().equals(hostBody.getPort())) { + throw new OpsException("host[" + hostEntity.getPublicIp() + "]already exists, please do not add it again"); + } + OpsAssert.isTrue(StrUtil.isNotEmpty(hostBody.getUsername()), "username is empty"); + SshLogin sshLogin = new SshLogin(hostBody.getPublicIp(), hostBody.getPort(), hostBody.getUsername(), + encryptionUtils.decrypt(hostBody.getPassword())); + hostEntity = hostBody.toHostEntity(buildHostInfo(sshLogin)); + save(hostEntity); + opsHostTagRelService.cleanHostTag(hostEntity.getHostId()); + opsHostTagService.addTag(HostTagInputDto.of(hostBody.getTags(), hostEntity.getHostId())); + HostUserBody hostUserBody = hostBody.toUserBody(hostEntity.getHostId()); + return hostUserService.add(hostEntity, hostUserBody); + } + + @Override + public void invokeFile(String uuid, HashMap fileStreamMap, String currentLocale) { + isSwitchingLanguage = currentLocale; + ExecutorService executor = Executors.newCachedThreadPool(); + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + RequestContextHolder.setRequestAttributes(attributes, true); + final InputStream inputStream = fileStreamMap.get(uuid); + Future future = executor.submit(new Runnable() { + @Override + public void run() { + try { + startInvoke(inputStream, uuid); + } catch (OpsException e) { + log.error("Unable to import.", e); + ImportAsynInfoUtils.getAsynInfo(uuid).setMsg(e.getMessage()); + ImportAsynInfoUtils.getAsynInfo(uuid).setEnd(true); + throw new OpsException("Unable to import."); + } + } + }); + } + + @Override + public void downloadTemplate(HttpServletResponse response, String currentLocale) { + try { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + String fileName = URLEncoder.encode("模板", StandardCharsets.UTF_8).replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); + OutputStream out = response.getOutputStream(); + if ("zh-CN".equals(currentLocale)) { + EasyExcel.write(out, HostRecord.class).sheet("模板").doWrite(new ArrayList()); + } else { + EasyExcel.write(out, HostRecord.class) + .head(head(0)) + .sheet("Template") + .doWrite(new ArrayList()); + } + out.flush(); + out.close(); + } catch (IOException e) { + log.error("Download template failed.", e); + throw new OpsException("Download template failed."); + } + } + + @Override + public void downloadErrorExcel(HttpServletResponse response, String uuid) { + List errorhostRecords = errorExcel.getIfPresent(uuid); + try { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + String fileName = URLEncoder.encode("错误报告" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()), + StandardCharsets.UTF_8).replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); + OutputStream out = response.getOutputStream(); + if ("zh-CN".equals(isSwitchingLanguage)) { + EasyExcel.write(out, ErrorHostRecord.class).sheet("错误报告").doWrite(errorhostRecords); + } else { + EasyExcel.write(out, ErrorHostRecord.class) + .head(head(1)) + .sheet("Error Report") + .doWrite(errorhostRecords); + } + out.flush(); + out.close(); + } catch (IOException e) { + log.error("Download failed.", e); + throw new OpsException("Download failed."); + } + } + + private void startInvoke(InputStream inputStream, String uuid) { + try { + List hostRecords = readExcelFile(inputStream); + if (CollectionUtils.isEmpty(hostRecords) || hostRecords.size() == 0) { + log.error("Failed to parse data."); + throw new OpsException("Failed to parse data."); + } + ImportAsynInfoUtils.getAsynInfo(uuid).setTotality(hostRecords.size()); + Map> groupedHostLists = groupHostRecordsByIP(hostRecords); + invoke(groupedHostLists, uuid); + ImportAsynInfoUtils.getAsynInfo(uuid).setEnd(true); + } catch (IllegalArgumentException e) { + log.error("File format mismatch.", e); + throw new OpsException("The format of the file you are trying to import does not match the template. " + + "Please re-download the template and fill it out again."); + } catch (ExcelDataConvertException e) { + log.error("Data parsing format error.", e); + throw new OpsException(e.getRowIndex() + "row" + (e.getColumnIndex() + 1) + + "column,parsing exception, please modify and re-upload."); + } + } + + private List readExcelFile(InputStream inputStream) { + HostRecordDataListener hostRecordDataListener = new HostRecordDataListener(this, isSwitchingLanguage); + try { + return EasyExcel.read(inputStream, HostRecord.class, hostRecordDataListener).sheet().doReadSync(); + } catch (Exception e) { + log.error("Failed to parse data.", e); + throw new OpsException("Failed to parse data."); + } + } + + private Map> groupHostRecordsByIP(List hostRecords) { + Map> map = new HashMap<>(); + for (HostRecord hostRecord : hostRecords) { + List list = map.getOrDefault(hostRecord.getPublicIp(), new ArrayList<>()); + list.add(hostRecord); + map.put(hostRecord.getPublicIp(), list); + } + return map; + } + + private void invoke(Map> groupedHostLists, String uuid) { + ExecutorService executorService = Executors.newFixedThreadPool(16); + List> futures = new ArrayList<>(); + for (List list : groupedHostLists.values()) { + Future future = executorService.submit(() -> { + for (HostRecord hostRecord : list) { + try { + invokeRecord(hostRecord, uuid); + ImportAsynInfoUtils.addSuccessSum(uuid); + } catch (OpsException e) { + log.error("Record of failure.", e); + ImportAsynInfoUtils.addErrorSum(uuid); + hostRecord.setRemark(e.getMessage()); + addHostRecordToErrorMap(uuid, errorExcel, hostRecord); + } finally { + ImportAsynInfoUtils.addDoneSum(uuid); + } + } + }); + futures.add(future); + } + for (Future future : futures) { + try { + future.get(); + } catch (InterruptedException | ExecutionException e) { + log.error("Failed to obtain the thread execution result.", e); + throw new OpsException(e.getMessage()); + } + } + executorService.shutdown(); + } + + private void invokeRecord(@Validated HostRecord hostRecord, String uuid) { + if (!checkRecord(hostRecord)) { + throw new OpsException("Some attributes have not been filled in."); + } + OpsHostEntity hostEntity = getByPublicIp(hostRecord.getPublicIp()); + if (Objects.isNull(hostEntity)) { + addHostInfo(hostRecord); + } else { + addUserInfo(hostRecord, hostEntity); + } + } + + private void addHostInfo(HostRecord hostRecord) { + SshLogin sshLogin = new SshLogin(hostRecord.getPublicIp(), hostRecord.getPort(), hostRecord.getUserName(), + encryptionUtils.decrypt(hostRecord.getPassword())); + hostRecord.toHostEntity(getHostInfoVo(sshLogin)); + HostBody hostBody = hostRecord.toHostBody(); + hostBody.setPassword(encryptionUtils.encrypt(hostBody.getPassword())); + add(hostBody); + } + + private void addUserInfo(HostRecord hostRecord, OpsHostEntity hostEntity) { + HostUserBody hostUserBody = new HostUserBody(); + hostUserBody.setHostId(hostEntity.getHostId()); + hostUserBody.setUsername(hostRecord.getUserName()); + hostUserBody.setPassword(encryptionUtils.encrypt(hostRecord.getPassword())); + hostUserService.add(hostEntity, hostUserBody); + List tags = addAllTagsToNewCollection(hostRecord.getTags()); + opsHostTagService.addTag(HostTagInputDto.of(tags, hostEntity.getHostId())); + } + + private List> createBaseHeaders() { + return new ArrayList<>( + Arrays.asList(List.of("id"), List.of("name"), List.of("privateIP"), List.of("publicIP"), List.of("port"), + List.of("userName"), List.of("password"), List.of("isAdmin(yes|no)"), List.of("tags"), + List.of("remark"))); + } + + private List> head(int tempOrerro) { + List> headers = createBaseHeaders(); + if (tempOrerro == 1) { + headers.add(List.of("timestamp")); + } + return headers; + } + + private synchronized void addHostRecordToErrorMap(String uuid, Cache> errorExcel, + HostRecord hostRecord) { + List list = errorExcel.getIfPresent(uuid); + if (list == null) { + list = new ArrayList<>(); + } + list.add(hostRecord.toErrorHostRecord()); + errorExcel.put(uuid, list); + } + + private Boolean checkRecord(HostRecord hostRecord) { + return Objects.nonNull(hostRecord.getName()) && Objects.nonNull(hostRecord.getIsAdmin()) && Objects.nonNull( + hostRecord.getPublicIp()) && Objects.nonNull(hostRecord.getPrivateIp()) && Objects.nonNull( + hostRecord.getPort()) && Objects.nonNull(hostRecord.getUserName()) && Objects.nonNull( + hostRecord.getPassword()); + } + + private List addAllTagsToNewCollection(List tags) { + List hostTags = new ArrayList<>(); + if (tags != null) { + hostTags.addAll(tags); + } + return hostTags; + } + + private HostInfoVo buildHostInfo(SshLogin sshLogin) { + HostInfoVo hostInfoVo = new HostInfoVo(); + try { + log.info("get host info {}", sshLogin); + hostInfoVo = getHostInfoVo(sshLogin); + } catch (OpsException e) { + log.error("Failed to get host info", e); + } + return hostInfoVo; + } + + private HostInfoVo getHostInfoVo(SshLogin sshLogin) { + HostInfoVo hostInfoVo = new HostInfoVo(); + hostInfoVo.setHostname(jschExecutorService.getHostname(sshLogin)); + hostInfoVo.setCpuArch(jschExecutorService.getCpuArch(sshLogin)); + hostInfoVo.setOs(jschExecutorService.getOs(sshLogin)); + hostInfoVo.setOsVersion(jschExecutorService.getOsVersion(sshLogin)); + return hostInfoVo; + } + + @Override + public boolean ping(HostBody hostBody) { + SshLogin sshLogin = new SshLogin(hostBody.getPublicIp(), hostBody.getPort(), hostBody.getUsername(), + encryptionUtils.decrypt(hostBody.getPassword())); + return jschExecutorService.checkOsUserExist(sshLogin); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean del(String hostId) { + if (clusterService.countByHostId(hostId) > 0) { + throw new OpsException("The host is being used by the cluster"); + } + if (agentInstallService.hasInstall(hostId)) { + throw new OpsException("The host must be uninstalled the agent before deleting host"); + } + removeById(hostId); + hostUserService.removeByHostId(hostId); + hostMonitorCacheService.deleteHostCache(hostId); + return true; + } + + @Override + public boolean ping(String hostId, String rootPassword) { + OpsHostEntity hostEntity = getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + List opsHostUserEntities = hostUserService.listHostUserByHostId(hostId); + if (CollUtil.isEmpty(opsHostUserEntities)) { + throw new OpsException("User information not found"); + } + OpsHostUserEntity userEntity = opsHostUserEntities.stream().findAny().get(); + SshLogin pingLogin = new SshLogin(hostEntity.getPublicIp(), hostEntity.getPort(), userEntity.getUsername(), + encryptionUtils.decrypt(userEntity.getPassword())); + return jschExecutorService.checkOsUserExist(pingLogin); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean edit(String hostId, HostBody hostBody) { + OpsHostEntity hostEntity = checkHostExist(hostId); + if (StringUtils.isBlank(hostBody.getName())) { + throw new OpsException("Host name cannot be empty"); + } + if (ObjectUtils.isEmpty(hostBody.getPort())) { + throw new OpsException("Host port cannot be empty"); + } + if (!hostEntity.getPrivateIp().equals(hostBody.getPrivateIp()) || !hostEntity.getPublicIp() + .equals(hostBody.getPublicIp())) { + throw new OpsException("Host public and private IP cannot be modified"); + } + if (StringUtils.isAnyBlank(hostBody.getUsername(), hostBody.getPassword())) { + throw new OpsException("Host username and password cannot be empty"); + } + SshLogin sshLogin = new SshLogin(hostBody.getPublicIp(), hostBody.getPort(), hostBody.getUsername(), + encryptionUtils.decrypt(hostBody.getPassword())); + HostInfoVo hostInfoVo = getHostInfoVo(sshLogin); + hostEntity.setName(hostBody.getName()); + hostEntity.setPort(hostBody.getPort()); + hostEntity.setRemark(hostBody.getRemark()); + hostEntity.setOs(hostInfoVo.getOs()); + hostEntity.setOsVersion(hostInfoVo.getOsVersion()); + hostEntity.setCpuArch(hostInfoVo.getCpuArch()); + hostEntity.setHostname(hostInfoVo.getHostname()); + updateById(hostEntity); + opsHostTagRelService.cleanHostTag(hostId); + opsHostTagService.addTag(HostTagInputDto.of(hostBody.getTags(), hostId)); + return true; + } + + @Override + public IPage pageHost(Page page, String name, Set tagIds, String os) { + if (Objects.isNull(tagIds)) { + tagIds = Collections.emptySet(); + } + final IPage opsHostVOIPage = hostMapper.pageHost(page, name, tagIds, os, tagIds.size()); + final List records = opsHostVOIPage.getRecords(); + populateIsRememberVO(records); + populateTags(records); + populateAgentInstallInfoAndStatus(records); + return opsHostVOIPage; + } + + private void populateAgentInstallInfoAndStatus(List records) { + Map agentInstallMap = agentInstallService.queryAgentInstallInfo(); + for (OpsHostVO record : records) { + AgentInstallEntity agentInstall = agentInstallMap.get(record.getHostId()); + if (Objects.isNull(agentInstall)) { + record.setAgentStatus(AgentStatus.UNINSTALL); + } else { + record.setAgentName(agentInstall.getAgentName()); + record.setAgentInstallPort(agentInstall.getAgentPort()); + record.setAgentStatus(agentInstall.getStatus()); + record.setAgentInstallPath(agentInstall.getInstallPath()); + record.setAgentInstallUsername(agentInstall.getInstallUser()); + } + } + } + + @Override + public void ssh(SSHBody sshBody) { + WsSession wsSession = wsConnectorManager.getSession(sshBody.getBusinessId()) + .orElseThrow(() -> new OpsException("websocket session not exist")); + Session session = jschExecutorService.createSession( + new SshLogin(sshBody.getIp(), sshBody.getSshPort(), sshBody.getSshUsername(), + encryptionUtils.decrypt(sshBody.getSshPassword()))); + ChannelShell channelShell = jschExecutorService.openChannelShell(session); + SSHChannelManager.registerChannelShell(sshBody.getBusinessId(), channelShell); + Future future = threadPoolTaskExecutor.submit( + () -> jschExecutorService.channelToWsSession(channelShell, wsSession)); + TaskManager.registry(sshBody.getBusinessId(), future); + } + + @Override + public void ssh(String hostId, SSHBody sshBody) { + OpsHostEntity hostEntity = getById(hostId); + OpsAssert.nonNull(hostEntity, "host info not found"); + OpsAssert.isTrue(StrUtil.isNotEmpty(sshBody.getSshUsername()), "ssh username can not be empty"); + String password = null; + OpsHostUserEntity hostUserEntity = hostUserService.getHostUserByUsername(hostId, sshBody.getSshUsername()); + OpsAssert.nonNull(hostUserEntity, "host user " + sshBody.getSshUsername() + " info not found"); + password = hostUserEntity.getPassword(); + if (StrUtil.isEmpty(password)) { + password = sshBody.getSshPassword(); + } + OpsAssert.isTrue(StrUtil.isNotEmpty(password), "ssh username password can not be empty"); + WsSession wsSession = wsConnectorManager.getSession(sshBody.getBusinessId()) + .orElseThrow(() -> new OpsException("websocket session not exist")); + Session session = jschExecutorService.createSession( + new SshLogin(sshBody.getIp(), sshBody.getSshPort(), sshBody.getSshUsername(), + encryptionUtils.decrypt(password))); + ChannelShell channelShell = jschUtil.openChannelShell(session); + SSHChannelManager.registerChannelShell(sshBody.getBusinessId(), channelShell); + Future future = threadPoolTaskExecutor.submit(() -> jschUtil.channelToWsSession(channelShell, wsSession)); + TaskManager.registry(sshBody.getBusinessId(), future); + } + + @Override + public Map mapOsByIps(Set ipSet) { + if (CollUtil.isEmpty(ipSet)) { + return Collections.emptyMap(); + } + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostEntity.class) + .in(OpsHostEntity::getPublicIp, ipSet); + List hostList = list(queryWrapper); + return hostList.stream() + .filter(host -> StrUtil.isNotEmpty(host.getOs())) + .collect(Collectors.toMap(OpsHostEntity::getPublicIp, OpsHostEntity::getOs)); + } + + @Override + public List listAll(String azId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostEntity.class); + List list = list(queryWrapper); + populateIsRemember(list); + return list; + } + + @Override + public Map pageMonitor(List hostIds, String businessId) { + Map res = new HashMap<>(); + res.put("res", true); + List pageHostList; + try { + pageHostList = list().stream() + .filter(hostEntity -> hostIds.contains(hostEntity.getHostId())) + .map(OpsHostEntity::getHostId) + .collect(Collectors.toList()); + if (pageHostList.size() != hostIds.size()) { + res.put("miss_msg", "some host ids not found"); + res.put("miss", CollUtil.subtract(hostIds, pageHostList)); + } + } catch (IllegalArgumentException | OpsException e) { + res.put("res", false); + res.put("msg", e.getMessage()); + return res; + } + Runnable worker = TaskManager.getRegistryWorker(businessId); + if (worker == null) { + WsSession wsSession = wsConnectorManager.getSession(businessId) + .orElseThrow(() -> new OpsException("response session does not exist")); + worker = new PageMonitorWorker(wsSession, pageHostList); + TaskManager.registryWorker(businessId, worker); + Future future = threadPoolTaskExecutor.submit(worker); + TaskManager.registry(businessId, future); + } else { + if (worker instanceof PageMonitorWorker pageMonitorWorker) { + pageMonitorWorker.resetHostIds(pageHostList); + } else { + throw new OpsException("worker is not PageMonitorWorker"); + } + } + return res; + } + + /** + * PageMonitorWorker + */ + @AllArgsConstructor + class PageMonitorWorker implements Runnable { + private final WsSession wsSession; + private final List hostIds; + + /** + * reset hostIds + * + * @param hostIds hostIds + */ + public void resetHostIds(List hostIds) { + this.hostIds.clear(); + this.hostIds.addAll(hostIds); + } + + @Override + public void run() { + try { + doPageMonitor(); + } finally { + hostIds.clear(); + wsUtil.close(wsSession); + } + } + + private void doPageMonitor() { + while (true) { + synchronized (sessionLock) { + if (!wsSession.getSession().isOpen()) { + break; + } + } + for (String hostId : hostIds) { + if (!wsSession.getSession().isOpen()) { + break; + } + HostMonitorVO hostMonitorVO = new HostMonitorVO(); + try { + hostMonitorVO.setHostId(hostId); + String[] net = netMonitor(hostId); + hostMonitorVO.setUpSpeed(net[0]); + hostMonitorVO.setDownSpeed(net[1]); + hostMonitorVO.setCpu(cpuMonitor(hostId)); + hostMonitorVO.setMemory(memoryMonitor(hostId)); + hostMonitorVO.setDisk(diskMonitor(hostId)); + } catch (OpsException | NullPointerException ex) { + log.error("Failed to collect monitor data for hostId: {}", hostId, ex); + } + if (wsSession.getSession().isOpen()) { + wsUtil.sendText(wsSession, JSON.toJSONString(hostMonitorVO)); + } + } + ThreadUtil.safeSleep(1000L); + } + } + } + + @Override + public void updateHostOsVersion(OpsHostEntity opsHostEntity) { + String hostId = opsHostEntity.getHostId(); + List userList = hostUserService.listHostUserByHostId(hostId); + OpsAssert.isTrue(CollUtil.isNotEmpty(userList), "host does not config user"); + OpsHostUserEntity userEntity = userList.stream().findAny().get(); + SshLogin sshLogin = new SshLogin(opsHostEntity.getPublicIp(), opsHostEntity.getPort(), userEntity.getUsername(), + encryptionUtils.decrypt(userEntity.getPassword())); + opsHostEntity.setOsVersion(jschExecutorService.getOsVersion(sshLogin)); + updateById(opsHostEntity); + } + + private void doMonitor(WsSession wsSession, String hostId) { + while (wsSession.getSession().isOpen()) { + HostMonitorVO hostMonitorVO = new HostMonitorVO(); + try { + String[] net = netMonitor(hostId); + hostMonitorVO.setUpSpeed(net[0]); + hostMonitorVO.setDownSpeed(net[1]); + hostMonitorVO.setCpu(cpuMonitor(hostId)); + hostMonitorVO.setMemory(memoryMonitor(hostId)); + hostMonitorVO.setDisk(diskMonitor(hostId)); + } catch (OpsException | NullPointerException ex) { + log.error("doMonitor error", ex); + } + wsUtil.sendText(wsSession, JSON.toJSONString(hostMonitorVO)); + try { + TimeUnit.SECONDS.sleep(1L); + } catch (InterruptedException e) { + throw new OpsException("thread is interrupted"); + } + } + } + + private String diskMonitor(String hostId) { + return hostMonitorCacheService.getDiskUse(hostId); + } + + private String memoryMonitor(String hostId) { + return hostMonitorCacheService.getMemoryUsing(hostId); + } + + private String cpuMonitor(String hostId) { + return hostMonitorCacheService.getCpuUsing(hostId); + } + + private String[] netMonitor(String hostId) { + return hostMonitorCacheService.getNetMonitor(hostId, false); + } + + private void populateTags(List list) { + List hostIds = list.stream().map(OpsHostVO::getHostId).collect(Collectors.toList()); + if (CollUtil.isNotEmpty(hostIds)) { + Map> hostTagNamesMap = opsHostTagRelService.mapByHostIds(hostIds); + for (OpsHostVO opsHostVO : list) { + opsHostVO.setTags(hostTagNamesMap.get(opsHostVO.getHostId())); + } + } + } + + private void populateIsRememberVO(List list) { + List hostIds = list.stream().map(OpsHostVO::getHostId).collect(Collectors.toList()); + if (CollUtil.isNotEmpty(hostIds)) { + List opsHostUserEntities = hostUserService.listHostUserByHostIdList(hostIds); + List isRememberHostIds = new ArrayList<>(); + for (OpsHostUserEntity opsHostUserEntity : opsHostUserEntities) { + String username = opsHostUserEntity.getUsername(); + if ("root".equalsIgnoreCase(username) && StrUtil.isNotEmpty(opsHostUserEntity.getPassword())) { + isRememberHostIds.add(opsHostUserEntity.getHostId()); + } + } + for (OpsHostVO opsHostEntity : list) { + opsHostEntity.setIsRemember(isRememberHostIds.contains(opsHostEntity.getHostId())); + } + } + } + + private void populateIsRemember(List list) { + List hostIds = list.stream().map(OpsHostEntity::getHostId).collect(Collectors.toList()); + if (CollUtil.isNotEmpty(hostIds)) { + List opsHostUserEntities = hostUserService.listHostUserByHostIdList(hostIds); + List isRememberHostIds = new ArrayList<>(); + for (OpsHostUserEntity opsHostUserEntity : opsHostUserEntities) { + String username = opsHostUserEntity.getUsername(); + if ("root".equalsIgnoreCase(username) && StrUtil.isNotEmpty(opsHostUserEntity.getPassword())) { + isRememberHostIds.add(opsHostUserEntity.getHostId()); + } + } + for (OpsHostEntity opsHostEntity : list) { + opsHostEntity.setIsRemember(isRememberHostIds.contains(opsHostEntity.getHostId())); + } + } + } + + @Override + public OpsHostEntity getByPublicIp(String publicIp) { + if (StrUtil.isEmpty(publicIp)) { + return null; + } + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostEntity.class) + .eq(OpsHostEntity::getPublicIp, publicIp); + return getOne(queryWrapper, false); + } + + /** + * build os entity mapped + * + * @param hostId hostId + * @return os entity mapped + */ + @Override + public OpsHostEntity getMappedHostEntityById(String hostId) { + OpsHostEntity opsHostEntity = getById(hostId); + OsSupportMap osMapEnum = OsSupportMap.of(opsHostEntity.getOs(), opsHostEntity.getOsVersion(), + opsHostEntity.getCpuArch()); + opsHostEntity.setOs(osMapEnum.getOs()); + opsHostEntity.setOsVersion(osMapEnum.getOsVersion()); + return opsHostEntity; + } + + @Override + public void updateHostOsName(String host, String osName) { + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(OpsHostEntity.class) + .set(OpsHostEntity::getOs, osName) + .eq(OpsHostEntity::getPublicIp, host); + update(updateWrapper); + } + + @Override + public void updateHostOsVersion(String host, String osVersion) { + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(OpsHostEntity.class) + .set(OpsHostEntity::getOsVersion, osVersion) + .eq(OpsHostEntity::getPublicIp, host); + update(updateWrapper); + } + + @Override + public void updateHostCpu(String host, String cpuArch, String cpuCoreNum, String cpuFreq) { + String cpuFreqNum = ""; + if (StrUtil.isNotEmpty(cpuFreq)) { + cpuFreqNum = cpuFreq.replace("GHz", ""); + } + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(OpsHostEntity.class) + .set(StrUtil.isNotEmpty(cpuArch), OpsHostEntity::getCpuArch, cpuArch) + .set(StrUtil.isNotEmpty(cpuCoreNum), OpsHostEntity::getLogicalCores, cpuCoreNum) + .set(StrUtil.isNotEmpty(cpuFreq), OpsHostEntity::getCpuFreq, cpuFreqNum) + .eq(OpsHostEntity::getPublicIp, host); + update(updateWrapper); + } + + private OpsHostEntity checkHostExist(String hostId) { + OpsHostEntity hostEntity = getById(hostId); + if (Objects.isNull(hostEntity)) { + log.error("Cannot get host information by host id: {}", hostId); + throw new OpsException("host information does not exist"); + } + return hostEntity; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/HostUserServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/HostUserServiceImpl.java new file mode 100644 index 000000000..3218072b4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/HostUserServiceImpl.java @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostUserServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl + * /HostUserServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.jcraft.jsch.Session; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.constant.ops.SshCommandConstants; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.core.domain.model.ops.HostUserBody; +import org.opengauss.admin.common.core.domain.model.ops.JschResult; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.exception.ops.UserAlreadyExistsException; +import org.opengauss.admin.common.utils.ops.JschUtil; +import org.opengauss.admin.system.mapper.ops.OpsHostUserMapper; +import org.opengauss.admin.system.plugin.beans.SshLogin; +import org.opengauss.admin.system.service.JschExecutorService; +import org.opengauss.admin.system.service.ops.IHostUserService; +import org.opengauss.admin.system.service.ops.IOpsClusterNodeService; +import org.opengauss.agent.service.IAgentInstallService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import jakarta.annotation.Resource; + +/** + * @author lhf + * @date 2022/8/8 15:40 + **/ +@Slf4j +@Service +public class HostUserServiceImpl extends ServiceImpl implements IHostUserService { + @Autowired + private JschUtil jschUtil; + @Resource + private JschExecutorService jschExecutorService; + @Autowired + private EncryptionUtils encryptionUtils; + @Autowired + @Lazy + private IOpsClusterNodeService opsClusterNodeService; + @Resource + @Lazy + private IAgentInstallService agentInstallService; + + @Override + public List listHostUserByHostId(String hostId) { + if (StrUtil.isEmpty(hostId)) { + return Collections.emptyList(); + } + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostUserEntity.class) + .eq(OpsHostUserEntity::getHostId, hostId); + return list(queryWrapper); + } + + @Override + public boolean removeByHostId(String hostId) { + if (StrUtil.isEmpty(hostId)) { + return false; + } + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostUserEntity.class) + .eq(OpsHostUserEntity::getHostId, hostId); + return remove(queryWrapper); + } + + @Override + public List listHostUserByHostIdList(List hostIdList) { + if (CollUtil.isEmpty(hostIdList)) { + return Collections.emptyList(); + } + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostUserEntity.class) + .in(OpsHostUserEntity::getHostId, hostIdList); + return list(queryWrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean add(OpsHostEntity host, HostUserBody hostUserBody) { + // check user exists + String username = hostUserBody.getUsername(); + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostUserEntity.class) + .eq(OpsHostUserEntity::getHostId, hostUserBody.getHostId()) + .eq(OpsHostUserEntity::getUsername, username); + long count = count(queryWrapper); + if (count > 0) { + throw new UserAlreadyExistsException("User information already exists"); + } + + OpsHostUserEntity opsHostUserEntity = hostUserBody.toEntity(false); + return doAddOrEdit(host, opsHostUserEntity, true); + } + + private void createPhysicalUser(SshLogin rootLogin, String username, String password) { + try { + jschExecutorService.createOsUser(rootLogin, username); + } catch (OpsException ex) { + throw new OpsException("create" + username + "User failed"); + } + Session session = null; + try { + session = jschExecutorService.createSession(rootLogin); + Map autoResponse = new HashMap<>(); + autoResponse.put("password:", encryptionUtils.decrypt(password)); + autoResponse.put("password:", encryptionUtils.decrypt(password)); + JschResult jschResult = null; + try { + String command = MessageFormat.format(SshCommandConstants.CHANGE_OMM_PASSWORD_TEMPLATE, username); + jschResult = jschUtil.executeCommand(command, session, autoResponse); + } catch (InterruptedException e) { + throw new OpsException("Do not interrupt the thread"); + } + if (0 != jschResult.getExitCode()) { + log.error("update" + username + "User password failed, exit code: {}, log: {}", + jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("update" + username + "User password failed"); + } + } catch (IOException e) { + log.error("update" + username + "User password failed", e); + if (Objects.nonNull(session) && session.isConnected()) { + session.disconnect(); + } + throw new OpsException("update" + username + "User password failed"); + } finally { + if (Objects.nonNull(session) && session.isConnected()) { + session.disconnect(); + } + } + } + + /** + * The function of editing users currently only supports modifying the user passwords, + * and does not support the passwords of remote server users. + * After modifying the passwords of server users, the passwords of users + * managed by DataKit itself need to be manually modified and updated. + * + * @param host host info + * @param hostUserId host User Id + * @param hostUserBody host User Body + * @return boolean edit result + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean edit(OpsHostEntity host, String hostUserId, HostUserBody hostUserBody) { + // check user exists and username is not changed + OpsHostUserEntity hostUserEntity = getById(hostUserId); + if (Objects.isNull(hostUserEntity)) { + throw new OpsException("User information does not exist"); + } + if (!hostUserEntity.getUsername().equals(hostUserBody.getUsername())) { + throw new OpsException("The username cannot be modified"); + } + + OpsHostUserEntity newEntity = hostUserBody.toEntity(false); + newEntity.setHostId(hostUserEntity.getHostId()); + newEntity.setHostUserId(hostUserEntity.getHostUserId()); + return doAddOrEdit(host, newEntity, false); + } + + private boolean doAddOrEdit(OpsHostEntity hostEntity, OpsHostUserEntity userEntity, boolean isAdd) { + SshLogin sshLogin = new SshLogin(hostEntity.getPublicIp(), hostEntity.getPort(), userEntity.getUsername(), + encryptionUtils.decrypt(userEntity.getPassword())); + if (userEntity.isRootUser()) { + if (!jschExecutorService.checkOsUserExist(sshLogin)) { + throw new OpsException("Incorrect password, please enter correct password"); + } + userEntity.setSudo(true); + return isAdd ? save(userEntity) : updateById(userEntity); + } + if (jschExecutorService.checkOsUserExist(sshLogin)) { + userEntity.setSudo(false); + if (jschExecutorService.checkOsUserSudo(sshLogin)) { + userEntity.setSudo(true); + } + return isAdd ? save(userEntity) : updateById(userEntity); + } + // if user cannot connect, use root user to check user exists, if not exists, create user or throw error + String hostId = userEntity.getHostId(); + OpsHostUserEntity rootUser = getRootUserByHostId(hostId); + if (Objects.isNull(rootUser)) { + throw new OpsException("User does not exist or the password is incorrect"); + } + SshLogin rootLogin = new SshLogin(hostEntity.getPublicIp(), hostEntity.getPort(), rootUser.getUsername(), + encryptionUtils.decrypt(rootUser.getPassword())); + if (jschExecutorService.checkOsUserExist(rootLogin, userEntity.getUsername())) { + throw new OpsException("Incorrect password, please enter correct password"); + } + if (isAdd) { + createPhysicalUser(rootLogin, userEntity.getUsername(), userEntity.getPassword()); + userEntity.setSudo(false); + return save(userEntity); + } else { + throw new OpsException("User does not exist"); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean del(String hostUserId) { + OpsHostUserEntity hostUserEntity = getById(hostUserId); + if (Objects.isNull(hostUserEntity)) { + throw new OpsException("User information does not exist"); + } + if (opsClusterNodeService.countByHostUserId(hostUserId) > 0) { + throw new OpsException("User is being used by the cluster"); + } + if (agentInstallService.countByHostUserId(hostUserId) > 0) { + throw new OpsException("User is being used by the agent"); + } + List list = listHostUserByHostId(hostUserEntity.getHostId()); + if (list.size() > 1) { + return removeById(hostUserId); + } else { + log.warn("The last user of host[{}] cannot be deleted", hostUserEntity.getHostId()); + return false; + } + } + + @Override + public OpsHostUserEntity getOmmUserByHostId(String hostId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostUserEntity.class) + .eq(OpsHostUserEntity::getHostId, hostId) + .eq(OpsHostUserEntity::getUsername, "omm"); + return getOne(queryWrapper, false); + } + + @Override + public OpsHostUserEntity getRootUserByHostId(String hostId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostUserEntity.class) + .eq(OpsHostUserEntity::getHostId, hostId) + .eq(OpsHostUserEntity::getUsername, "root"); + return getOne(queryWrapper, false); + } + + @Override + public OpsHostUserEntity getAnyUserByHostId(String hostId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostUserEntity.class) + .eq(OpsHostUserEntity::getHostId, hostId); + List list = list(queryWrapper); + return list.stream().filter(user -> StrUtil.isNotEmpty(user.getPassword())).findAny().orElse(null); + } + + @Override + public void cleanPassword(String hostUserId) { + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(OpsHostUserEntity.class) + .set(OpsHostUserEntity::getPassword, null) + .set(OpsHostUserEntity::getSudo, Boolean.TRUE) + .eq(OpsHostUserEntity::getHostUserId, hostUserId); + update(updateWrapper); + } + + @Override + public OpsHostUserEntity getHostUserByUsername(String hostId, String username) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostUserEntity.class) + .eq(OpsHostUserEntity::getHostId, hostId) + .eq(OpsHostUserEntity::getUsername, username); + return getOne(queryWrapper, false); + } + + @Override + public boolean hasRootPermission(OpsHostEntity hostEntity, String userId) { + OpsHostUserEntity userEntity = getById(userId); + if (Objects.isNull(userEntity)) { + log.error("Cannot get user information by user id: {}", userId); + throw new OpsException("User information does not exist"); + } + + if (userEntity.isRootUser()) { + return true; + } + + SshLogin sshLogin = new SshLogin(hostEntity.getPublicIp(), hostEntity.getPort(), userEntity.getUsername(), + encryptionUtils.decrypt(userEntity.getPassword())); + return jschExecutorService.checkOsUserSudo(sshLogin); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsAzServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsAzServiceImpl.java new file mode 100644 index 000000000..31b3dc9d5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsAzServiceImpl.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsAzServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsAzServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.common.core.domain.entity.ops.OpsAzEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.system.mapper.ops.OpsAzMapper; +import org.opengauss.admin.system.service.ops.IOpsAzService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +/** + * @author lhf + * @date 2022/8/6 20:50 + **/ +@Service +public class OpsAzServiceImpl extends ServiceImpl implements IOpsAzService { + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean add(OpsAzEntity az) { + if (hasName(az.getName())) { + throw new OpsException("name already exists"); + } + return save(az); + } + + @Override + public boolean hasName(String name) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsAzEntity.class) + .eq(OpsAzEntity::getName, name); + return count(queryWrapper) > 0; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsCheckService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsCheckService.java new file mode 100644 index 000000000..e8f6e3a10 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsCheckService.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsCheckService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsCheckService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.collect.Maps; +import org.opengauss.admin.common.core.domain.entity.ops.OpsCheckEntity; +import org.opengauss.admin.system.mapper.ops.OpsCheckMapper; +import org.opengauss.admin.system.service.ops.IOpsCheckService; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author lhf + * @date 2022/11/13 17:17 + **/ +@Service +public class OpsCheckService extends ServiceImpl implements IOpsCheckService { + @Override + public OpsCheckEntity getLastResByClusterId(String clusterId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsCheckEntity.class) + .eq(OpsCheckEntity::getClusterId, clusterId) + .orderByDesc(OpsCheckEntity::getCreateTime); + + return getOne(queryWrapper, false); + } + + @Override + public Map mapLastResByClusterIds(List clusterIds) { + if (CollUtil.isEmpty(clusterIds)) { + return Maps.newHashMap(); + } + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsCheckEntity.class) + .in(OpsCheckEntity::getClusterId, clusterIds); + + List list = list(queryWrapper); + + Map res = new HashMap<>(); + Map> map = new HashMap<>(); + for (OpsCheckEntity opsCheckEntity : list) { + String clusterId = opsCheckEntity.getClusterId(); + List opsCheckEntities = map.get(clusterId); + if (Objects.isNull(opsCheckEntities)){ + opsCheckEntities = new ArrayList<>(); + map.put(clusterId, opsCheckEntities); + } + + opsCheckEntities.add(opsCheckEntity); + } + + map.forEach((k,v)->{ + v.sort(Comparator.comparing(OpsCheckEntity::getCreateTime).reversed()); + res.put(k, v.get(0)); + }); + return res; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsClusterNodeServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsClusterNodeServiceImpl.java new file mode 100644 index 000000000..43f0ae33f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsClusterNodeServiceImpl.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterNodeServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsClusterNodeServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.collect.Maps; +import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.core.dto.ops.ClusterNodeDto; +import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.system.mapper.ops.OpsClusterNodeMapper; +import org.opengauss.admin.system.service.ops.IHostService; +import org.opengauss.admin.system.service.ops.IHostUserService; +import org.opengauss.admin.system.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.system.service.ops.IOpsClusterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author lhf + * @date 2022/8/18 09:16 + **/ +@Service +public class OpsClusterNodeServiceImpl extends ServiceImpl implements IOpsClusterNodeService { + + @Autowired + private IHostService hostService; + @Autowired + private IHostUserService hostUserService; + @Autowired + private IOpsClusterService opsClusterService; + + @Override + public List listClusterNodeByClusterId(String clusterId) { + if (StrUtil.isEmpty(clusterId)) { + return Collections.emptyList(); + } + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsClusterNodeEntity.class) + .eq(OpsClusterNodeEntity::getClusterId, clusterId); + return list(queryWrapper); + } + + @Override + public long countByHostId(String hostId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsClusterNodeEntity.class) + .eq(OpsClusterNodeEntity::getHostId, hostId); + return count(queryWrapper); + } + + @Override + public long countByHostUserId(String hostUserId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsClusterNodeEntity.class) + .eq(OpsClusterNodeEntity::getInstallUserId, hostUserId); + return count(queryWrapper); + } + + @Override + public Map> listClusterNodeByClusterIds(List clusterIds) { + if (CollUtil.isEmpty(clusterIds)) { + return Maps.newHashMap(); + } + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsClusterNodeEntity.class) + .in(OpsClusterNodeEntity::getClusterId, clusterIds); + + return list(queryWrapper).stream().collect(Collectors.groupingBy(OpsClusterNodeEntity::getClusterId)); + } + + /** + * get ClusterNodeDto info by clusterNodeId + * + * @param clusterNodeId clusterNodeId + * @return ClusterNodeDto + */ + @Override + public ClusterNodeDto getClusterNodeDtoByNodeId(String clusterNodeId) { + ClusterNodeDto nodeDto = new ClusterNodeDto(); + if (StringUtils.isNotEmpty(clusterNodeId)) { + OpsClusterNodeEntity entity = this.getById(clusterNodeId); + if (entity != null) { + OpsClusterEntity cluster = opsClusterService.getById(entity.getClusterId()); + OpsHostEntity hostEntity = hostService.getById(entity.getHostId()); + OpsHostUserEntity hostUserEntity = hostUserService.getById(entity.getInstallUserId()); + if (cluster != null) { + nodeDto.setIsMiniVersion(OpenGaussVersionEnum.MINIMAL_LIST.name() + .equals(cluster.getVersion().name())); + } + nodeDto.setInstallPath(entity.getInstallPath()); + if (nodeDto.getIsMiniVersion()) { + nodeDto.setDataPath(entity.getDataPath() + "/single_node"); + } else { + nodeDto.setDataPath(entity.getDataPath()); + } + if (hostEntity != null) { + nodeDto.setPublicIp(hostEntity.getPublicIp()); + nodeDto.setPort(hostEntity.getPort()); + } + if (hostUserEntity != null) { + nodeDto.setInstallUserName(hostUserEntity.getUsername()); + nodeDto.setInstallPassword(hostUserEntity.getPassword()); + } + } + } + return nodeDto; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsClusterServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsClusterServiceImpl.java new file mode 100644 index 000000000..b95f9ca3e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsClusterServiceImpl.java @@ -0,0 +1,693 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl + * /OpsClusterServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; + +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.ops.*; +import org.opengauss.admin.common.core.domain.model.ops.*; +import org.opengauss.admin.common.core.domain.model.ops.check.*; +import org.opengauss.admin.common.core.handler.ops.cache.TaskManager; +import org.opengauss.admin.common.core.handler.ops.cache.WsConnectorManager; +import org.opengauss.admin.common.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.ops.WsUtil; +import org.opengauss.admin.system.mapper.ops.OpsClusterMapper; +import org.opengauss.admin.system.plugin.beans.SshLogin; +import org.opengauss.admin.system.service.HostMonitorCacheService; +import org.opengauss.admin.system.service.JschExecutorService; +import org.opengauss.admin.system.service.ops.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.sql.*; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; +import java.util.stream.Collectors; + +import jakarta.annotation.Resource; + +/** + * @author lhf + * @date 2022/8/6 17:38 + **/ +@Slf4j +@Service +public class OpsClusterServiceImpl extends ServiceImpl + implements IOpsClusterService { + @Autowired + private IHostService hostService; + @Autowired + private IHostUserService hostUserService; + @Autowired + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + @Autowired + @Lazy + private IOpsClusterNodeService opsClusterNodeService; + @Autowired + private IOpsJdbcDbClusterService opsJdbcDbClusterService; + @Autowired + private IOpsCheckService opsCheckService; + @Autowired + private WsConnectorManager wsConnectorManager; + @Autowired + private WsUtil wsUtil; + @Resource + private JschExecutorService jschExecutorService; + @Resource + private HostMonitorCacheService hostMonitorCacheService; + @Autowired + private EncryptionUtils encryptionUtils; + + @Override + public List listCluster() { + List res = new ArrayList<>(); + List opsClusterEntities = list( + Wrappers.lambdaQuery(OpsClusterEntity.class).orderByDesc(OpsClusterEntity::getCreateTime)); + List clusterIds = opsClusterEntities.stream() + .map(OpsClusterEntity::getClusterId) + .collect(Collectors.toList()); + Map checkResMap = opsCheckService.mapLastResByClusterIds(clusterIds); + Map> clusterNodeMap = opsClusterNodeService.listClusterNodeByClusterIds( + clusterIds); + Map hostEntityMap = new HashMap<>(); + List hostIds = clusterNodeMap.values() + .stream() + .flatMap(nodeList -> nodeList.stream()) + .map(OpsClusterNodeEntity::getHostId) + .collect(Collectors.toList()); + Map hostUserEntityMap = new HashMap<>(); + Map rootUserMap = new HashMap<>(); + if (CollUtil.isNotEmpty(hostIds)) { + List opsHostEntities = hostService.listByIds(hostIds); + if (CollUtil.isNotEmpty(opsHostEntities)) { + hostEntityMap.putAll( + opsHostEntities.stream().collect(Collectors.toMap(OpsHostEntity::getHostId, Function.identity()))); + List opsHostUserEntities = hostUserService.listHostUserByHostIdList( + opsHostEntities.stream().map(OpsHostEntity::getHostId).collect(Collectors.toList())); + hostUserEntityMap.putAll(opsHostUserEntities.stream() + .collect(Collectors.toMap(OpsHostUserEntity::getHostUserId, Function.identity()))); + hostUserEntityMap.putAll(opsHostUserEntities.stream() + .filter(hostUserEntity -> "root".equals(hostUserEntity.getUsername())) + .collect(Collectors.toMap(OpsHostUserEntity::getHostId, Function.identity()))); + } + } + for (OpsClusterEntity opsClusterEntity : opsClusterEntities) { + OpsClusterVO opsClusterVO = OpsClusterVO.of(opsClusterEntity); + String clusterId = opsClusterEntity.getClusterId(); + OpsCheckEntity checkRes = checkResMap.get(clusterId); + if (Objects.nonNull(checkRes)) { + HashMap checkSummary = new HashMap<>(); + CheckVO checkVO = parseCheckResToCheckVO(checkRes.getCheckRes()); + if (Objects.nonNull(checkVO)) { + Map> summary = checkVO.summary(); + if (CollUtil.isNotEmpty(summary)) { + summary.forEach((k, v) -> { + checkSummary.put(k, v.size()); + }); + } + } + opsClusterVO.setCheckSummary(checkSummary); + opsClusterVO.setLastCheckAt(checkRes.getCreateTime()); + } + res.add(opsClusterVO); + List clusterNodeVOS = new ArrayList<>(); + opsClusterVO.setClusterNodes(clusterNodeVOS); + List opsClusterNodeEntities = clusterNodeMap.get(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + continue; + } + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + OpsClusterNodeVO opsClusterNodeVO = OpsClusterNodeVO.of(opsClusterNodeEntity); + String hostId = opsClusterNodeEntity.getHostId(); + OpsHostEntity hostEntity = hostEntityMap.get(hostId); + if (Objects.nonNull(hostEntity)) { + OpsHostUserEntity installUser = hostUserEntityMap.get(opsClusterNodeEntity.getInstallUserId()); + opsClusterNodeVO.setPublicIp(hostEntity.getPublicIp()); + opsClusterNodeVO.setPrivateIp(hostEntity.getPrivateIp()); + opsClusterNodeVO.setHostPort(hostEntity.getPort()); + OpsHostUserEntity rootUser = hostUserEntityMap.get(hostId); + opsClusterNodeVO.setIsRemember( + Objects.nonNull(rootUser) && StrUtil.isNotEmpty(rootUser.getPassword())); + opsClusterNodeVO.setHostname(hostEntity.getHostname()); + opsClusterNodeVO.setHostId(hostId); + opsClusterNodeVO.setDbPort(opsClusterEntity.getPort()); + opsClusterNodeVO.setDbName("postgres"); + opsClusterNodeVO.setDbUser(opsClusterEntity.getDatabaseUsername()); + opsClusterNodeVO.setDbUserPassword(opsClusterEntity.getDatabasePassword()); + opsClusterNodeVO.setHostOs(hostEntity.getOs()); + opsClusterNodeVO.setHostCpuArch(hostEntity.getCpuArch()); + if (OpenGaussVersionEnum.ENTERPRISE == opsClusterEntity.getVersion()) { + opsClusterNodeVO.setInstallPath(opsClusterEntity.getInstallPath()); + } + if (Objects.nonNull(installUser)) { + opsClusterNodeVO.setInstallUserName(installUser.getUsername()); + } + } + clusterNodeVOS.add(opsClusterNodeVO); + } + } + return res; + } + + @Override + public ClusterSummaryVO summary() { + ClusterSummaryVO clusterSummaryVO = new ClusterSummaryVO(); + clusterSummaryVO.setClusterNum(count()); + clusterSummaryVO.setHostNum(hostService.count()); + clusterSummaryVO.setNodeNum(opsJdbcDbClusterService.count()); + return clusterSummaryVO; + } + + @Override + public void monitor(String clusterId, String hostId, String businessId) { + OpsClusterEntity clusterEntity = getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("Cluster node information does not exist"); + } + OpsClusterNodeEntity nodeEntity = opsClusterNodeEntities.stream() + .filter(opsClusterNodeEntity -> opsClusterNodeEntity.getHostId().equals(hostId)) + .findFirst() + .orElseThrow(() -> new OpsException("Cluster node information does not exist")); + String installUserId = nodeEntity.getInstallUserId(); + OpsHostEntity hostEntity = hostService.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("Node host information does not exist"); + } + OpsHostUserEntity hostUserEntity = hostUserService.getById(installUserId); + if (Objects.isNull(hostUserEntity)) { + throw new OpsException("Node installation user information does not exist"); + } + final String realDataPath = nodeEntity.getDataPath(); + Future future = threadPoolTaskExecutor.submit(() -> { + Connection connection = null; + WsSession wsSession = wsConnectorManager.getSession(businessId) + .orElseThrow(() -> new OpsException("response session does not exist")); + try { + String driver = "org.opengauss.Driver"; + String sourceURL = "jdbc:opengauss://" + hostEntity.getPublicIp() + ":" + clusterEntity.getPort() + + "/postgres"; + Properties info = new Properties(); + info.setProperty("user", clusterEntity.getDatabaseUsername()); + info.setProperty("password", encryptionUtils.decrypt(clusterEntity.getDatabasePassword())); + try { + Class.forName(driver); + connection = DriverManager.getConnection(sourceURL, info); + // create ssH login + SshLogin sshLogin = new SshLogin(hostEntity.getPublicIp(), hostEntity.getPort(), + hostUserEntity.getUsername(), encryptionUtils.decrypt(hostUserEntity.getPassword())); + MonitorParam monitorParam = new MonitorParam(hostEntity.getHostId(), realDataPath, + clusterEntity.getVersion(), clusterEntity.getEnvPath()); + doMonitor(wsSession, sshLogin, monitorParam, connection); + } catch (Exception e) { + log.error("Failed to obtain a connection {}", e.getMessage()); + } + } finally { + if (Objects.nonNull(connection)) { + try { + connection.close(); + } catch (SQLException e) { + } + } + wsUtil.close(wsSession); + } + + }); + TaskManager.registry(businessId, future); + } + + @Override + public Map threadPoolMonitor() { + Map res = new HashMap<>(); + int activeCount = threadPoolTaskExecutor.getActiveCount(); + int poolSize = threadPoolTaskExecutor.getPoolSize(); + int corePoolSize = threadPoolTaskExecutor.getCorePoolSize(); + int keepAliveSeconds = threadPoolTaskExecutor.getKeepAliveSeconds(); + res.put("activeCount", activeCount); + res.put("poolSize", poolSize); + res.put("corePoolSize", corePoolSize); + res.put("keepAliveSeconds", keepAliveSeconds); + return res; + } + + @Override + public long countByHostId(String hostId) { + return opsClusterNodeService.countByHostId(hostId); + } + + private CheckVO parseCheckResToCheckVO(String result) { + CheckVO checkVO = new CheckVO(); + // cluster + CheckClusterVO checkClusterVO = new CheckClusterVO(); + checkClusterVO.setCheckClusterState(parseCheckItem(result, "CheckClusterState")); + checkClusterVO.setCheckDBParams(parseCheckItem(result, "CheckDBParams")); + checkClusterVO.setCheckDebugSwitch(parseCheckItem(result, "CheckDebugSwitch")); + checkClusterVO.setCheckDirPermissions(parseCheckItem(result, "CheckDirPermissions")); + checkClusterVO.setCheckEnvProfile(parseCheckItem(result, "CheckEnvProfile")); + checkClusterVO.setCheckReadonlyMode(parseCheckItem(result, "CheckReadonlyMode")); + checkClusterVO.setCheckDilateSysTab(parseCheckItem(result, "CheckDilateSysTab")); + checkClusterVO.setCheckProStartTime(parseCheckItem(result, "CheckProStartTime")); + checkClusterVO.setCheckMpprcFile(parseCheckItem(result, "CheckMpprcFile")); + checkVO.setCluster(checkClusterVO); + // database + CheckDbVO checkDbVO = new CheckDbVO(); + checkDbVO.setCheckCurConnCount(parseCheckItem(result, "CheckCurConnCount")); + checkDbVO.setCheckPgxcgroup(parseCheckItem(result, "CheckPgxcgroup")); + checkDbVO.setCheckCursorNum(parseCheckItem(result, "CheckCursorNum")); + checkDbVO.setCheckNodeGroupName(parseCheckItem(result, "CheckNodeGroupName")); + checkDbVO.setCheckTableSpace(parseCheckItem(result, "CheckTableSpace")); + checkDbVO.setCheckSysadminUser(parseCheckItem(result, "CheckSysadminUser")); + checkDbVO.setCheckHashIndex(parseCheckItem(result, "CheckHashIndex")); + checkDbVO.setCheckPgxcRedistb(parseCheckItem(result, "CheckPgxcRedistb")); + checkDbVO.setCheckNodeGroupName(parseCheckItem(result, "CheckNodeGroupName")); + checkDbVO.setCheckTDDate(parseCheckItem(result, "CheckTDDate")); + checkVO.setDb(checkDbVO); + // os + CheckOSVO checkOSVO = new CheckOSVO(); + checkOSVO.setCheckEncoding(parseCheckItem(result, "CheckEncoding")); + checkOSVO.setCheckFirewall(parseCheckItem(result, "CheckFirewall")); + checkOSVO.setCheckKernelVer(parseCheckItem(result, "CheckKernelVer")); + checkOSVO.setCheckMaxHandle(parseCheckItem(result, "CheckMaxHandle")); + checkOSVO.setCheckNTPD(parseCheckItem(result, "CheckNTPD")); + checkOSVO.setCheckOSVer(parseCheckItem(result, "CheckOSVer")); + checkOSVO.setCheckSysParams(parseCheckItem(result, "CheckSysParams")); + checkOSVO.setCheckTHP(parseCheckItem(result, "CheckTHP")); + checkOSVO.setCheckTimeZone(parseCheckItem(result, "CheckTimeZone")); + checkOSVO.setCheckCPU(parseCheckItem(result, "CheckCPU")); + checkOSVO.setCheckSshdService(parseCheckItem(result, "CheckSshdService")); + checkOSVO.setCheckSshdConfig(parseCheckItem(result, "CheckSshdConfig")); + checkOSVO.setCheckCrondService(parseCheckItem(result, "CheckCrondService")); + checkOSVO.setCheckStack(parseCheckItem(result, "CheckStack")); + checkOSVO.setCheckSysPortRange(parseCheckItem(result, "CheckSysPortRange")); + checkOSVO.setCheckMemInfo(parseCheckItem(result, "CheckMemInfo")); + checkOSVO.setCheckHyperThread(parseCheckItem(result, "CheckHyperThread")); + checkOSVO.setCheckMaxProcMemory(parseCheckItem(result, "CheckMaxProcMemory")); + checkOSVO.setCheckBootItems(parseCheckItem(result, "CheckBootItems")); + checkOSVO.setCheckKeyProAdj(parseCheckItem(result, "CheckKeyProAdj")); + checkOSVO.setCheckFilehandle(parseCheckItem(result, "CheckFilehandle")); + checkOSVO.setCheckDropCache(parseCheckItem(result, "CheckDropCache")); + checkVO.setOs(checkOSVO); + // device + CheckDeviceVO checkDeviceVO = new CheckDeviceVO(); + checkDeviceVO.setCheckBlockdev(parseCheckItem(result, "CheckBlockdev")); + checkDeviceVO.setCheckDiskFormat(parseCheckItem(result, "CheckDiskFormat")); + checkDeviceVO.setCheckSpaceUsage(parseCheckItem(result, "CheckSpaceUsage")); + checkDeviceVO.setCheckInodeUsage(parseCheckItem(result, "CheckInodeUsage")); + checkDeviceVO.setCheckSwapMemory(parseCheckItem(result, "CheckSwapMemory")); + checkDeviceVO.setCheckLogicalBlock(parseCheckItem(result, "CheckLogicalBlock")); + checkDeviceVO.setCheckIOrequestqueue(parseCheckItem(result, "CheckIOrequestqueue")); + checkDeviceVO.setCheckMaxAsyIOrequests(parseCheckItem(result, "CheckMaxAsyIOrequests")); + checkDeviceVO.setCheckIOConfigure(parseCheckItem(result, "CheckIOConfigure")); + checkVO.setDevice(checkDeviceVO); + // network + CheckNetworkVO checkNetworkVO = new CheckNetworkVO(); + checkNetworkVO.setCheckMTU(parseCheckItem(result, "CheckMTU")); + checkNetworkVO.setCheckPing(parseCheckItem(result, "CheckPing")); + checkNetworkVO.setCheckRXTX(parseCheckItem(result, "CheckRXTX")); + checkNetworkVO.setCheckNetWorkDrop(parseCheckItem(result, "CheckNetWorkDrop")); + checkNetworkVO.setCheckMultiQueue(parseCheckItem(result, "CheckMultiQueue")); + checkNetworkVO.setCheckRouting(parseCheckItem(result, "CheckRouting")); + checkNetworkVO.setCheckNICModel(parseCheckItem(result, "CheckNICModel")); + checkVO.setNetwork(checkNetworkVO); + return checkVO; + } + + private CheckItemVO parseCheckItem(String result, String item) { + item = item + "."; + CheckItemVO checkItemVO = new CheckItemVO(); + checkItemVO.setName(item); + int indexStart = result.indexOf(item); + int reIndex = result.indexOf("\n", indexStart); + int indexEnd = result.indexOf(".............", reIndex); + if ("CheckMpprcFile.".equals(item)) { + indexEnd = result.indexOf("Analysis the check result", reIndex); + } + if (indexStart == -1) { + log.warn("gs check item {} not found", item); + return checkItemVO; + } + if (indexEnd <= indexStart) { + log.error("index end le index start"); + return checkItemVO; + } + String substring = result.substring(indexStart, indexEnd); + if (StrUtil.isNotEmpty(substring)) { + String[] split = substring.split("\n"); + if (split.length < 2) { + return checkItemVO; + } + StringBuffer msg = new StringBuffer(); + for (int i = 0; i < split.length; i++) { + String line = split[i]; + if (i == 0) { + String status = line.substring(line.lastIndexOf(".") + 1, line.indexOf("\r")); + checkItemVO.setStatus(status); + } else { + if (StrUtil.isNotEmpty(line.trim())) { + msg.append(line).append("\n"); + } else { + break; + } + } + } + checkItemVO.setMsg(msg.toString()); + } + return checkItemVO; + } + + private void doMonitor(WsSession wsSession, SshLogin sshLogin, MonitorParam monitorParam, Connection connection) { + AtomicBoolean hasError = new AtomicBoolean(false); + while (wsSession.getSession().isOpen() && !hasError.get()) { + NodeMonitorVO nodeMonitorVO = new NodeMonitorVO(); + CountDownLatch countDownLatch = new CountDownLatch(2); + threadPoolTaskExecutor.submit(() -> { + try { + nodeMonitorVO.setTime(System.currentTimeMillis()); + nodeMonitorVO.setCpu(cpu(monitorParam.getHostId())); + nodeMonitorVO.setMemory(memory(monitorParam.getHostId())); + nodeMonitorVO.setNet(net(monitorParam.getHostId())); + nodeMonitorVO.setKernel(kernel(monitorParam.getHostId())); + nodeMonitorVO.setMemorySize(memorySize(monitorParam.getHostId())); + } catch (Exception e) { + log.error("time error : {}", e.getMessage()); + hasError.set(true); + } finally { + countDownLatch.countDown(); + } + }); + threadPoolTaskExecutor.submit(() -> { + try { + nodeMonitorVO.setState(state(sshLogin, monitorParam.getVersion(), monitorParam.getDataPath(), + monitorParam.getEnvPath())); + nodeMonitorVO.setLock(lock(connection)); + nodeMonitorVO.setSession(session(connection)); + nodeMonitorVO.setConnectNum(connectNum(connection)); + nodeMonitorVO.setSessionMemoryTop10(sessionMemoryTop10(connection)); + } catch (Exception e) { + log.error("state monitor error : {}", e.getMessage()); + hasError.set(true); + } finally { + countDownLatch.countDown(); + } + }); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + log.error("monitor await thread is interrupted {}", e.getMessage()); + } + wsUtil.sendText(wsSession, JSON.toJSONString(nodeMonitorVO)); + try { + TimeUnit.SECONDS.sleep(5L); + } catch (InterruptedException e) { + log.error("monitor sleep is interrupted {}", e.getMessage()); + } + } + } + + /** + * MonitorParam 类的功能说明 + *

+ * 该类用于存储监控参数。 + */ + @AllArgsConstructor + @Data + private class MonitorParam { + private String hostId; + private String dataPath; + private OpenGaussVersionEnum version; + private String envPath; + } + + private List> sessionMemoryTop10(Connection connection) { + String sql = "SELECT * FROM pv_session_memory_detail() ORDER BY usedsize desc limit 10"; + List> res = new ArrayList<>(); + try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) { + while (resultSet.next()) { + HashMap model = new HashMap<>(); + model.put(resultSet.getString("sessid"), resultSet.getString("usedsize")); + res.add(model); + } + } catch (Exception e) { + log.error("Failed to query session memory top10 {}", e.getMessage()); + throw new OpsException("Failed to query session memory top10"); + } + return res; + } + + @Override + public String connectNum(Connection connection) { + String sql = "SELECT count(*) FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS s"; + String res = null; + try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) { + if (resultSet.next()) { + res = resultSet.getString("count"); + } + } catch (Exception e) { + log.error("Failed to query the number of connections {}", e.getMessage()); + throw new OpsException("Failed to query the number of connections"); + } + return res; + } + + @Override + public String session(Connection connection) { + String sql = "SELECT count(*) FROM pg_stat_activity"; + String res = null; + try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) { + if (resultSet.next()) { + res = resultSet.getString("count"); + } + } catch (Exception e) { + log.error("Failed to query the number of connections {}", e.getMessage()); + throw new OpsException("Failed to query the number of connections"); + } + return res; + } + + @Override + public String lock(Connection connection) { + String sql = "SELECT count(*) FROM pg_locks"; + String res = null; + try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) { + if (resultSet.next()) { + res = resultSet.getString("count"); + } + } catch (Exception e) { + log.error("Failed to query the number of connections {}", e.getMessage()); + throw new OpsException("Failed to query the number of connections"); + } + return res; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public CheckSummaryVO check(String clusterId, String rootPassword) { + OpsClusterEntity clusterEntity = getById(clusterId); + if (clusterEntity.getVersionNum().equals("5.1.0")) { + throw new OpsException("Unsupported openGauss version: 5.1.0"); + } + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information cannot be empty"); + } + if (OpenGaussVersionEnum.ENTERPRISE != clusterEntity.getVersion()) { + throw new OpsException("Only Enterprise edition is supported"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("Cluster node information cannot be empty"); + } + OpsClusterNodeEntity nodeEntity = opsClusterNodeEntities.stream() + .filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER) + .findFirst() + .orElseThrow(() -> new OpsException("Masternode information not found")); + String hostId = nodeEntity.getHostId(); + String installUserId = nodeEntity.getInstallUserId(); + OpsHostEntity hostEntity = hostService.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + OpsHostUserEntity hostUserEntity = hostUserService.getById(installUserId); + if (Objects.isNull(hostUserEntity)) { + throw new OpsException("Installation user information does not exist"); + } + SshLogin sshLogin = new SshLogin(hostEntity.getPublicIp(), hostEntity.getPort(), hostUserEntity.getUsername(), + encryptionUtils.decrypt(hostUserEntity.getPassword())); + String res; + String rootPwd = getRootPassword(hostId, rootPassword); + if (hasRootPassword(rootPwd)) { + res = doCheckWithRootItems(rootPwd, sshLogin, clusterEntity.getEnvPath()); + } else { + // 如果没有root密码,则跳过root检查项 + res = doCheckSkipRootItems(sshLogin, clusterEntity.getEnvPath()); + } + OpsCheckEntity opsCheckEntity = new OpsCheckEntity(); + opsCheckEntity.setCheckRes(res); + opsCheckEntity.setClusterId(clusterId); + opsCheckService.save(opsCheckEntity); + CheckVO checkVO = parseCheckResToCheckVO(res); + return CheckSummaryVO.of(checkVO); + } + + private boolean hasRootPassword(String rootPassword) { + return StrUtil.isNotEmpty(rootPassword); + } + + private String getRootPassword(String hostId, String rootPassword) { + OpsHostUserEntity rootUserEntity = hostUserService.getRootUserByHostId(hostId); + String pwd = Objects.nonNull(rootUserEntity) ? rootUserEntity.getPassword() : ""; + return StrUtil.isNotEmpty(pwd) ? pwd : rootPassword; + } + + private String doCheckWithRootItems(String rootPassword, SshLogin sshLogin, String envPath) { + String command = "gs_check -e inspect"; + Map autoResponse = new HashMap<>(); + autoResponse.put("Please enter root privileges user[root]:", "root"); + autoResponse.put("Please enter password for user[root]:", encryptionUtils.decrypt(rootPassword)); + return doCheckItems(sshLogin, command, envPath, autoResponse); + } + + private String doCheckSkipRootItems(SshLogin sshLogin, String envPath) { + String command = "gs_check -e inspect --skip-root-items"; + Map autoResponse = new HashMap<>(); + return doCheckItems(sshLogin, command, envPath, autoResponse); + } + + private String doCheckItems(SshLogin sshLogin, String command, String envPath, Map autoResponse) { + log.info("One-click self-test start"); + try { + return jschExecutorService.execCommandAutoResponse(sshLogin, command, envPath, autoResponse); + } catch (Exception e) { + log.error("One-click self-test results {}", e.getMessage()); + throw new OpsException("One key self-test error"); + } finally { + log.info("One-click self-test end"); + } + } + + private String memorySize(String hostId) { + return hostMonitorCacheService.getMemoryTotal(hostId); + } + + private String kernel(String hostId) { + return hostMonitorCacheService.getCpuCoreNum(hostId); + } + + private String state(SshLogin sshLogin, OpenGaussVersionEnum version, String dataPath, String envPath) { + if (OpenGaussVersionEnum.ENTERPRISE == version) { + String command = "gs_om -t status --detail"; + Map res = new HashMap<>(); + String result = jschExecutorService.execCommand(sshLogin, command, envPath); + int clusterStateIndex = result.indexOf("cluster_state"); + String clusterState = null; + if (clusterStateIndex >= 1) { + int splitIndex = result.indexOf(":", clusterStateIndex); + int lineEndIndex = result.indexOf("\n", clusterStateIndex); + clusterState = result.substring(splitIndex + 1, lineEndIndex).trim(); + res.put("cluster_state", clusterState); + } + return parseNodeStateAndRole(result, res); + } else { + String command = "gs_ctl status -D " + dataPath; + String result = jschExecutorService.execCommand(sshLogin, command, envPath); + return result.contains("no server running") ? "false" : "true"; + } + } + + private static String parseNodeStateAndRole(String result, Map res) { + Map nodeState = new HashMap<>(1); + Map nodeRole = new HashMap<>(1); + int datanodeStateIndex = result.indexOf("Datanode State"); + if (datanodeStateIndex >= 1) { + int splitIndex = result.indexOf("------------------", datanodeStateIndex); + String dataNodeStateStr = result.substring(splitIndex); + String[] dataNode = dataNodeStateStr.split("\n"); + parseNodeStateAndRole(dataNode, nodeState, nodeRole); + } + res.put("nodeState", nodeState); + res.put("nodeRole", nodeRole); + return JSON.toJSONString(res); + } + + private static void parseNodeStateAndRole(String[] dataNode, Map nodeState, + Map nodeRole) { + for (String s : dataNode) { + String[] s1 = s.replaceAll(" +", " ").split(" "); + String state = ""; + if (s1.length >= 9) { + for (int i = 8; i < s1.length; i++) { + state += (s1[i] + " "); + } + nodeState.put(s1[1], state.trim()); + nodeRole.put(s1[1], s1[7]); + } else if (s1.length >= 8) { + for (int i = 7; i < s1.length; i++) { + state += (s1[i] + " "); + } + nodeState.put(s1[1], state.trim()); + nodeRole.put(s1[1], s1[6]); + } + } + } + + private List net(String hostId) { + List res = new ArrayList<>(); + String[] netMonitor = hostMonitorCacheService.getNetMonitor(hostId, true); + NodeNetMonitor nodeNetMonitor = new NodeNetMonitor(); + nodeNetMonitor.setFace(StrUtil.trim(netMonitor[0])); + nodeNetMonitor.setReceive(netMonitor[1]); + nodeNetMonitor.setTransmit(netMonitor[2]); + res.add(nodeNetMonitor); + return res; + } + + private String memory(String hostId) { + return hostMonitorCacheService.getMemoryUsing(hostId); + } + + private String cpu(String hostId) { + return hostMonitorCacheService.getCpuUsing(hostId); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsHostTagRelService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsHostTagRelService.java new file mode 100644 index 000000000..09d587132 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsHostTagRelService.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsHostTagRelService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsHostTagRelService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostTagEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostTagRel; +import org.opengauss.admin.common.core.domain.model.ops.host.tag.HostTagInputDto; +import org.opengauss.admin.system.mapper.ops.OpsHostTagRelMapper; +import org.opengauss.admin.system.service.ops.IOpsHostTagRelService; +import org.opengauss.admin.system.service.ops.IOpsHostTagService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author lhf + * @date 2023/3/14 23:40 + **/ +@Service +public class OpsHostTagRelService extends ServiceImpl implements IOpsHostTagRelService { + + @Autowired + private IOpsHostTagService opsHostTagService; + + @Override + public void addHostTagRel(List hostIds, List tags) { + List hostTagRelList = new ArrayList<>(); + for (String hostId : hostIds) { + for (OpsHostTagEntity tag : tags) { + OpsHostTagRel opsHostTagRel = new OpsHostTagRel(); + opsHostTagRel.setHostId(hostId); + opsHostTagRel.setTagId(tag.getHostTagId()); + opsHostTagRel.setCreateTime(new Date()); + hostTagRelList.add(opsHostTagRel); + } + } + + saveBatch(hostTagRelList); + } + + @Override + public Map> mapByHostIds(List hostIds) { + if (CollUtil.isEmpty(hostIds)) { + return Collections.emptyMap(); + } + + Map> tags = new HashMap<>(); + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostTagRel.class) + .in(OpsHostTagRel::getHostId, hostIds); + + List list = list(queryWrapper); + if (CollUtil.isNotEmpty(list)) { + List tagIds = list.stream().map(OpsHostTagRel::getTagId).collect(Collectors.toList()); + List tagEntityList = opsHostTagService.listByIds(tagIds); + Map tagMap = tagEntityList.stream().collect(Collectors.toMap(OpsHostTagEntity::getHostTagId, OpsHostTagEntity::getName)); + + for (OpsHostTagRel opsHostTagRel : list) { + String hostId = opsHostTagRel.getHostId(); + String tagId = opsHostTagRel.getTagId(); + + Set tagNames = tags.get(hostId); + if (Objects.isNull(tagNames)) { + tagNames = new HashSet<>(); + tags.put(hostId, tagNames); + } + tagNames.add(tagMap.get(tagId)); + } + } + + return tags; + } + + @Override + public void cleanHostTag(String hostId) { + if (StrUtil.isEmpty(hostId)) { + return; + } + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostTagRel.class) + .eq(OpsHostTagRel::getHostId, hostId); + + remove(queryWrapper); + } + + @Override + public void delByTagId(String tagId) { + if (StrUtil.isEmpty(tagId)) { + return; + } + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostTagRel.class) + .eq(OpsHostTagRel::getTagId, tagId); + remove(queryWrapper); + } + + @Override + public void delTagRelation(HostTagInputDto hostTagInputDto) { + if (CollUtil.isNotEmpty(hostTagInputDto.getNames()) + && CollUtil.isNotEmpty(hostTagInputDto.getHostIds())) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostTagRel.class) + .in(OpsHostTagRel::getHostId, hostTagInputDto.getHostIds()) + .in(OpsHostTagRel::getTagId, hostTagInputDto.getNames()); + + remove(queryWrapper); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsHostTagService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsHostTagService.java new file mode 100644 index 000000000..8b6cac67b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsHostTagService.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsHostTagService.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsHostTagService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostTagEntity; +import org.opengauss.admin.common.core.domain.model.ops.host.tag.HostTagInputDto; +import org.opengauss.admin.common.core.domain.model.ops.host.tag.HostTagPageVO; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.system.mapper.ops.OpsHostTagMapper; +import org.opengauss.admin.system.service.ops.IOpsHostTagRelService; +import org.opengauss.admin.system.service.ops.IOpsHostTagService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author lhf + * @date 2023/3/14 23:39 + **/ +@Service +public class OpsHostTagService extends ServiceImpl implements IOpsHostTagService { + @Autowired + @Lazy + private IOpsHostTagRelService opsHostTagRelService; + @Autowired + private OpsHostTagMapper opsHostTagMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public synchronized void addTag(HostTagInputDto hostTagInputDto) { + if (CollUtil.isEmpty(hostTagInputDto.getHostIds()) || CollUtil.isEmpty(hostTagInputDto.getNames())){ + return; + } + List tags = buildTags(hostTagInputDto.getNames()); + opsHostTagRelService.addHostTagRel(hostTagInputDto.getHostIds(),tags); + } + + @Override + public IPage page(Page page, String name) { + IPage tagPageVOIPage = opsHostTagMapper.page(page, name); + if (tagPageVOIPage.getRecords().size() != 0) { + List records = tagPageVOIPage.getRecords(); + for (HostTagPageVO record : records) { + if (record.getRelNum() == null) { + record.setRelNum(0); + } + } + } + return tagPageVOIPage; + } + + @Override + public synchronized void add(String name) { + if (exist(name)){ + throw new OpsException("tag already exists"); + } + buildTags(Arrays.asList(name)); + } + + @Override + public void update(String tagId, String name) { + OpsHostTagEntity tagEntity = getById(tagId); + if (Objects.isNull(tagEntity)){ + throw new OpsException("tag does not exist"); + } + + if (StrUtil.isEmpty(name)){ + throw new OpsException("Tag name cannot be empty"); + } + + tagEntity.setName(name); + tagEntity.setUpdateTime(new Date()); + updateById(tagEntity); + } + + private boolean exist(String name) { + if (StrUtil.isEmpty(name)){ + throw new OpsException("Tag name cannot be empty"); + } + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostTagEntity.class) + .eq(OpsHostTagEntity::getName, name); + + return CollUtil.isNotEmpty(list(queryWrapper)); + } + + private List buildTags(List names) { + List res = new ArrayList<>(); + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsHostTagEntity.class) + .in(OpsHostTagEntity::getName, names); + + List existTags = list(queryWrapper); + + if (CollUtil.isNotEmpty(existTags)){ + res.addAll(existTags); + names.removeAll(existTags.stream().map(OpsHostTagEntity::getName).collect(Collectors.toList())); + } + + if (CollUtil.isNotEmpty(names)){ + List newTags = new ArrayList<>(); + for (String name : names) { + OpsHostTagEntity opsHostTagEntity = new OpsHostTagEntity(); + opsHostTagEntity.setName(name); + opsHostTagEntity.setCreateTime(new Date()); + newTags.add(opsHostTagEntity); + } + + saveBatch(newTags); + res.addAll(newTags); + } + + return res; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsJdbcDbClusterNodeServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsJdbcDbClusterNodeServiceImpl.java new file mode 100644 index 000000000..00720088a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsJdbcDbClusterNodeServiceImpl.java @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsJdbcDbClusterNodeServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsJdbcDbClusterNodeServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.common.core.domain.entity.ops.OpsJdbcDbClusterEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsJdbcDbClusterNodeEntity; +import org.opengauss.admin.common.core.domain.model.ops.WsSession; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterNodeInputDto; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcInfo; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcMonitorVO; +import org.opengauss.admin.common.core.handler.ops.cache.TaskManager; +import org.opengauss.admin.common.core.handler.ops.cache.WsConnectorManager; +import org.opengauss.admin.common.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.common.enums.ops.DbTypeEnum; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.ops.JdbcUtil; +import org.opengauss.admin.common.utils.ops.WsUtil; +import org.opengauss.admin.system.mapper.ops.OpsJdbcDbClusterNodeMapper; +import org.opengauss.admin.system.service.ops.IOpsClusterService; +import org.opengauss.admin.system.service.ops.IOpsJdbcDbClusterNodeService; +import org.opengauss.admin.system.service.ops.IOpsJdbcDbClusterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * @author lhf + * @date 2023/1/13 11:09 + **/ +@Service +public class OpsJdbcDbClusterNodeServiceImpl extends ServiceImpl implements IOpsJdbcDbClusterNodeService { + @Autowired + private IOpsJdbcDbClusterService opsJdbcDbClusterService; + @Autowired + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + @Autowired + private WsConnectorManager wsConnectorManager; + @Autowired + private WsUtil wsUtil; + @Autowired + private IOpsClusterService opsClusterService; + @Autowired + private EncryptionUtils encryptionUtils; + + @Override + @Transactional(rollbackFor = Exception.class) + public void del(String clusterNodeId) { + OpsJdbcDbClusterNodeEntity clusterNodeEntity = getById(clusterNodeId); + if (Objects.isNull(clusterNodeEntity)) { + throw new OpsException("Node information does not exist"); + } + + String clusterId = clusterNodeEntity.getClusterId(); + List nodes = listNodeByClusterId(clusterId); + if (CollUtil.isNotEmpty(nodes) && nodes.size() == 1) { + opsJdbcDbClusterService.del(clusterNodeEntity.getClusterId()); + } else { + removeById(clusterNodeId); + } + } + + private List listNodeByClusterId(String clusterId) { + if (StrUtil.isEmpty(clusterId)) { + return Collections.emptyList(); + } + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsJdbcDbClusterNodeEntity.class) + .eq(OpsJdbcDbClusterNodeEntity::getClusterId, clusterId); + return list(queryWrapper); + } + + @Override + public OpsJdbcDbClusterNodeEntity getClusterNodeByIpAndPort(String ip, String port, String username) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsJdbcDbClusterNodeEntity.class) + .eq(OpsJdbcDbClusterNodeEntity::getIp, ip) + .eq(OpsJdbcDbClusterNodeEntity::getPort, port) + .eq(StrUtil.isNotEmpty(username), OpsJdbcDbClusterNodeEntity::getUsername, username); + return getOne(queryWrapper, false); + } + + @Override + public Set fuzzyQueryClusterIdsByIp(String name) { + Set res = new HashSet<>(); + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsJdbcDbClusterNodeEntity.class) + .like(OpsJdbcDbClusterNodeEntity::getIp, name) + .select(OpsJdbcDbClusterNodeEntity::getClusterId); + + List list = list(queryWrapper); + if (CollUtil.isNotEmpty(list)) { + res.addAll(list.stream().map(OpsJdbcDbClusterNodeEntity::getClusterId).collect(Collectors.toSet())); + } + return res; + } + + @Override + public Map> mapClusterNodesByClusterId(Set clusterIds) { + if (CollUtil.isEmpty(clusterIds)) { + return Collections.emptyMap(); + } + + Map> res = new HashMap<>(); + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsJdbcDbClusterNodeEntity.class) + .in(OpsJdbcDbClusterNodeEntity::getClusterId, clusterIds) + .orderByDesc(OpsJdbcDbClusterNodeEntity::getCreateTime); + + List list = list(queryWrapper); + if (CollUtil.isNotEmpty(list)) { + res = list.stream().collect(Collectors.groupingBy(OpsJdbcDbClusterNodeEntity::getClusterId)); + } + + return res; + } + + @Override + public void delByClusterId(String clusterId) { + if (StrUtil.isEmpty(clusterId)) { + return; + } + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsJdbcDbClusterNodeEntity.class) + .eq(OpsJdbcDbClusterNodeEntity::getClusterId, clusterId); + + remove(queryWrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(String clusterNodeId, JdbcDbClusterNodeInputDto clusterNodeInput) { + OpsJdbcDbClusterNodeEntity clusterNodeEntity = getById(clusterNodeId); + if (Objects.isNull(clusterNodeEntity)) { + throw new OpsException("Cluster node information does not exist"); + } + + String url = clusterNodeInput.getUrl(); + JdbcInfo jdbcInfo = JdbcUtil.parseUrl(url); + + OpsJdbcDbClusterNodeEntity opsJdbcDbClusterNodeEntity = new OpsJdbcDbClusterNodeEntity(); + opsJdbcDbClusterNodeEntity.setClusterNodeId(clusterNodeEntity.getClusterNodeId()); + opsJdbcDbClusterNodeEntity.setClusterId(clusterNodeEntity.getClusterId()); + opsJdbcDbClusterNodeEntity.setName(clusterNodeInput.getName()); + opsJdbcDbClusterNodeEntity.setIp(jdbcInfo.getIp()); + opsJdbcDbClusterNodeEntity.setPort(jdbcInfo.getPort()); + opsJdbcDbClusterNodeEntity.setUsername(clusterNodeInput.getUsername()); + opsJdbcDbClusterNodeEntity.setPassword(clusterNodeInput.getPassword()); + opsJdbcDbClusterNodeEntity.setUrl(url); + opsJdbcDbClusterNodeEntity.setUpdateTime(new Date()); + + updateById(opsJdbcDbClusterNodeEntity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void add(String clusterId, JdbcDbClusterNodeInputDto clusterNodeInput) { + OpsJdbcDbClusterEntity clusterEntity = opsJdbcDbClusterService.getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + + if (StrUtil.isEmpty(clusterNodeInput.getUsername())) { + throw new OpsException("Username can not be empty"); + } + + if (StrUtil.isEmpty(clusterNodeInput.getPassword())) { + throw new OpsException("password can not be empty"); + } + + String url = clusterNodeInput.getUrl(); + JdbcInfo jdbcInfo = JdbcUtil.parseUrl(url); + + OpsJdbcDbClusterNodeEntity clusterNodeEntity = getClusterNodeByIpAndPort(jdbcInfo.getIp(), + jdbcInfo.getPort(), clusterNodeInput.getUsername()); + if (Objects.nonNull(clusterNodeEntity)) { + throw new OpsException("Cluster node information already exists"); + } + + OpsJdbcDbClusterNodeEntity opsJdbcDbClusterNodeEntity = new OpsJdbcDbClusterNodeEntity(); + + opsJdbcDbClusterNodeEntity.setClusterId(clusterEntity.getClusterId()); + opsJdbcDbClusterNodeEntity.setName(clusterNodeInput.getName()); + opsJdbcDbClusterNodeEntity.setIp(jdbcInfo.getIp()); + opsJdbcDbClusterNodeEntity.setPort(jdbcInfo.getPort()); + opsJdbcDbClusterNodeEntity.setUsername(clusterNodeInput.getUsername()); + opsJdbcDbClusterNodeEntity.setPassword(clusterNodeInput.getPassword()); + opsJdbcDbClusterNodeEntity.setUrl(url); + opsJdbcDbClusterNodeEntity.setCreateTime(new Date()); + + save(opsJdbcDbClusterNodeEntity); + + if (Objects.nonNull(clusterNodeInput.getDeployType())) { + clusterEntity.setDeployType(clusterNodeInput.getDeployType()); + opsJdbcDbClusterService.updateById(clusterEntity); + } else { + throw new OpsException("deployment type is empty"); + } + } + + @Override + public boolean ping(JdbcDbClusterNodeInputDto clusterNodeInput) { + boolean res = false; + try (Connection connection = JdbcUtil.getConnection(clusterNodeInput.getUrl(), clusterNodeInput.getUsername(), + encryptionUtils.decrypt(clusterNodeInput.getPassword()))) { + if (Objects.nonNull(connection)) { + res = true; + } + } catch (Exception e) { + log.error("jdbc ping get link exception", e); + res = false; + } + return res; + } + + @Override + public Map monitor(String clusterNodeId, String businessId) { + Map res = new HashMap<>(); + res.put("res", true); + + OpsJdbcDbClusterNodeEntity clusterNodeEntity = getById(clusterNodeId); + if (Objects.isNull(clusterNodeEntity)) { + res.put("msg", "Node information not found"); + res.put("res", false); + return res; + } + + String clusterId = clusterNodeEntity.getClusterId(); + OpsJdbcDbClusterEntity clusterEntity = opsJdbcDbClusterService.getById(clusterId); + if (Objects.isNull(clusterEntity)) { + res.put("msg", "Cluster information not found"); + res.put("res", false); + return res; + } + + Connection connection = null; + try { + connection = JdbcUtil.getConnection(clusterNodeEntity.getUrl(), clusterNodeEntity.getUsername(), + encryptionUtils.decrypt(clusterNodeEntity.getPassword())); + if (Objects.isNull(connection) || connection.isClosed()) { + res.put("msg", "JDBC connection failed"); + res.put("res", false); + return res; + } + } catch (Exception e) { + res.put("msg", "JDBC connection failed"); + res.put("res", false); + return res; + } + + WsSession wsSession = wsConnectorManager.getSession(businessId).orElseThrow(() -> new OpsException("response session does not exist")); + + Connection finalConnection = connection; + Future future = threadPoolTaskExecutor.submit(() -> { + try { + doMonitor(wsSession, finalConnection, clusterEntity.getDbType()); + } finally { + if (Objects.nonNull(finalConnection)) { + try { + finalConnection.close(); + } catch (SQLException ignore) { + + } + } + + wsUtil.close(wsSession); + } + }); + TaskManager.registry(businessId, future); + return res; + } + + private void doMonitor(WsSession wsSession, Connection connection, DbTypeEnum dbType) { + while (wsSession.getSession().isOpen()) { + JdbcMonitorVO jdbcMonitorVO = new JdbcMonitorVO(); + + if (dbType == DbTypeEnum.MYSQL) { + doMonitorMysql(connection, jdbcMonitorVO); + } else if (dbType == DbTypeEnum.OPENGAUSS) { + doMonitorOpenGauss(connection, jdbcMonitorVO); + } else { + doMonitorPostgresql(connection, jdbcMonitorVO); + } + + wsUtil.sendText(wsSession, JSON.toJSONString(jdbcMonitorVO)); + + try { + TimeUnit.SECONDS.sleep(5L); + } catch (InterruptedException e) { + throw new OpsException("thread is interrupted"); + } + } + } + + private void doMonitorPostgresql(Connection connection, JdbcMonitorVO jdbcMonitorVO) { + doMonitorOpenGauss(connection, jdbcMonitorVO); + } + + private void doMonitorOpenGauss(Connection connection, JdbcMonitorVO jdbcMonitorVO) { + CountDownLatch countDownLatch = new CountDownLatch(3); + threadPoolTaskExecutor.submit(() -> { + try { + jdbcMonitorVO.setLockNum(opsClusterService.lock(connection)); + } finally { + countDownLatch.countDown(); + } + }); + + threadPoolTaskExecutor.submit(() -> { + try { + jdbcMonitorVO.setSessionNum(opsClusterService.session(connection)); + } finally { + countDownLatch.countDown(); + } + }); + + threadPoolTaskExecutor.submit(() -> { + try { + jdbcMonitorVO.setConnNum(opsClusterService.connectNum(connection)); + } finally { + countDownLatch.countDown(); + } + }); + + try { + countDownLatch.await(); + } catch (InterruptedException e) { + log.error("waiting for thread to be interrupted", e); + throw new OpsException("monitor error"); + } + } + + private void doMonitorMysql(Connection connection, JdbcMonitorVO jdbcMonitorVO) { + CountDownLatch countDownLatch = new CountDownLatch(6); + + threadPoolTaskExecutor.submit(() -> { + try { + jdbcMonitorVO.setRole(role(connection)); + } finally { + countDownLatch.countDown(); + } + }); + + threadPoolTaskExecutor.submit(() -> { + try { + jdbcMonitorVO.setConnNum(connNum(connection)); + } finally { + countDownLatch.countDown(); + } + }); + + threadPoolTaskExecutor.submit(() -> { + try { + jdbcMonitorVO.setQps(qps(connection)); + } finally { + countDownLatch.countDown(); + } + }); + + threadPoolTaskExecutor.submit(() -> { + try { + jdbcMonitorVO.setTps(tps(connection)); + } finally { + countDownLatch.countDown(); + } + }); + + threadPoolTaskExecutor.submit(() -> { + try { + jdbcMonitorVO.setMemoryUsed(memoryUsed(connection)); + } finally { + countDownLatch.countDown(); + } + }); + + threadPoolTaskExecutor.submit(() -> { + try { + jdbcMonitorVO.setTableSpaceUsed(tableSpaceUsed(connection)); + } finally { + countDownLatch.countDown(); + } + }); + + try { + countDownLatch.await(); + } catch (InterruptedException e) { + log.error("waiting for thread to be interrupted", e); + throw new OpsException("monitor error"); + } + } + + private String tableSpaceUsed(Connection connection) { + String sql = "SELECT SUM( table_schema_size.table_schema_size ) AS 'tableSpaceUsed' FROM ( SELECT table_schema, SUM( data_length + index_length ) AS table_schema_size FROM information_schema.TABLES GROUP BY table_schema ) table_schema_size"; + try (PreparedStatement preparedStatement = connection.prepareStatement(sql); + ResultSet resultSet = preparedStatement.executeQuery()) { + + if (resultSet.next()) { + return resultSet.getString("tableSpaceUsed"); + } + } catch (Exception e) { + log.error("Failed to get tablespace", e); + } + + throw new OpsException("Failed to get tablespace"); + } + + private String memoryUsed(Connection connection) { + String sql = "SELECT (@@key_buffer_size + @@innodb_buffer_pool_size + @@innodb_log_buffer_size + @@max_connections * ( @@read_buffer_size + @@read_rnd_buffer_size + @@sort_buffer_size + @@join_buffer_size + @@binlog_cache_size + @@thread_stack + @@tmp_table_size )) AS 'memoryUsed'"; + + try (PreparedStatement preparedStatement = connection.prepareStatement(sql); + ResultSet resultSet = preparedStatement.executeQuery()) { + + if (resultSet.next()) { + return resultSet.getString("memoryUsed"); + } + } catch (Exception e) { + log.error("Failed to get memoryUsed", e); + } + + throw new OpsException("Failed to get memoryUsed"); + } + + /** + * TPS = (Com_commit+Com_rollback)/Uptime + * + * @param connection + * @return + */ + private String tps(Connection connection) { + String commitSql = "show global status like 'Com_commit'"; + String rollbackSql = "show global status like 'Com_rollback'"; + String uptimeSql = "show global status like 'Uptime'"; + + try (PreparedStatement commitPreparedStatement = connection.prepareStatement(commitSql); + PreparedStatement rollbackPreparedStatement = connection.prepareStatement(rollbackSql); + PreparedStatement uptimePreparedStatement = connection.prepareStatement(uptimeSql); + ResultSet commitResultSet = commitPreparedStatement.executeQuery(); + ResultSet rollbackResultSet = rollbackPreparedStatement.executeQuery(); + ResultSet uptimeResultSet = uptimePreparedStatement.executeQuery()) { + + if (commitResultSet.next() && rollbackResultSet.next() && uptimeResultSet.next()) { + long commit = commitResultSet.getLong("Value"); + long rollback = rollbackResultSet.getLong("Value"); + long uptime = uptimeResultSet.getLong("Value"); + + return Long.valueOf((commit + rollback) / uptime).toString(); + } + } catch (Exception e) { + log.error("Failed to get tps", e); + } + throw new OpsException("Failed to get tps"); + } + + /** + * QPS = Questions/Uptime + * + * @param connection + * @return + */ + private String qps(Connection connection) { + String questionsSql = "show global status like 'Questions'"; + String uptimeSql = "show global status like 'Uptime'"; + + try (PreparedStatement questionsPreparedStatement = connection.prepareStatement(questionsSql); + PreparedStatement uptimePreparedStatement = connection.prepareStatement(uptimeSql); + ResultSet questionsResultSet = questionsPreparedStatement.executeQuery(); + ResultSet uptimeResultSet = uptimePreparedStatement.executeQuery()) { + + if (questionsResultSet.next() && uptimeResultSet.next()) { + long questions = questionsResultSet.getLong("Value"); + long uptime = uptimeResultSet.getLong("Value"); + + return Long.valueOf(questions / uptime).toString(); + } + } catch (Exception e) { + log.error("Failed to get qps", e); + } + throw new OpsException("Failed to get qps"); + } + + private String connNum(Connection connection) { + String sql = "SHOW STATUS LIKE 'Threads_connected'"; + try (PreparedStatement preparedStatement = connection.prepareStatement(sql); + ResultSet resultSet = preparedStatement.executeQuery()) { + + if (resultSet.next()) { + return resultSet.getString("Value"); + } + } catch (Exception e) { + log.error("Failed to get connection number", e); + } + + throw new OpsException("Failed to get connection number"); + } + + private ClusterRoleEnum role(Connection connection) { + String sql = "SHOW SLAVE STATUS"; + try (PreparedStatement preparedStatement = connection.prepareStatement(sql); + ResultSet resultSet = preparedStatement.executeQuery()) { + + if (resultSet.next()) { + return ClusterRoleEnum.SLAVE; + } else { + return ClusterRoleEnum.MASTER; + } + } catch (Exception e) { + log.error("Failed to get role", e); + } + + throw new OpsException("Failed to get role"); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsJdbcDbClusterServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsJdbcDbClusterServiceImpl.java new file mode 100644 index 000000000..103a41c7a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsJdbcDbClusterServiceImpl.java @@ -0,0 +1,622 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsJdbcDbClusterServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/OpsJdbcDbClusterServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.opengauss.admin.common.core.domain.entity.ops.OpsJdbcDbClusterEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsJdbcDbClusterNodeEntity; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.*; +import org.opengauss.admin.common.enums.ops.DbTypeEnum; +import org.opengauss.admin.common.enums.ops.DeployTypeEnum; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.common.utils.ops.JdbcUtil; +import org.opengauss.admin.system.mapper.ops.OpsJdbcDbClusterMapper; +import org.opengauss.admin.system.service.ops.IHostService; +import org.opengauss.admin.system.service.ops.IOpsJdbcDbClusterNodeService; +import org.opengauss.admin.system.service.ops.IOpsJdbcDbClusterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author lhf + * @date 2023/1/13 11:08 + **/ +@Service +public class OpsJdbcDbClusterServiceImpl extends ServiceImpl implements IOpsJdbcDbClusterService { + @Autowired + @Lazy + private IOpsJdbcDbClusterNodeService opsJdbcDbClusterNodeService; + @Autowired + private IHostService hostService; + @Autowired + private EncryptionUtils encryptionUtils; + + @Override + @Transactional(rollbackFor = Exception.class) + public void add(JdbcDbClusterInputDto clusterInput) { + OpsJdbcDbClusterEntity clusterEntity = saveCluster(clusterInput); + saveClusterNode(clusterEntity, clusterInput); + } + + @Override + public Page page(String name, String ip, String type, Page page) { + Set clusterIdConditions = new HashSet<>(); + + Set nameSet = null; + Set ipSet = null; + Set typeSet = null; + boolean condition = false; + if (StrUtil.isNotEmpty(name)) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsJdbcDbClusterEntity.class) + .like(OpsJdbcDbClusterEntity::getName, name) + .select(OpsJdbcDbClusterEntity::getClusterId); + + List clusterEntityList = list(queryWrapper); + if (CollUtil.isNotEmpty(clusterEntityList)) { + nameSet = (clusterEntityList.stream().map(OpsJdbcDbClusterEntity::getClusterId).collect(Collectors.toSet())); + } + + if (CollUtil.isEmpty(nameSet)) { + return Page.of(0, 0); + } + } + + if (StrUtil.isNotEmpty(ip)) { + condition = true; + Set ipFuzzyQueryClusterIds = opsJdbcDbClusterNodeService.fuzzyQueryClusterIdsByIp(ip); + + ipSet = (ipFuzzyQueryClusterIds); + + if (CollUtil.isEmpty(ipSet)) { + return Page.of(0, 0); + } + } + + if (StrUtil.isNotEmpty(type)) { + condition = true; + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsJdbcDbClusterEntity.class) + .like(OpsJdbcDbClusterEntity::getDbType, type.toUpperCase()) + .select(OpsJdbcDbClusterEntity::getClusterId); + + List clusterEntityList = list(queryWrapper); + if (CollUtil.isNotEmpty(clusterEntityList)) { + typeSet = (clusterEntityList.stream().map(OpsJdbcDbClusterEntity::getClusterId).collect(Collectors.toSet())); + } + + if (CollUtil.isEmpty(typeSet)) { + return Page.of(0, 0); + } + } + + Set retainAll = retainAll(nameSet, ipSet, typeSet); + clusterIdConditions.addAll(retainAll); + + if (condition && CollUtil.isEmpty(clusterIdConditions)) { + return Page.of(0, 0); + } + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsJdbcDbClusterEntity.class) + .in(CollUtil.isNotEmpty(clusterIdConditions), OpsJdbcDbClusterEntity::getClusterId, clusterIdConditions) + .orderByDesc(OpsJdbcDbClusterEntity::getCreateTime); + + Page searchPage = page(page, queryWrapper); + + Page resPage = new Page<>(); + resPage.setTotal(searchPage.getTotal()); + resPage.setRecords(buildPageRecords(searchPage.getRecords())); + return resPage; + } + + private Set retainAll(Set nameSet, Set ipSet, Set typeSet) { + if (nameSet != null) { + if (ipSet != null) { + nameSet.retainAll(ipSet); + } + + if (typeSet != null) { + nameSet.retainAll(typeSet); + } + + return nameSet; + } + + if (ipSet != null) { + if (nameSet != null) { + ipSet.retainAll(nameSet); + } + if (typeSet != null) { + ipSet.retainAll(typeSet); + } + + return ipSet; + } + + if (typeSet != null) { + if (nameSet != null) { + typeSet.retainAll(nameSet); + } + + if (ipSet != null) { + typeSet.retainAll(ipSet); + } + + return typeSet; + } + + return Collections.emptySet(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void del(String clusterId) { + OpsJdbcDbClusterEntity clusterEntity = getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + opsJdbcDbClusterNodeService.delByClusterId(clusterId); + removeById(clusterId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(String clusterId, JdbcDbClusterInputDto clusterInput) { + OpsJdbcDbClusterEntity clusterEntity = getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + + del(clusterId); + add(clusterInput); + } + + @Override + public JdbcDbClusterImportAnalysisVO importAnalysis(MultipartFile file) { + List jdbcDbClusterInputDto = null; + try { + jdbcDbClusterInputDto = parseCSV(file); + } catch (Exception e) { + String msg = "The content of the file is malformed"; + + if (e instanceof OpsException) { + msg = msg + " : " + e.getMessage(); + } + + log.error(msg, e); + throw new OpsException(msg); + } + + List jdbcDbClusterInputDtos = analysisWrongNodes(jdbcDbClusterInputDto); + return JdbcDbClusterImportAnalysisVO.of(jdbcDbClusterInputDto, jdbcDbClusterInputDtos); + } + + private List analysisWrongNodes(List jdbcDbClusterInputDto) { + List wrongClusters = new ArrayList<>(); + + for (JdbcDbClusterInputDto dbClusterInputDto : jdbcDbClusterInputDto) { + boolean clusterWrong = false; + DeployTypeEnum deployType = dbClusterInputDto.getDeployType(); + if (Objects.isNull(deployType)) { + if (clusterWrong) { + dbClusterInputDto.setRemark(dbClusterInputDto.getRemark() + ",deployType not null"); + } else { + dbClusterInputDto.setRemark("deployType not null"); + } + clusterWrong = true; + } + + List nodes = dbClusterInputDto.getNodes(); + if (CollUtil.isEmpty(nodes)) { + if (clusterWrong) { + dbClusterInputDto.setRemark(dbClusterInputDto.getRemark() + ",Cluster node cannot be empty"); + } else { + dbClusterInputDto.setRemark("Cluster node cannot be empty"); + } + clusterWrong = true; + } + + for (JdbcDbClusterNodeInputDto node : nodes) { + boolean nodeWrong = false; +// String name = node.getName(); +// if (StrUtil.isEmpty(name)) { +// if (nodeWrong) { +// node.setRemark(node.getRemark() + ",Node name cannot be empty"); +// } else { +// node.setRemark("Node name cannot be empty"); +// } +// +// nodeWrong = true; +// } + + String url = node.getUrl(); + try { + JdbcInfo jdbcInfo = JdbcUtil.parseUrl(url); + if (Objects.isNull(jdbcInfo)) { + throw new OpsException("parsing url failed"); + } + + if (Objects.isNull(jdbcInfo.getDbType())) { + throw new OpsException("wrong database type"); + } + + if (StrUtil.isEmpty(jdbcInfo.getIp())) { + throw new OpsException("wrong ip"); + } + + if (StrUtil.isEmpty(jdbcInfo.getPort())) { + throw new OpsException("wrong port"); + } + + OpsJdbcDbClusterNodeEntity clusterNodeByIpAndPort = opsJdbcDbClusterNodeService + .getClusterNodeByIpAndPort(jdbcInfo.getIp(), jdbcInfo.getPort(), node.getUsername()); + if (Objects.nonNull(clusterNodeByIpAndPort)) { + throw new OpsException("The current instance node already exists in other clusters"); + } + } catch (Exception e) { + String msg = ""; + if (e instanceof OpsException) { + msg = e.getMessage(); + } else { + msg = "parsing url error"; + } + if (nodeWrong) { + node.setRemark(node.getRemark() + "," + msg); + } else { + node.setRemark(msg); + } + nodeWrong = true; + } + + String username = node.getUsername(); + if (StrUtil.isEmpty(username)) { + if (nodeWrong) { + node.setRemark(node.getRemark() + ",username is empty"); + } else { + node.setRemark("username is empty"); + } + nodeWrong = true; + } + + String password = node.getPassword(); + if (StrUtil.isEmpty(password)) { + if (nodeWrong) { + node.setRemark(node.getRemark() + ",password is empty"); + } else { + node.setRemark("password is empty"); + } + nodeWrong = true; + } + + clusterWrong |= nodeWrong; + } + + if (clusterWrong) { + wrongClusters.add(dbClusterInputDto); + } + } + + return wrongClusters; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void importCluster(MultipartFile file) { + List jdbcDbClusterInputDto = null; + try { + jdbcDbClusterInputDto = parseCSV(file); + } catch (Exception e) { + log.error("The content of the file is malformed", e); + throw new OpsException("The content of the file is malformed"); + } + + List wrongClusters = analysisWrongNodes(jdbcDbClusterInputDto); + if (CollUtil.isNotEmpty(wrongClusters)) { + throw new OpsException("There are " + wrongClusters.size() + " errors in the file content, please re-import after processing"); + } + + for (JdbcDbClusterInputDto dbClusterInputDto : jdbcDbClusterInputDto) { + add(dbClusterInputDto); + } + } + + @Override + public List listAll() { + List dbClusterList = list(); + return buildPageRecords(dbClusterList); + } + + @Override + public List listByType(String type) { + if (StrUtil.isEmpty(type)) { + throw new OpsException("type parameter does not exist"); + } + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsJdbcDbClusterEntity.class) + .eq(OpsJdbcDbClusterEntity::getDbType, type.toUpperCase()); + + List dbClusterList = list(queryWrapper); + return buildPageRecords(dbClusterList); + } + + @Data + @AllArgsConstructor + class Cluster { + private String name; + private String deployType; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Cluster cluster = (Cluster) o; + + return name.equals(cluster.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + } + + @Data + @AllArgsConstructor + class ClusterNode { + private String name; + private String url; + private String username; + private String password; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ClusterNode that = (ClusterNode) o; + + return url != null ? url.equals(that.url) : that.url == null; + } + + @Override + public int hashCode() { + return url != null ? url.hashCode() : 0; + } + } + + private List parseCSV(MultipartFile file) { + List clusterList = new ArrayList<>(); + Map> clusterListMap = new HashMap<>(); + Integer lineNum = 0; + try (InputStream inputStream = file.getInputStream(); final InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { + String line = null; + while ((line = bufferedReader.readLine()) != null) { + if (0 == lineNum++) { + continue; + } + + String[] col = line.split(","); + if (col.length != 4) { + throw new OpsException("The data in row " + lineNum + " is wrong"); + } else { + if (StrUtil.isEmpty(col[0].trim()) && StrUtil.isEmpty(col[1].trim()) && StrUtil.isEmpty(col[2].trim()) && StrUtil.isEmpty(col[3].trim())) { + continue; + } + + Cluster thatCluster = new Cluster(col[0], null); + ClusterNode thatClusterNode = new ClusterNode(null, col[1], col[2], col[3]); + + Set clusterNodes = clusterListMap.get(thatCluster); + if (Objects.isNull(clusterNodes)) { + clusterNodes = new HashSet<>(); + clusterListMap.put(thatCluster, clusterNodes); + } + + clusterNodes.add(thatClusterNode); + } + } + } catch (IOException e) { + throw new OpsException("Failed to read zone file contents"); + } + + if (CollUtil.isNotEmpty(clusterListMap)) { + clusterListMap.forEach((cluster, nodes) -> { + List clusterNodes = new ArrayList<>(); + for (ClusterNode node : nodes) { + clusterNodes.add(JdbcDbClusterNodeInputDto.of(node.getName(), node.getUrl(), node.getUsername(), node.getPassword())); + } + + JdbcDbClusterInputDto inputDto = JdbcDbClusterInputDto.of(cluster.getName(), DeployTypeEnum.nameOf(cluster.getDeployType())); + inputDto.setNodes(clusterNodes); + clusterList.add(inputDto); + }); + } + + if (CollUtil.isEmpty(clusterList)) { + throw new OpsException("The cluster content read from the file is empty"); + } + + for (JdbcDbClusterInputDto inputDto : clusterList) { + DeployTypeEnum deployType = inputDto.getDeployType(); + if (Objects.isNull(deployType)) { + List nodes = inputDto.getNodes(); + if (CollUtil.isEmpty(nodes) || nodes.size() < 2) { + inputDto.setDeployType(DeployTypeEnum.SINGLE_NODE); + } else { + inputDto.setDeployType(DeployTypeEnum.CLUSTER); + } + } + } + return clusterList; + } + + private List buildPageRecords(List records) { + if (CollUtil.isEmpty(records)) { + return Collections.emptyList(); + } + List res = new ArrayList<>(); + Set clusterIds = records.stream().map(OpsJdbcDbClusterEntity::getClusterId).collect(Collectors.toSet()); + Map> clusterNodeMap = opsJdbcDbClusterNodeService.mapClusterNodesByClusterId(clusterIds); + final Set ipSet = clusterNodeMap.values().stream().flatMap(val -> val.stream()).map(OpsJdbcDbClusterNodeEntity::getIp).collect(Collectors.toSet()); + Map ipOsMap = hostService.mapOsByIps(ipSet); + for (OpsJdbcDbClusterEntity record : records) { + List nodes = new ArrayList<>(); + JdbcDbClusterVO jdbcDbClusterVO = JdbcDbClusterVO.of(record, nodes); + String clusterId = record.getClusterId(); + List clusterNodeEntityList = clusterNodeMap.get(clusterId); + if (CollUtil.isNotEmpty(clusterNodeEntityList)) { + Connection conn = null; + for (OpsJdbcDbClusterNodeEntity clusterNodeEntity : clusterNodeEntityList) { + nodes.add(JdbcDbClusterNodeVO.of(clusterNodeEntity, ipOsMap.get(clusterNodeEntity.getIp()))); + try { + conn = JdbcUtil.getConnection(clusterNodeEntity.getUrl(), clusterNodeEntity.getUsername(), + encryptionUtils.decrypt(clusterNodeEntity.getPassword())); + } catch (OpsException e) { + log.error("opsException: ", e); + } + jdbcDbClusterVO.setVersionNum(getDbVersion(conn)); + } + } + res.add(jdbcDbClusterVO); + } + return res; + } + + private String getDbVersion(Connection connection) { + if (connection == null) { + return ""; + } + String sql = "select version()"; + String version = ""; + try (PreparedStatement preparedStatement = connection.prepareStatement(sql); + ResultSet resultSet = preparedStatement.executeQuery()) { + if (resultSet.next()) { + version = resultSet.getString(1); + return version; + } + } catch (SQLException e) { + log.error("query version fail", e); + throw new OpsException("query version fail"); + } + return version; + } + + private void saveClusterNode(OpsJdbcDbClusterEntity clusterEntity, JdbcDbClusterInputDto clusterInput) { + List nodes = clusterInput.getNodes(); + if (CollUtil.isEmpty(nodes)) { + throw new OpsException("Cluster node information does not exist"); + } + + Date now = new Date(); + List clusterNodeEntityList = new ArrayList<>(); + for (JdbcDbClusterNodeInputDto node : nodes) { + String url = node.getUrl(); + JdbcInfo jdbcInfo = JdbcUtil.parseUrl(url); + + OpsJdbcDbClusterNodeEntity clusterNodeEntity = opsJdbcDbClusterNodeService + .getClusterNodeByIpAndPort(jdbcInfo.getIp(), jdbcInfo.getPort(), node.getUsername()); + if (Objects.nonNull(clusterNodeEntity)) { + throw new OpsException("Cluster node information already exists"); + } + + OpsJdbcDbClusterNodeEntity opsJdbcDbClusterNodeEntity = new OpsJdbcDbClusterNodeEntity(); + + opsJdbcDbClusterNodeEntity.setClusterId(clusterEntity.getClusterId()); + opsJdbcDbClusterNodeEntity.setName(node.getName()); + opsJdbcDbClusterNodeEntity.setIp(jdbcInfo.getIp()); + opsJdbcDbClusterNodeEntity.setPort(jdbcInfo.getPort()); + opsJdbcDbClusterNodeEntity.setUsername(node.getUsername()); + opsJdbcDbClusterNodeEntity.setPassword(node.getPassword()); + opsJdbcDbClusterNodeEntity.setUrl(url); + opsJdbcDbClusterNodeEntity.setCreateTime(now); + opsJdbcDbClusterNodeEntity.setUpdateTime(now); + opsJdbcDbClusterNodeEntity.setRemark(node.getRemark()); + + clusterNodeEntityList.add(opsJdbcDbClusterNodeEntity); + } + + if (CollUtil.isNotEmpty(clusterNodeEntityList)) { + opsJdbcDbClusterNodeService.saveBatch(clusterNodeEntityList); + } else { + throw new OpsException("Cluster node information does not exist"); + } + } + + private OpsJdbcDbClusterEntity saveCluster(JdbcDbClusterInputDto clusterInput) { + List nodes = clusterInput.getNodes(); + if (CollUtil.isEmpty(nodes)) { + throw new OpsException("Cluster node information does not exist"); + } + if (StringUtils.isEmpty(clusterInput.getClusterName())) { + throw new OpsException("Cluster name cannot be empty"); + } + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(OpsJdbcDbClusterEntity::getName, clusterInput.getClusterName()); + List clusterEntityList = list(queryWrapper); + if (CollUtil.isNotEmpty(clusterEntityList)) { + throw new OpsException("Cluster name already exists"); + } + + JdbcDbClusterNodeInputDto firstNode = nodes.get(0); + String url = firstNode.getUrl(); + JdbcInfo jdbcInfo = JdbcUtil.parseUrl(url); + DbTypeEnum dbType = jdbcInfo.getDbType(); + + + OpsJdbcDbClusterEntity clusterEntity = new OpsJdbcDbClusterEntity(); + clusterEntity.setName(clusterInput.getClusterName()); + clusterEntity.setDeployType(clusterInput.getDeployType()); + clusterEntity.setDbType(dbType); + + Date now = new Date(); + clusterEntity.setCreateTime(now); + clusterEntity.setUpdateTime(now); + + save(clusterEntity); + return clusterEntity; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/WsServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/WsServiceImpl.java new file mode 100644 index 000000000..c33794485 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/WsServiceImpl.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WsServiceImpl.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-service/src/main/java/org/opengauss/admin/system/service/ops/impl/WsServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.system.service.ops.impl; + +import com.jcraft.jsch.ChannelShell; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.model.ops.WsSession; +import org.opengauss.admin.common.core.handler.ops.cache.SSHChannelManager; +import org.opengauss.admin.common.core.handler.ops.cache.TaskManager; +import org.opengauss.admin.common.core.handler.ops.cache.WsConnectorManager; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.ops.JschUtil; +import org.opengauss.admin.system.service.ops.IWsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import jakarta.websocket.Session; +import java.io.IOException; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author lhf + * @date 2022/8/6 18:14 + **/ +@Slf4j +@Service +public class WsServiceImpl implements IWsService { + @Autowired + private JschUtil jschUtil; + @Autowired + private WsConnectorManager wsConnectorManager; + + @Override + public void onOpen(String businessId, Session session) { + wsConnectorManager.register(businessId, new WsSession(session, businessId)); + } + + @Override + public void onClose(String businessId) { + wsConnectorManager.getSession(businessId).ifPresent(wsSession -> { + try { + wsSession.getSession().close(); + } catch (IOException e) { + log.error("websocket session close fail", e); + } + }); + TaskManager.remove(businessId).ifPresent(future -> future.cancel(true)); + wsConnectorManager.remove(businessId); + } + + @Override + public void onMessage(String businessId, String message) { + ChannelShell channelShell = SSHChannelManager.getChannelShell(businessId).orElseThrow(() -> new OpsException("No connection information found")); + jschUtil.sendToChannelShell(channelShell, message); + } + + @Override + public void sendMessage(Session session, byte[] message) { + try { + session.getBasicRemote().sendText(new String(message)); + } catch (IOException e) { + log.error("Failed to send ws message", e); + } + } + + @Override + public WsSession getRetWsSession(String businessId) { + return wsConnectorManager + .getSession(businessId) + .orElseThrow(() -> new OpsException("response session does not exist")); + } + + @Override + public Optional obtainFreshSession(WsSession wsSession) { + if (wsSession.getSession().isOpen()){ + return Optional.ofNullable(wsSession.getSession()); + } + + AtomicReference session = new AtomicReference<>(); + wsConnectorManager.getSession(wsSession.getBusinessId()).ifPresent(wsSession1 -> session.set(wsSession1.getSession())); + return Optional.ofNullable(session.get()); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/utils/OnlinePluginloadUtil.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/utils/OnlinePluginloadUtil.java new file mode 100644 index 000000000..fb60d1242 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/utils/OnlinePluginloadUtil.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OnlinePluginloadUtil.java + * + * IDENTIFICATION + *openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/utils/OnlinePluginloadUtil.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.system.utils; + +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.config.SystemConfig; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.opengauss.admin.common.utils.ops.WsUtil; +import org.opengauss.admin.common.core.domain.model.ops.WsSession; +import org.opengauss.admin.system.plugin.facade.SysSettingFacade; + + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.util.Objects; + +/** + * OnlinePluginloadUtil + * + * @author zhaochen + * @since 2025-03-20 + * @version 7.0.0-RC1 + */ +@Slf4j +@Component +public class OnlinePluginloadUtil { + /** + * WsUtil + */ + @Autowired + private WsUtil wsUtil; + + /** + * SysSettingFacade + */ + @Autowired + private SysSettingFacade sysSettingFacade; + + /** + * download plugin + * + * @param resourceUrl resourceUrl + * @param fileName fileName + * @param wsSession wsSession + */ + public void download(String resourceUrl, String fileName, WsSession wsSession) { + DownloadMonitor downloadMonitor = new DownloadMonitor(); + InputStream inputStream = null; + OutputStream outputStream = null; + String targetPath = SystemConfig.getPluginPath(); + try { + File existFile = new File(targetPath + File.separator + fileName); + if (existFile.exists()) { + wsUtil.sendText(wsSession, "1"); + return; + } + File file = new File(targetPath); + if (!file.exists()) { + if (!file.mkdirs()) { + throw new OpsException("Failed to create a directory"); + } + } + URL url = new URL(resourceUrl); + URLConnection urlConnection = openConnectionWithProxy(url); + long max = urlConnection.getContentLengthLong(); + + downloadMonitor.init(max, 0, wsSession); + + inputStream = urlConnection.getInputStream(); + byte[] bs = new byte[10240]; + int len; + outputStream = new FileOutputStream(targetPath + "/" + fileName, false); + + while ((len = inputStream.read(bs)) != -1) { + outputStream.write(bs, 0, len); + downloadMonitor.count(len); + } + } catch (Exception e) { + log.error("File download Failed", e); + wsUtil.sendText(wsSession, "File download Failed"); + } finally { + downloadMonitor.end(); + if (Objects.nonNull(outputStream)) { + try { + outputStream.close(); + } catch (IOException e) { + log.error("Failed to close the output stream. Procedure"); + } + } + if (Objects.nonNull(inputStream)) { + try { + inputStream.close(); + } catch (IOException e) { + log.error("Failed to close the output stream. Procedure"); + } + } + } + } + + private URLConnection openConnectionWithProxy(URL url) throws IOException { + Proxy proxy = sysSettingFacade.getSysNetProxy(); + if (Objects.isNull(proxy)) { + return url.openConnection(); + } else { + return url.openConnection(proxy); + } + } + class DownloadMonitor { + private long max; + private long count; + private long percent; + private WsSession wsSession; + + public void init(long max, long count, WsSession wsSession) { + this.max = max; + this.count = count; + this.percent = 0; + this.wsSession = wsSession; + } + + /** + * download monitor + * + * @param bytes byte + */ + public void count(long bytes) { + count += bytes; + long currentPercent = count * 100 / max; + if (currentPercent > percent) { + this.percent = currentPercent; + if (currentPercent % 10 == 0) { + log.info("========{}========", this.percent + "%"); + } + wsUtil.sendText(wsSession, this.percent / 100.00 + ""); + } + } + + /** + * end + */ + public void end() { + wsUtil.sendText(wsSession, "DOWNLOAD_FINISH"); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/utils/SmartStopWatchFormatter.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/utils/SmartStopWatchFormatter.java new file mode 100644 index 000000000..397a5239e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/admin/system/utils/SmartStopWatchFormatter.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.system.utils; + +import org.springframework.util.StopWatch; + +/** + * SmartStopWatchFormatter + * + * @author: wangchao + * @Date: 2025/8/1 15:23 + * @since 7.0.0-RC2 + **/ +public class SmartStopWatchFormatter { + /** + * formart stopwatch print + * + * @param stopWatch stopwatch + * @return format result + */ + public static String format(StopWatch stopWatch) { + if (stopWatch.getTotalTimeSeconds() < 0.1) { + return formatMillis(stopWatch); + } + return formatSeconds(stopWatch); + } + + private static String formatSeconds(StopWatch stopWatch) { + StringBuilder sb = new StringBuilder("\n" + stopWatch.getId()).append(" stopwatch\n") + .append("-----------------------------------------\n") + .append(String.format("%-25s %-12s %s%n", "Task Name", "Time (s)", "% of Total")); + double totalSeconds = stopWatch.getTotalTimeSeconds(); + for (StopWatch.TaskInfo task : stopWatch.getTaskInfo()) { + double taskSeconds = task.getTimeSeconds(); + double percentage = totalSeconds > 0 ? (taskSeconds / totalSeconds) * 100 : 0; + sb.append(String.format("%-25s %-12.3f %.1f%%%n", task.getTaskName(), taskSeconds, percentage)); + } + sb.append("-----------------------------------------\n") + .append(String.format("Total time: %.3f seconds%n", totalSeconds)); + return sb.toString(); + } + + private static String formatMillis(StopWatch stopWatch) { + StringBuilder sb = new StringBuilder("\n" + stopWatch.getId()).append(" stopwatch\n") + .append("-----------------------------------------\n") + .append(String.format("%-25s %-12s %s%n", "Task Name", "Time (ms)", "% of Total")); + double totalMillis = stopWatch.getTotalTimeMillis(); + for (StopWatch.TaskInfo task : stopWatch.getTaskInfo()) { + double taskMillis = task.getTimeMillis(); + double percentage = totalMillis > 0 ? (taskMillis / totalMillis) * 100 : 0; + sb.append(String.format("%-25s %-12.0f %.1f%%%n", task.getTaskName(), taskMillis, percentage)); + } + sb.append("-----------------------------------------\n") + .append(String.format("Total time: %.0f ms%n", totalMillis)); + return sb.toString(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/ClusterOriginal.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/ClusterOriginal.java new file mode 100644 index 000000000..60e40530c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/ClusterOriginal.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.data; + +import lombok.Getter; + +import org.opengauss.admin.common.core.domain.model.agent.AgentClusterVo; +import org.springframework.jdbc.core.BeanPropertyRowMapper; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; + +/** + *

+ * ClusterOriginal enum  implements TaskClusterConfigLoader interface,
+ * The original cluster information is obtained from the database, loading by : ops_cluster, ops_jdbc_cluster.
+ * 
+ * + * @author: wangchao + * @Date: 2025/3/18 16:17 + * @Description: ClusterOriginal + * @since 7.0.0-RC2 + **/ +@Getter +public enum ClusterOriginal implements TaskClusterConfigLoader { + /** + * ops_cluster: load cluster information from ops_cluster table. + */ + OPS_CLUSTER("ops_cluster") { + final String sql = + "select c.cluster_id clusterId,n.cluster_node_id clusterNodeId,'OPENGAUSS' dataBaseType,h.public_ip hostIp," + + " c.port port, c.database_username username,c.database_password dbPassword " + + " from ops_cluster c left join ops_cluster_node n on c.cluster_id=n.cluster_id " + + " left join ops_host h on n.host_id=h.host_id where n.cluster_node_id=?"; + + @Override + public AgentClusterVo load(String clusterNodeId) { + BeanPropertyRowMapper rowMapper = new BeanPropertyRowMapper<>(AgentClusterVo.class); + return jdbcTemplate.queryForObject(sql, rowMapper, clusterNodeId); + } + }, + /** + * ops_jdbc_cluster: load cluster information from ops_jdbcdb_cluster table. + */ + OPS_JDBC_CLUSTER("ops_jdbc_cluster") { + final String sql = + "select c.cluster_id clusterId,n.cluster_node_id clusterNodeId ,c.db_type dataBaseType,n.ip hostIp," + + " n.port port, n.username username,n.password dbPassword,n.url url " + + " from ops_jdbcdb_cluster c left join ops_jdbcdb_cluster_node n on c.cluster_id= n.cluster_id " + + " where n.cluster_node_id=?;"; + + @Override + public AgentClusterVo load(String clusterNodeId) { + BeanPropertyRowMapper rowMapper = new BeanPropertyRowMapper<>(AgentClusterVo.class); + return jdbcTemplate.queryForObject(sql, rowMapper, clusterNodeId); + } + }; + + private final String value; + private static JdbcTemplate jdbcTemplate; + + /** + * constructor + * + * @param value cluster type + */ + ClusterOriginal(String value) { + this.value = value; + } + + @Component + static class ClusterServiceHolder { + /** + * Set jdbcTemplate + * + * @param jdbcTemplate JdbcTemplate + */ + @Resource + public void setMappers(JdbcTemplate jdbcTemplate) { + ClusterOriginal.jdbcTemplate = jdbcTemplate; + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/CustomEventConfig.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/CustomEventConfig.java new file mode 100644 index 000000000..7329eb518 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/CustomEventConfig.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.data; + +import lombok.Builder; +import lombok.Data; + +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskConfig; + +/** + * CustomEventConfig + * + * @author: wangchao + * @Date: 2025/5/6 17:31 + * @Description: CustomMetricData + * @since 7.0.0-RC2 + **/ +@Data +@Builder +public class CustomEventConfig { + private Long taskId; + private Long agentId; + private AgentTaskConfig taskConfig; + + /** + * simple info of task config + * + * @return String + */ + public String info() { + return "[taskId=" + taskId + ", agentId=" + agentId + ", metric=" + taskConfig.getCollectorMetricDetails() + .size() + ":" + taskConfig.getMetricsDefinitionList().size() + "]"; + } + + /** + * detail info of task config + * + * @return String + */ + public String detail() { + return "[taskId=" + taskId + ", agentId=" + agentId + ", metrics=" + taskConfig.getCollectorMetricDetails() + + "]"; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/CustomMetricData.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/CustomMetricData.java new file mode 100644 index 000000000..f0af06e13 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/CustomMetricData.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.data; + +import lombok.Data; + +import java.util.List; + +/** + * CustomMetricData + * + * @author: wangchao + * @Date: 2025/5/6 17:31 + * @Description: CustomMetricData + * @since 7.0.0-RC2 + **/ +@Data +public class CustomMetricData { + private String name; + private String description; + private String unit; + private String type; + private List points; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/CustomMetricDataPoint.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/CustomMetricDataPoint.java new file mode 100644 index 000000000..d9319e20e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/CustomMetricDataPoint.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.data; + +import lombok.Data; + +import java.time.Instant; +import java.util.Map; + +/** + * CustomMetricDataPoint + * + * @author: wangchao + * @Date: 2025/5/6 17:31 + * @Description: CustomMetricData + * @since 7.0.0-RC2 + **/ +@Data +public class CustomMetricDataPoint { + private String value; + private Instant epochNanos; + private Instant startEpochNanos; + private Map attributes; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/TaskClusterConfigLoader.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/TaskClusterConfigLoader.java new file mode 100644 index 000000000..c4379585d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/data/TaskClusterConfigLoader.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.data; + +import org.opengauss.admin.common.core.domain.model.agent.AgentClusterVo; + +/** + * TaskClusterConfigLoader + * + * @author: wangchao + * @Date: 2025/4/18 11:38 + * @Description: TaskClusterConfigLoader + * @since 7.0.0-RC2 + **/ +public interface TaskClusterConfigLoader { + /** + * load cluster config info + * + * @param clusterNodeId clusterNodeId + * @return AgentClusterVo + */ + AgentClusterVo load(String clusterNodeId); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/AgentStatusEvent.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/AgentStatusEvent.java new file mode 100644 index 000000000..5d671d4e2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/AgentStatusEvent.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event; + +import lombok.Getter; + +import org.springframework.context.ApplicationEvent; + +/** + * AgentStatusEvent + * + * @author: wangchao + * @Date: 2025/4/14 16:29 + * @Description: AgentStatusEvent + * @since 7.0.0-RC2 + **/ +@Getter +public class AgentStatusEvent extends ApplicationEvent { + private final String agentId; + private final boolean isOnline; + + /** + * Constructor Agent status event + * + * @param source source + * @param agentId agentId + * @param isOnline isOnline + */ + public AgentStatusEvent(Object source, String agentId, boolean isOnline) { + super(source); + this.agentId = agentId; + this.isOnline = isOnline; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/BaseEvent.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/BaseEvent.java new file mode 100644 index 000000000..eddf3ea41 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/BaseEvent.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event; + +import lombok.Data; + +import org.opengauss.admin.common.enums.agent.StoragePolicy; + +import java.util.List; +import java.util.Map; + +/** + * BaseEvent + * + * @author: wangchao + * @Date: 2025/3/13 09:49 + * @Description: MemoryMetricEvent + * @since 7.0.0 + **/ +@Data +public class BaseEvent { + /** + * event id + */ + protected Long eventId; + + /** + * task id + */ + protected Long taskId; + + /** + * agent id + */ + protected Long agentId; + + /** + * cluster node id + */ + protected String clusterNodeId; + + /** + * keep period + */ + protected String keepPeriod; + + /** + * collector metrics + */ + protected List collectorMetrics; + + /** + * property + */ + protected Map property; + + /** + * storage policy + */ + protected StoragePolicy storagePolicy; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/MetricEvent.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/MetricEvent.java new file mode 100644 index 000000000..3f486470c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/MetricEvent.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import org.opengauss.agent.data.CustomMetricData; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * MetricEvent + * + * @author: wangchao + * @Date: 2025/3/13 09:49 + * @Description: MetricEvent + * @since 7.0.0-RC2 + **/ +@Data +@EqualsAndHashCode(callSuper = true) +public class MetricEvent extends BaseEvent { + private List metricData; + + /** + * metric event info + * + * @return String + */ + public String info() { + return "[eventId=" + eventId + ", taskId=" + taskId + ", agentId=" + agentId + ", clusterNodeId=" + + clusterNodeId + ", collectorMetrics=" + collectorMetrics.size() + ", property=" + property.size() + + ", storagePolicy=" + storagePolicy + ", metricData=" + metricData.size() + "]"; + } + + /** + * metric event detail + * + * @return String + */ + public String detail() { + return "[eventId=" + eventId + ", taskId=" + taskId + ", agentId=" + agentId + ", clusterNodeId=" + + clusterNodeId + ", collectorMetrics=" + Optional.ofNullable(collectorMetrics) + .orElse(Collections.emptyList()) + .stream() + .filter(Objects::nonNull) + .collect(Collectors.joining("|")) + ", property=" + Optional.ofNullable(property) + .orElse(Collections.emptyMap()) + ", storagePolicy=" + storagePolicy + ",\n metricData=" + + Optional.ofNullable(metricData).orElse(Collections.emptyList()).stream().map(m -> { + String pointValues = Optional.ofNullable(m.getPoints()) + .orElse(Collections.emptyList()) + .stream() + .map(p -> p != null ? p.getValue() : "null") + .collect(Collectors.joining(",")); + return m.getName() + "(" + pointValues + ")"; + }).collect(Collectors.joining(";")) + "]"; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/PipelineEvent.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/PipelineEvent.java new file mode 100644 index 000000000..7c35e30a5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/PipelineEvent.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * PipelineEvent + * + * @author: wangchao + * @Date: 2025/3/13 09:49 + * @Description: PipelineEvent + * @since 7.0.0-RC2 + **/ +@Data +@EqualsAndHashCode(callSuper = true) +public class PipelineEvent extends BaseEvent { + private List> dataList; + + /** + * pipeline event info + * + * @return String + */ + public String info() { + return "[eventId=" + eventId + ", taskId=" + taskId + ", agentId=" + agentId + ", clusterNodeId=" + + clusterNodeId + ", collectorMetrics=" + Optional.ofNullable(collectorMetrics) + .orElse(Collections.emptyList()) + .size() + ", property=" + property.size() + ", storagePolicy=" + storagePolicy + ", dataList=" + + Optional.ofNullable(dataList).orElse(Collections.emptyList()).size() + "]"; + } + + /** + * pipeline event detail + * + * @return String + */ + public String detail() { + return "[eventId=" + eventId + ", taskId=" + taskId + ", agentId=" + agentId + ", clusterNodeId=" + + clusterNodeId + ", collectorMetrics=" + Optional.ofNullable(collectorMetrics) + .orElse(Collections.emptyList()) + .stream() + .filter(Objects::nonNull) + .collect(Collectors.joining("|")) + ", property=" + Optional.ofNullable(property) + .orElse(Collections.emptyMap()) + ", storagePolicy=" + storagePolicy + ",\n metricData=" + + Optional.ofNullable(dataList) + .orElse(Collections.emptyList()) + .stream() + .map(m -> "[" + m.keySet().stream().map(k -> k + "=" + m.get(k)).collect(Collectors.joining(",")) + "]") + .collect(Collectors.joining(";")) + "]"; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/config/BackpressureConfig.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/config/BackpressureConfig.java new file mode 100644 index 000000000..55bf2c5ba --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/config/BackpressureConfig.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.config; + +import com.lmax.disruptor.SleepingWaitStrategy; +import com.lmax.disruptor.dsl.Disruptor; +import com.lmax.disruptor.dsl.ProducerType; + +import org.opengauss.agent.event.MetricEvent; +import org.opengauss.agent.event.handler.HistoricalStorageHandler; +import org.opengauss.agent.event.handler.MetricExceptionHandler; +import org.opengauss.agent.event.handler.RealTimeStorageHandler; +import org.opengauss.agent.event.producer.BackpressureStrategy; +import org.opengauss.agent.event.producer.DynamicBackpressureStrategy; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.Executors; + +/** + * BackpressureConfig + * + * @author: wangchao + * @Date: 2025/3/18 15:57 + * @Description: BackpressureConfig + * @since 7.0.0-RC2 + **/ +@Configuration +public class BackpressureConfig { + /** + * capacity Threshold Percent + */ + @Value("${disruptor.backpressure.threshold:10}") + private int capacityThresholdPercent; + + /** + * init Delay Ms + */ + @Value("${disruptor.backpressure.initDelay:1}") + private long initDelayMs; + + /** + * max Delay Ms + */ + @Value("${disruptor.backpressure.maxDelay:100}") + private long maxDelayMs; + + /** + * delay Factor + */ + @Value("${disruptor.backpressure.factor:2}") + private double delayFactor; + @Value("${disruptor.bufferSize:16}") + private int bufferSize; + + /** + * backpressure Strategy + *
+     *     config disruptor
+     *     1. buffer size: 1024 * 16
+     *     2. producer type: multi
+     *     3. wait strategy: YieldingWaitStrategy
+     *     4. event handler: PipeTreeStorageHandler, PipeFingerprintStorageHandler,
+     *        RealTimeStorageHandler, HistoricalStorageHandler
+     *     5. exception handler: MetricExceptionHandler
+     *     6. start disruptor
+     *     7. log disruptor started with buffer size
+     * 
+ * + * @return BackpressureStrategy + */ + @Bean + public BackpressureStrategy backpressureStrategy() { + Disruptor disruptor = new Disruptor<>(MetricEvent::new, bufferSize * 1024, + Executors.defaultThreadFactory(), ProducerType.SINGLE, // 多生产者模式 + new SleepingWaitStrategy(100, 1000)); + // bind event handlers to the disruptor: + // all of the event handlers will be broadcast to all of the event messages. + disruptor.handleEventsWith(new RealTimeStorageHandler()).then(new HistoricalStorageHandler()); + disruptor.setDefaultExceptionHandler(new MetricExceptionHandler()); + disruptor.start(); + return new DynamicBackpressureStrategy(disruptor.getRingBuffer(), capacityThresholdPercent, initDelayMs, + maxDelayMs, delayFactor); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/config/DisruptorConfig.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/config/DisruptorConfig.java new file mode 100644 index 000000000..1bbd274e5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/config/DisruptorConfig.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.config; + +import com.lmax.disruptor.SleepingWaitStrategy; +import com.lmax.disruptor.dsl.Disruptor; +import com.lmax.disruptor.dsl.ProducerType; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.agent.event.PipelineEvent; +import org.opengauss.agent.event.handler.HistoricalStorageCleanerManager; +import org.opengauss.agent.event.handler.MetricExceptionHandler; +import org.opengauss.agent.event.handler.PipeFingerprintStorageHandler; +import org.opengauss.agent.event.handler.PipeHistoricalStorageHandler; +import org.opengauss.agent.event.handler.PipeRealTimeStorageHandler; +import org.opengauss.agent.event.handler.PipeTreeStorageHandler; +import org.opengauss.agent.event.handler.StorageHandlerUtils; +import org.opengauss.agent.event.producer.PipelineEventProducer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.Executors; + +import jakarta.annotation.PreDestroy; + +/** + * DisruptorConfig + * + * @author: wangchao + * @Date: 2025/3/18 10:24 + * @Description: DisruptorConfig + * @since 7.0.0-RC2 + **/ +@Slf4j +@Configuration +public class DisruptorConfig { + @Value("${disruptor.bufferSize:16}") + private int bufferSize; + + /** + * create a bean for the order event producer + *
+     *     config disruptor
+     *     1. buffer size: 1024 * 16
+     *     2. producer type: multi
+     *     3. wait strategy: YieldingWaitStrategy
+     *     4. event handler: PipeTreeStorageHandler, PipeFingerprintStorageHandler,
+     *        RealTimeStorageHandler, HistoricalStorageHandler
+     *     5. exception handler: MetricExceptionHandler
+     *     6. start disruptor
+     *     7. log disruptor started with buffer size
+     * 
+ * + * @return PipelineEventProducer + */ + @Bean + public PipelineEventProducer orderEventProducer() { + Disruptor disruptor = new Disruptor<>(PipelineEvent::new, bufferSize * 1024, + Executors.defaultThreadFactory(), ProducerType.SINGLE, new SleepingWaitStrategy(100, 1000)); + // bind event handlers to the disruptor: + // all of the event handlers will be broadcast to all of the event messages. + disruptor.handleEventsWith(new PipeRealTimeStorageHandler()); + disruptor.handleEventsWith(new PipeHistoricalStorageHandler()); + disruptor.handleEventsWith(new PipeTreeStorageHandler()); + disruptor.handleEventsWith(new PipeFingerprintStorageHandler()); + disruptor.setDefaultExceptionHandler(new MetricExceptionHandler()); + disruptor.start(); + log.info("disruptor started with buffer size: {}", bufferSize); + return new PipelineEventProducer(disruptor.getRingBuffer()); + } + + /** + * system shutdown, release resources + */ + @PreDestroy + public void destroy() { + // system shutdown, release resources + HistoricalStorageCleanerManager.unregister(); + StorageHandlerUtils.shutdownNow(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/HistoricalStorageCleanerManager.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/HistoricalStorageCleanerManager.java new file mode 100644 index 000000000..d3a4ae315 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/HistoricalStorageCleanerManager.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.handler; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiFunction; + +/** + * HistoricalStorageCleanerManager + * + * @author: wangchao + * @Date: 2025/6/10 09:39 + * @since 7.0.0-RC2 + **/ +public class HistoricalStorageCleanerManager { + private static final ConcurrentMap CLEANER_CONCURRENT_MAP = new ConcurrentHashMap<>(); + + /** + * register a cleaner + * + * @param keepPeriod keep period + * @param remappingFunction remapping function + */ + public static void registerCleanScheduledFuture(String keepPeriod, + BiFunction remappingFunction) { + CLEANER_CONCURRENT_MAP.compute(keepPeriod, remappingFunction); + } + + /** + * unregister all cleaners + */ + public static void unregister() { + CLEANER_CONCURRENT_MAP.forEach((period, cleaner) -> { + if (cleaner != null) { + cleaner.shutdownNow(); + } + }); + CLEANER_CONCURRENT_MAP.clear(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/HistoricalStorageHandler.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/HistoricalStorageHandler.java new file mode 100644 index 000000000..a15710ddc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/HistoricalStorageHandler.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.handler; + +import static org.opengauss.admin.common.constant.AgentConstants.Default.NONE; + +import com.lmax.disruptor.EventHandler; + +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.agent.MetricHistorical; +import org.opengauss.admin.common.enums.agent.StoragePolicy; +import org.opengauss.admin.common.exception.ops.AgentTaskException; +import org.opengauss.admin.common.utils.spring.SpringUtils; +import org.opengauss.agent.data.CustomMetricData; +import org.opengauss.agent.data.CustomMetricDataPoint; +import org.opengauss.agent.event.MetricEvent; +import org.opengauss.agent.repository.MetricHistoricalStorageService; +import org.opengauss.agent.utils.AgentTaskUtils; + +import java.time.Instant; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * HistoricalStorageHandler + * + * @author: wangchao + * @Date: 2025/3/18 10:21 + * @Description: HistoricalStorageHandler + * @since 7.0.0-RC2 + **/ +@Slf4j +public class HistoricalStorageHandler implements EventHandler { + private static final Snowflake SNOWFLAKE = IdUtil.getSnowflake(3, 2); + + private MetricHistoricalStorageService metricHistoricalStorageService; + + public HistoricalStorageHandler() { + } + + /** + * Initialize the handler. + * + * @param event published to the RingBuffer + * @param sequence of the event being processed + * @param isEndOfBatch flag to indicate if this is the last event in a batch from the RingBuffer + */ + @Override + public void onEvent(MetricEvent event, long sequence, boolean isEndOfBatch) { + if (Objects.equals(StoragePolicy.HISTORY, event.getStoragePolicy())) { + log.info("historical metric event with {} event {} info {}", sequence, isEndOfBatch, event.info()); + // save metric data, processing the data and save it to the historical storage + saveMetricData(event); + // and then register a scheduled task to clean up the historical data after the retention time + registerCleanScheduledFuture(event); + } + } + + private void saveMetricData(MetricEvent event) { + List data = event.getMetricData(); + List collectorMetrics = event.getCollectorMetrics(); + List metricHistoricalList = new LinkedList<>(); + Map propertyMap = event.getProperty(); + data.stream().filter(metric -> collectorMetrics.contains(metric.getName())).forEach(metric -> { + List points = metric.getPoints(); + points.forEach(point -> { + metricHistoricalList.add(convertMetricHistorical(event, metric, point, propertyMap)); + }); + }); + metricHistoricalStorageService.saveBatch(metricHistoricalList); + } + + private void registerCleanScheduledFuture(MetricEvent event) { + try { + String keepPeriod = event.getKeepPeriod(); + Long taskId = event.getTaskId(); + HistoricalStorageCleanerManager.registerCleanScheduledFuture(keepPeriod, (k, cleaner) -> { + if (cleaner != null) { + cleaner.addTask(taskId); + return cleaner; + } + cleaner = new OldHistoryDataCleaner(keepPeriod); + long interval = StorageHandlerUtils.calculateCleanInterval(keepPeriod); + long delay = StorageHandlerUtils.calculateInitialDelay(keepPeriod); + ScheduledFuture scheduledFuture = StorageHandlerUtils.getPeriodExecutor() + .scheduleAtFixedRate(cleaner, delay, interval, TimeUnit.SECONDS); + cleaner.setFuture(scheduledFuture); + cleaner.addTask(taskId); + return cleaner; + }); + } catch (AgentTaskException ex) { + log.error("register old history data cleaner error", ex); + } + } + + static class OldHistoryDataCleaner extends HistoryDataCleaner { + private final MetricHistoricalStorageService storageService; + + /** + * OldHistoryDataCleaner + * + * @param keepPeriod keep period + */ + public OldHistoryDataCleaner(String keepPeriod) { + super(keepPeriod); + this.storageService = SpringUtils.getBean(MetricHistoricalStorageService.class); + } + + @Override + public int cleanOldHistoryData(List taskIds, Instant cutoffTime) { + if (storageService != null) { + return storageService.deleteOldHistoryData(getTaskIds(), cutoffTime); + } + return 0; + } + } + + private MetricHistorical convertMetricHistorical(MetricEvent event, CustomMetricData metric, + CustomMetricDataPoint point, Map propertyMap) { + return MetricHistorical.builder() + .id(SNOWFLAKE.nextId()) + .taskId(event.getTaskId()) + .metric(metric.getName()) + .agentId(event.getAgentId()) + .clusterNodeId(StrUtil.isEmpty(event.getClusterNodeId()) ? NONE : event.getClusterNodeId()) + .property(AgentTaskUtils.getProperty(metric.getName(), point.getAttributes(), propertyMap)) + .value(point.getValue()) + .startTime(point.getStartEpochNanos()) + .endTime(point.getEpochNanos()) + .createTime(Instant.now()) + .build(); + } + + @Override + public void onStart() { + log.info("Real time metric handler onStart"); + metricHistoricalStorageService = SpringUtils.getBean(MetricHistoricalStorageService.class); + log.info("MetricHistoricalStorageService is get successfully"); + } + + @Override + public void onShutdown() { + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/HistoryDataCleaner.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/HistoryDataCleaner.java new file mode 100644 index 000000000..0c315effe --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/HistoryDataCleaner.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.handler; + +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.exception.ops.AgentTaskException; + +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ScheduledFuture; + +/** + * HistoryDataCleaner + * + * @author: wangchao + * @Date: 2025/6/10 09:05 + * @since 7.0.0-RC2 + **/ +@Slf4j +public abstract class HistoryDataCleaner implements Runnable { + private final CopyOnWriteArraySet taskIds = new CopyOnWriteArraySet<>(); + private final String keepPeriod; + @Setter + private ScheduledFuture future; + private Instant lastCleanTime = Instant.now(); + + /** + * PipOldHistoryDataCleaner + * + * @param keepPeriod keep period + */ + public HistoryDataCleaner(String keepPeriod) { + this.keepPeriod = keepPeriod; + } + + @Override + public void run() { + try { + if (!taskIds.isEmpty()) { + Instant currentCleanTime = Instant.now(); + Instant cutoffTime = currentCleanTime.minus(Duration.parse(keepPeriod)); + int deleted = cleanOldHistoryData(getTaskIds(), cutoffTime); + log.info("clean keepPeriod({}) tasks {} old history data [{}] clean period={}s, cost={}s ", keepPeriod, + taskIds, deleted, Duration.between(lastCleanTime, currentCleanTime).toSeconds(), + Duration.between(currentCleanTime, Instant.now()).toSeconds()); + lastCleanTime = currentCleanTime; + } + } catch (AgentTaskException ex) { + log.error("History data cleanup failed for period {}", keepPeriod, ex); + } + } + + /** + * clean old history data + * + * @param taskIds task ids + * @param cutoffTime cutoff time + * @return deleted count + */ + public abstract int cleanOldHistoryData(List taskIds, Instant cutoffTime); + + public List getTaskIds() { + return List.copyOf(taskIds); + } + + /** + * add task id + * + * @param taskId task id + */ + public void addTask(Long taskId) { + this.taskIds.add(taskId); + } + + /** + * remove task id + * + * @param taskId task id + */ + public void removeTask(Long taskId) { + taskIds.remove(taskId); + if (taskIds.isEmpty() && future != null) { + future.cancel(false); + } + } + + /** + * shutdown now clear task ids and cancel future + */ + public void shutdownNow() { + if (future != null) { + future.cancel(false); + } + taskIds.clear(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/MetricExceptionHandler.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/MetricExceptionHandler.java new file mode 100644 index 000000000..cad6089ea --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/MetricExceptionHandler.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.handler; + +import com.lmax.disruptor.ExceptionHandler; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.agent.event.BaseEvent; +import org.opengauss.agent.event.MetricEvent; +import org.opengauss.agent.event.PipelineEvent; + +/** + * MetricExceptionHandler + * + * @author: wangchao + * @Date: 2025/3/13 10:13 + * @Description: EventExceptionHandler + * @since 7.0.0-RC2 + **/ +@Slf4j +public class MetricExceptionHandler implements ExceptionHandler { + @Override + public void handleEventException(Throwable ex, long sequence, BaseEvent event) { + if (event instanceof MetricEvent) { + log.error("Error processing metric event: {}", ((MetricEvent) event).info(), ex); + } else if (event instanceof PipelineEvent) { + log.error("Error processing pipeline event: {}", ((PipelineEvent) event).info(), ex); + } else { + log.error("Error processing event: {}", event.toString(), ex); + } + } + + @Override + public void handleOnStartException(Throwable ex) { + log.error("Error starting metric event processor", ex); + } + + @Override + public void handleOnShutdownException(Throwable ex) { + log.error("Error shutting down metric event processor", ex); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/PipeFingerprintStorageHandler.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/PipeFingerprintStorageHandler.java new file mode 100644 index 000000000..38374ac90 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/PipeFingerprintStorageHandler.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.handler; + +import com.lmax.disruptor.EventHandler; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.enums.agent.StoragePolicy; +import org.opengauss.agent.event.PipelineEvent; + +import java.util.Objects; + +/** + * PipeFingerprintStorageHandler + * + * @author: wangchao + * @Date: 2025/3/18 10:21 + * @Description: PipeFingerprintStorageHandler + * @since 7.0.0-RC2 + **/ +@Slf4j +public class PipeFingerprintStorageHandler implements EventHandler { + public PipeFingerprintStorageHandler() { + } + + @Override + public void onEvent(PipelineEvent event, long sequence, boolean isEndOfBatch) { + if (Objects.equals(StoragePolicy.FINGERPRINT, event.getStoragePolicy())) { + log.info("fingerprint metric onEvent with {} event {} ", sequence, isEndOfBatch); + } + } + + @Override + public void onStart() { + } + + @Override + public void onShutdown() { + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/PipeHistoricalStorageHandler.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/PipeHistoricalStorageHandler.java new file mode 100644 index 000000000..7619b008a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/PipeHistoricalStorageHandler.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.handler; + +import com.lmax.disruptor.EventHandler; + +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskConfig; +import org.opengauss.admin.common.enums.agent.StoragePolicy; +import org.opengauss.admin.common.exception.ops.AgentTaskException; +import org.opengauss.admin.common.utils.spring.SpringUtils; +import org.opengauss.agent.event.PipelineEvent; +import org.opengauss.agent.impl.AgentTaskConfigService; +import org.opengauss.agent.repository.DynamicTableService; +import org.opengauss.agent.repository.SqlTemplate; + +import java.sql.Timestamp; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * PipeHistoricalStorageHandler + * + * @author: wangchao + * @Date: 2025/3/18 10:21 + * @Description: PipeHistoricalStorageHandler + * @since 7.0.0-RC2 + **/ +@Slf4j +public class PipeHistoricalStorageHandler implements EventHandler { + private static final Snowflake SNOWFLAKE = IdUtil.getSnowflake(4, 5); + + private DynamicTableService dynamicTableService; + private AgentTaskConfigService agentTaskConfigService; + + public PipeHistoricalStorageHandler() { + } + + @Override + public void onEvent(PipelineEvent event, long sequence, boolean isEndOfBatch) { + if (!Objects.equals(StoragePolicy.HISTORY, event.getStoragePolicy())) { + return; + } + long lastRowId = saveMetricData(event); + log.info("pipe history onEvent with {} event {}, rowSize={}, endOfId={}", sequence, isEndOfBatch, + event.getDataList().size(), lastRowId); + registerCleanScheduledFuture(event); + } + + private long saveMetricData(PipelineEvent event) { + String clusterNodeId = event.getClusterNodeId(); + AgentTaskConfig taskConfig = agentTaskConfigService.getAgentTaskConfig(event.getTaskId()); + // 设置commonProperty的属性值 + Map commonProperty = new HashMap<>(); + commonProperty.put("batch_id", SNOWFLAKE.nextId()); + commonProperty.put("task_id", taskConfig.getTaskId()); + commonProperty.put("agent_id", taskConfig.getAgentId()); + commonProperty.put("cluster_node_id", clusterNodeId); + commonProperty.put("create_time", Instant.now()); + String taskTemplateName = agentTaskConfigService.getAgentTaskTemplateName(event.getTaskId()); + List fieldNameList = agentTaskConfigService.getAgentTaskFields(taskConfig.getTaskId()); + String insertSql = SqlTemplate.buildHistoryTableInsertSql(taskTemplateName, fieldNameList); + long lastRowId = 0L; + List nameList = agentTaskConfigService.getAgentTaskNames(taskConfig.getTaskId()); + for (Map rowData : event.getDataList()) { + lastRowId = dynamicTableService.saveHistoryRowData(insertSql, nameList, commonProperty, rowData); + } + return lastRowId; + } + + private void registerCleanScheduledFuture(PipelineEvent event) { + try { + String keepPeriod = event.getKeepPeriod(); + Long taskId = event.getTaskId(); + String taskTemplateName = agentTaskConfigService.getAgentTaskTemplateName(event.getTaskId()); + HistoricalStorageCleanerManager.registerCleanScheduledFuture(keepPeriod, (k, cleaner) -> { + if (cleaner != null) { + cleaner.addTask(taskId); + return cleaner; + } + cleaner = new PipOldHistoryDataCleaner(keepPeriod, taskTemplateName, dynamicTableService); + long interval = StorageHandlerUtils.calculateCleanInterval(keepPeriod); + long delay = StorageHandlerUtils.calculateInitialDelay(keepPeriod); + cleaner.setFuture(StorageHandlerUtils.getPeriodExecutor() + .scheduleAtFixedRate(cleaner, delay, interval, TimeUnit.SECONDS)); + cleaner.addTask(taskId); + return cleaner; + }); + } catch (AgentTaskException ex) { + log.error("register old history data cleaner error", ex); + } + } + + @Override + public void onStart() { + dynamicTableService = SpringUtils.getBean(DynamicTableService.class); + agentTaskConfigService = SpringUtils.getBean(AgentTaskConfigService.class); + } + + static class PipOldHistoryDataCleaner extends HistoryDataCleaner { + private final String taskTemplateName; + private final DynamicTableService dynamicTableService; + + /** + * PipOldHistoryDataCleaner + * + * @param keepPeriod keep period + * @param taskTemplateName taskTemplateName + * @param dynamicTableService dynamicTableService + */ + public PipOldHistoryDataCleaner(String keepPeriod, String taskTemplateName, + DynamicTableService dynamicTableService) { + super(keepPeriod); + this.taskTemplateName = taskTemplateName; + this.dynamicTableService = dynamicTableService; + } + + @Override + public int cleanOldHistoryData(List taskIds, Instant cutOffTime) { + String deleteSql = SqlTemplate.buildHistoryTableDeleteSql(taskTemplateName); + Map commonProperty = new HashMap<>(); + commonProperty.put("create_time", Timestamp.from(cutOffTime)); + int cleanRowSize = 0; + for (Long taskId : taskIds) { + commonProperty.put("taskId", taskId); + cleanRowSize += dynamicTableService.deleteHistoryExpiredData(deleteSql, commonProperty); + } + return cleanRowSize; + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/PipeRealTimeStorageHandler.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/PipeRealTimeStorageHandler.java new file mode 100644 index 000000000..353bca272 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/PipeRealTimeStorageHandler.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.handler; + +import com.lmax.disruptor.EventHandler; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.agent.TaskTemplateDefinition; +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskConfig; +import org.opengauss.admin.common.enums.agent.StoragePolicy; +import org.opengauss.admin.common.utils.spring.SpringUtils; +import org.opengauss.agent.event.PipelineEvent; +import org.opengauss.agent.impl.AgentTaskConfigService; +import org.opengauss.agent.repository.DynamicTableService; +import org.opengauss.agent.repository.SqlTemplate; +import org.opengauss.agent.utils.AgentTaskUtils; + +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; + +/** + * PipeRealTimeStorageHandler + * + * @author: wangchao + * @Date: 2025/3/18 10:21 + * @Description: PipeRealTimeStorageHandler + * @since 7.0.0-RC2 + **/ +@Slf4j +public class PipeRealTimeStorageHandler implements EventHandler { + private DynamicTableService dynamicTableService; + private AgentTaskConfigService agentTaskConfigService; + + public PipeRealTimeStorageHandler() { + } + + @Override + public void onEvent(PipelineEvent event, long sequence, boolean isEndOfBatch) { + if (!Objects.equals(StoragePolicy.REAL_TIME, event.getStoragePolicy())) { + return; + } + String clusterNodeId = event.getClusterNodeId(); + AgentTaskConfig taskConfig = agentTaskConfigService.getAgentTaskConfig(event.getTaskId()); + List fieldNameList = agentTaskConfigService.getAgentTaskFields(taskConfig.getTaskId()); + // 设置commonProperty的属性值 + Map commonProperty = new HashMap<>(); + commonProperty.put("task_id", taskConfig.getTaskId()); + commonProperty.put("agent_id", taskConfig.getAgentId()); + commonProperty.put("cluster_node_id", clusterNodeId); + commonProperty.put("create_time", Instant.now()); + List nameList = agentTaskConfigService.getAgentTaskNames(taskConfig.getTaskId()); + TaskTemplateDefinition templateDefinition = taskConfig.getTemplateDefinition(); + dynamicTableService.deleteRealtimeExpiredData(SqlTemplate.buildRealTimeDeleteSql(templateDefinition.getName()), + commonProperty); + final AtomicLong rowIdx = new AtomicLong(0); + String insertSql = SqlTemplate.buildRealTimeTableInsertSql(templateDefinition.getName(), fieldNameList); + for (Map rowData : event.getDataList()) { + commonProperty.put("row_id", rowIdx.incrementAndGet()); + commonProperty.put("id", + AgentTaskUtils.calcRowPrimaryId(taskConfig.getTaskId(), taskConfig.getAgentId(), clusterNodeId, + rowIdx.get())); + dynamicTableService.saveRealtimeRowData(insertSql, nameList, commonProperty, rowData); + } + log.info("pipe realtime onEvent with {} event {}, rowSize={}", sequence, isEndOfBatch, + event.getDataList().size()); + } + + @Override + public void onStart() { + dynamicTableService = SpringUtils.getBean(DynamicTableService.class); + agentTaskConfigService = SpringUtils.getBean(AgentTaskConfigService.class); + } + + @Override + public void onShutdown() { + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/PipeTreeStorageHandler.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/PipeTreeStorageHandler.java new file mode 100644 index 000000000..0fbe31f97 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/PipeTreeStorageHandler.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.handler; + +import com.lmax.disruptor.EventHandler; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.enums.agent.StoragePolicy; +import org.opengauss.agent.event.PipelineEvent; + +import java.util.Objects; + +/** + * PipeTreeStorageHandler + * + * @author: wangchao + * @Date: 2025/3/18 10:21 + * @Description: PipeTreeStorageHandler + * @since 7.0.0-RC2 + **/ +@Slf4j +public class PipeTreeStorageHandler implements EventHandler { + public PipeTreeStorageHandler() { + } + + @Override + public void onEvent(PipelineEvent event, long sequence, boolean isEndOfBatch) { + if (Objects.equals(StoragePolicy.TREE, event.getStoragePolicy())) { + log.info("tree metric onEvent with {} event {} ", sequence, isEndOfBatch); + } + } + + @Override + public void onStart() { + } + + @Override + public void onShutdown() { + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/RealTimeStorageHandler.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/RealTimeStorageHandler.java new file mode 100644 index 000000000..b9195a8cc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/RealTimeStorageHandler.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.handler; + +import static org.opengauss.admin.common.constant.AgentConstants.Default.NONE; + +import com.lmax.disruptor.EventHandler; + +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.codec.digest.MurmurHash3; +import org.opengauss.admin.common.core.domain.entity.agent.MetricRealTime; +import org.opengauss.admin.common.enums.agent.StoragePolicy; +import org.opengauss.admin.common.utils.spring.SpringUtils; +import org.opengauss.agent.data.CustomMetricData; +import org.opengauss.agent.data.CustomMetricDataPoint; +import org.opengauss.agent.event.MetricEvent; +import org.opengauss.agent.repository.MetricRealTimeStorageService; +import org.opengauss.agent.utils.AgentTaskUtils; + +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * RealTimeStorageHandler + * + * @author: wangchao + * @Date: 2025/3/18 10:21 + * @Description: RealTimeStorageHandler + * @since 7.0.0-RC2 + **/ +@Slf4j +public class RealTimeStorageHandler implements EventHandler { + private MetricRealTimeStorageService realTimeStorageService; + + public RealTimeStorageHandler() { + } + + @Override + public void onEvent(MetricEvent event, long sequence, boolean isEndOfBatch) { + if (Objects.equals(StoragePolicy.REAL_TIME, event.getStoragePolicy()) || Objects.equals(StoragePolicy.HISTORY, + event.getStoragePolicy())) { + log.info("realTime metric event with {} event {} info {}", sequence, isEndOfBatch, event.info()); + List data = event.getMetricData(); + List collectorMetrics = event.getCollectorMetrics(); + List metricRealTimeList = new LinkedList<>(); + Map propertyMap = event.getProperty(); + data.stream().filter(metric -> collectorMetrics.contains(metric.getName())).forEach(metric -> { + List points = metric.getPoints(); + points.forEach(point -> { + MetricRealTime metricData = MetricRealTime.builder() + .metric(metric.getName()) + .agentId(event.getAgentId()) + .clusterNodeId(StrUtil.isEmpty(event.getClusterNodeId()) ? NONE : event.getClusterNodeId()) + .property(AgentTaskUtils.getProperty(metric.getName(), point.getAttributes(), propertyMap)) + .value(point.getValue()) + .startTime(point.getStartEpochNanos()) + .endTime(point.getEpochNanos()) + .createTime(Instant.now()) + .build(); + metricData.setMultiId( + MurmurHash3.hash32x86(metricData.pkVal().toString().getBytes(StandardCharsets.UTF_8))); + metricRealTimeList.add(metricData); + }); + }); + realTimeStorageService.saveOrUpdateBatch(metricRealTimeList); + } + } + + @Override + public void onStart() { + log.info("Real time metric handler onStart"); + realTimeStorageService = SpringUtils.getBean(MetricRealTimeStorageService.class); + log.info("MetricRealTimeStorageService is get successfully"); + } + + @Override + public void onShutdown() { + log.info("Real time metric handler onShutdown"); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/StorageHandlerUtils.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/StorageHandlerUtils.java new file mode 100644 index 000000000..9ea2ee3bb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/handler/StorageHandlerUtils.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.handler; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; + +import java.time.Duration; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +/** + * StorageHandlerUtils + * + * @author: wangchao + * @Date: 2025/6/10 08:58 + * @since 7.0.0-RC2 + **/ +public class StorageHandlerUtils { + private static final ScheduledExecutorService PERIOD_EXECUTOR = Executors.newScheduledThreadPool(4, + new ThreadFactoryBuilder().setNameFormat("his-cleaner-%d").build()); + + /** + * get period schedule executor + * + * @return period executor + */ + public static ScheduledExecutorService getPeriodExecutor() { + return PERIOD_EXECUTOR; + } + + /** + * calculate initial delay : after half of keep period (avoid execute immediately) + * + * @param keepPeriod keep period + * @return initial delay in seconds + */ + public static long calculateInitialDelay(String keepPeriod) { + return Duration.parse(keepPeriod).toSeconds() / 2; + } + + /** + * calculate clean interval : + *
+     *     1. calculate base interval : 0.1 * keep period
+     *     2. keep period <= 1h , the tiniest interval is 60s
+     *     3. keep period > 1h , the tiniest interval is 3600s
+     *     4. calculate clean interval : take the max of base interval and tiniest interval
+     * 
+ * + * @param keepPeriod keep period + * @return clean interval in seconds + */ + public static long calculateCleanInterval(String keepPeriod) { + long durationSeconds = Duration.parse(keepPeriod).toSeconds(); + long interval = durationSeconds / 10; + long tiniestInterval = durationSeconds <= 3600 ? 60 : 3600; + return Math.max(interval, tiniestInterval); + } + + /** + * shutdown period executor + */ + public static void shutdownNow() { + PERIOD_EXECUTOR.shutdownNow(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/BackpressureStatus.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/BackpressureStatus.java new file mode 100644 index 000000000..963e4f216 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/BackpressureStatus.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.producer; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * BackpressureStatus + * + * @author: wangchao + * @Date: 2025/3/18 15:57 + * @Description: BackpressureStatus + * @since 7.0.0-RC2 + **/ +@Data +@AllArgsConstructor +public class BackpressureStatus { + private long currentDelayMs; + private long pressureCount; + private long remainingCapacity; +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/BackpressureStrategy.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/BackpressureStrategy.java new file mode 100644 index 000000000..6010e28ac --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/BackpressureStrategy.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.producer; + +import com.lmax.disruptor.EventTranslatorTwoArg; + +import cn.hutool.core.lang.Pair; + +import org.opengauss.agent.data.CustomEventConfig; +import org.opengauss.agent.data.CustomMetricData; +import org.opengauss.agent.event.MetricEvent; + +import java.util.List; + +/** + * BackpressureStrategy + * + * @author: wangchao + * @Date: 2025/3/18 16:17 + * @Description: BackpressureStrategy + * @since 7.0.0-RC2 + **/ +public interface BackpressureStrategy { + /** + * shouldApplyBackpressure + * + * @return boolean + */ + boolean shouldApplyBackpressure(); + + /** + * applyBackpressure + */ + void applyBackpressure(); + + /** + * tryPublishEvent + * + * @param translator EventTranslatorTwoArg + * @param data Pair> + * @return boolean + */ + boolean tryPublishEvent(EventTranslatorTwoArg> translator, + Pair> data); + + /** + * setThresholdPercent + * + * @param percent int + */ + void setThresholdPercent(int percent); + + /** + * getBackpressureStatus + * + * @return BackpressureStatus + */ + BackpressureStatus getBackpressureStatus(); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/DynamicBackpressureStrategy.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/DynamicBackpressureStrategy.java new file mode 100644 index 000000000..bfa318621 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/DynamicBackpressureStrategy.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.producer; + +import com.lmax.disruptor.EventTranslatorTwoArg; +import com.lmax.disruptor.RingBuffer; + +import cn.hutool.core.lang.Pair; + +import org.opengauss.agent.data.CustomEventConfig; +import org.opengauss.agent.data.CustomMetricData; +import org.opengauss.agent.event.MetricEvent; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.LockSupport; + +/** + * DynamicBackpressureStrategy + * + * @author: wangchao + * @Date: 2025/3/18 15:58 + * @Description: DynamicBackpressureStrategy + * @since 7.0.0-RC2 + **/ +public class DynamicBackpressureStrategy implements BackpressureStrategy { + private final RingBuffer ringBuffer; + private final long initDelayNanos; + private final long maxDelayNanos; + private final double factor; + private volatile long currentDelayNanos; + private volatile int threshold; + private final AtomicLong pressureCounter = new AtomicLong(); + + /** + * Constructor dynamic backpressure strategy + * + * @param ringBuffer RingBuffer ring buffer + * @param thresholdPercent threshold percent + * @param initDelayMs init delay ms + * @param maxDelayMs max delay ms + * @param factor factor + */ + public DynamicBackpressureStrategy(RingBuffer ringBuffer, int thresholdPercent, long initDelayMs, + long maxDelayMs, double factor) { + this.ringBuffer = ringBuffer; + this.threshold = (int) (ringBuffer.getBufferSize() * thresholdPercent / 100.0); + this.initDelayNanos = TimeUnit.MILLISECONDS.toNanos(initDelayMs); + this.maxDelayNanos = TimeUnit.MILLISECONDS.toNanos(maxDelayMs); + this.factor = factor; + this.currentDelayNanos = initDelayNanos; + } + + /** + * Should apply backpressure + * + * @return boolean + */ + public boolean shouldApplyBackpressure() { + return ringBuffer.remainingCapacity() < threshold; + } + + @Override + public boolean tryPublishEvent( + EventTranslatorTwoArg> translator, + Pair> data) { + return ringBuffer.tryPublishEvent(translator, data.getKey(), data.getValue()); + } + + @Override + public void setThresholdPercent(int percent) { + this.threshold = percent; + } + + @Override + public BackpressureStatus getBackpressureStatus() { + return new BackpressureStatus(currentDelayNanos / 1000, pressureCounter.get(), ringBuffer.remainingCapacity()); + } + + @Override + public void applyBackpressure() { + long delay = currentDelayNanos; + int consecutivePressure = 0; + while (shouldApplyBackpressure()) { + if (consecutivePressure > 5) { + delay = Math.min(delay * 2, maxDelayNanos); + } else { + delay = (long) (delay * factor); + } + LockSupport.parkNanos(delay); + pressureCounter.incrementAndGet(); + consecutivePressure++; + if (consecutivePressure % 10 == 0) { + threshold = (int) (ringBuffer.getBufferSize() * Math.min(0.3, + 0.1 + consecutivePressure * 0.02)); + } + } + currentDelayNanos = Math.max(delay / 2, initDelayNanos); + } + + /** + * Reset delay + */ + public void resetDelay() { + currentDelayNanos = initDelayNanos; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/DynamicMetricEventProducer.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/DynamicMetricEventProducer.java new file mode 100644 index 000000000..443352488 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/DynamicMetricEventProducer.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.producer; + +import com.lmax.disruptor.EventTranslatorTwoArg; + +import cn.hutool.core.lang.Pair; +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.constant.AgentConstants; +import org.opengauss.admin.common.core.domain.entity.agent.TaskMetricsDefinition; +import org.opengauss.admin.common.core.domain.entity.agent.TaskTemplateDefinition; +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskConfig; +import org.opengauss.admin.common.exception.ops.AgentTaskException; +import org.opengauss.agent.data.CustomEventConfig; +import org.opengauss.agent.data.CustomMetricData; +import org.opengauss.agent.event.MetricEvent; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import jakarta.annotation.PreDestroy; + +/** + * DynamicMetricEventProducer + * + * @author: wangchao + * @Date: 2025/3/18 15:59 + * @Description: DynamicMetricEventProducer + * @since 7.0.0-RC2 + **/ +@Slf4j +@Service +public class DynamicMetricEventProducer { + private static final Snowflake SNOWFLAKE = IdUtil.getSnowflake(3, 3); + private static final Map> taskMetricPropMap = new ConcurrentHashMap<>(); + private static volatile AtomicInteger requestCounter = new AtomicInteger(0); + + private final BlockingQueue>> overflowQueue + = new LinkedBlockingQueue<>(10000); + private final BackpressureStrategy backpressureStrategy; + + /** + *
+     *   translator function definition:
+     *      the function parameters { MetricEvent, sequence , CustomEventConfig }
+     *   otel data structure will be converted to Disruptor data Event
+     * 
+ */ + private final EventTranslatorTwoArg> translator + = (event, sequence, config, data) -> { + event.setEventId(SNOWFLAKE.nextId()); + AgentTaskConfig taskConfig = config.getTaskConfig(); + event.setProperty(taskMetricPropMap.get(config.getTaskId())); + event.setCollectorMetrics(taskConfig.getCollectorMetricDetails()); + TaskTemplateDefinition templateDefinition = taskConfig.getTemplateDefinition(); + event.setKeepPeriod(templateDefinition.getKeepPeriod()); + event.setClusterNodeId(taskConfig.isDbTemplate() + ? taskConfig.getClusterConfig().getClusterNodeId() + : AgentConstants.Default.EMPTY); + event.setStoragePolicy(templateDefinition.getStoragePolicy()); + event.setAgentId(config.getAgentId()); + event.setTaskId(config.getTaskId()); + event.setMetricData(data); + }; + + /** + * DynamicMetricEventProducer + * + * @param backpressureStrategy BackpressureStrategy + */ + public DynamicMetricEventProducer(BackpressureStrategy backpressureStrategy) { + this.backpressureStrategy = backpressureStrategy; + } + + /** + * onDataTranslate + * + * @param metric CustomEventConfig + * @param dataList dataList + */ + public void onDataTranslate(CustomEventConfig metric, List dataList) { + requestCounter.incrementAndGet(); + Pair> pair = Pair.of(metric, dataList); + updateTaskMetric(metric); + log.debug("metric={}, requestCounter={}", metric.info(), requestCounter.get()); + if (tryFastPublish(pair)) { + return; + } + if (backpressureStrategy.shouldApplyBackpressure()) { + backpressureStrategy.applyBackpressure(); + if (tryFastPublish(pair)) { + log.debug("Backpressure tryFastPublish"); + return; + } + } + if (!overflowQueue.offer(pair)) { + handleCriticalOverflow(metric); + } + if (!overflowQueue.isEmpty()) { + log.debug("Metric overflowQueue={}", overflowQueue.size()); + } + } + + private void updateTaskMetric(CustomEventConfig metric) { + if (taskMetricPropMap.containsKey(metric.getTaskId())) { + return; + } + taskMetricPropMap.put(metric.getTaskId(), metric.getTaskConfig() + .getMetricsDefinitionList() + .stream() + .filter(definition -> definition.getProp() != null) + .collect(Collectors.toMap(TaskMetricsDefinition::getName, TaskMetricsDefinition::getProp))); + } + + private boolean tryFastPublish(Pair> data) { + try { + return backpressureStrategy.tryPublishEvent(translator, data); + } catch (AgentTaskException e) { + handlePublishError(data.getKey(), e); + return false; + } + } + + private void handlePublishError(CustomEventConfig metric, Exception e) { + log.error("CRITICAL: Publish failed {}", metric, e); + } + + @Scheduled(fixedRate = 10) + private void drainOverflowQueue() { + Pair> pair; + log.debug("Metric requestCounter={}", requestCounter.get()); + while ((pair = overflowQueue.poll()) != null) { + if (!tryFastPublish(pair)) { + overflowQueue.offer(pair); + break; + } + } + } + + private void handleCriticalOverflow(CustomEventConfig metric) { + log.error("Critical overflow persistence failed {}", metric); + } + + /** + * shutdown + */ + @PreDestroy + public void shutdown() { + Pair> pair; + while ((pair = overflowQueue.poll()) != null) { + handleCriticalOverflow(pair.getKey()); + } + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/PipelineEventProducer.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/PipelineEventProducer.java new file mode 100644 index 000000000..29b22c0c5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/event/producer/PipelineEventProducer.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.event.producer; + +import com.lmax.disruptor.EventTranslatorTwoArg; +import com.lmax.disruptor.RingBuffer; + +import cn.hutool.core.lang.Pair; +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.IdUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.constant.AgentConstants; +import org.opengauss.admin.common.core.domain.entity.agent.TaskMetricsDefinition; +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskConfig; +import org.opengauss.agent.data.CustomEventConfig; +import org.opengauss.agent.event.PipelineEvent; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import jakarta.annotation.PreDestroy; + +/** + * PipelineEventProducer + * + * @author: wangchao + * @Date: 2025/3/18 10:28 + * @Description: PipelineEventProducer + * @since 7.0.0-RC2 + **/ +@Slf4j +public class PipelineEventProducer { + private static final Snowflake SNOWFLAKE = IdUtil.getSnowflake(1, 2); + private static volatile AtomicInteger requestCounter = new AtomicInteger(0); + + private final RingBuffer ringBuffer; + private final BlockingQueue>>> backpressureQueue + = new LinkedBlockingQueue<>(10000); + private final ScheduledExecutorService retryScheduler = Executors.newSingleThreadScheduledExecutor(); + private final EventTranslatorTwoArg>> translator + = (event, sequence, config, data) -> { + event.setEventId(SNOWFLAKE.nextId()); + AgentTaskConfig taskConfig = config.getTaskConfig(); + event.setProperty(taskConfig.getMetricsDefinitionList() + .stream() + .filter(definition -> definition.getProp() != null) + .collect(Collectors.toMap(TaskMetricsDefinition::getName, TaskMetricsDefinition::getProp))); + event.setClusterNodeId(taskConfig.isDbTemplate() + ? taskConfig.getClusterConfig().getClusterNodeId() + : AgentConstants.Default.EMPTY); + event.setStoragePolicy(taskConfig.getTemplateDefinition().getStoragePolicy()); + event.setAgentId(config.getAgentId()); + event.setTaskId(config.getTaskId()); + event.setDataList(data); + }; + + /** + * PipelineEventProducer + * + * @param ringBuffer ringBuffer + */ + public PipelineEventProducer(RingBuffer ringBuffer) { + this.ringBuffer = ringBuffer; + retryScheduler.scheduleAtFixedRate(this::retryFailedEvents, 10, 10, TimeUnit.MILLISECONDS); + } + + /** + * onDataTranslate + * + * @param metric metric + * @param dataList dataList + */ + public void onDataTranslate(CustomEventConfig metric, List> dataList) { + requestCounter.incrementAndGet(); + if (!ringBuffer.tryPublishEvent(translator, metric, dataList)) { + if (!backpressureQueue.offer(Pair.of(metric, dataList))) { + handleOverflow(metric); + } + log.info("onDataTranslate backpressureQueue={} ", backpressureQueue.size()); + ThreadUtil.safeSleep(200); + } + } + + private void retryFailedEvents() { + Pair>> pair; + while ((pair = backpressureQueue.poll()) != null) { + if (!ringBuffer.tryPublishEvent(translator, pair.getKey(), pair.getValue())) { + backpressureQueue.offer(pair); + break; + } + } + } + + private void handleOverflow(CustomEventConfig metric) { + log.error("CRITICAL: Backpressure queue overflow! {}", metric); + } + + /** + * shutdown + */ + @PreDestroy + public void shutdown() { + Pair>> pair; + while ((pair = backpressureQueue.poll()) != null) { + handleOverflow(pair.getKey()); + } + retryScheduler.shutdown(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentCmdBuilder.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentCmdBuilder.java new file mode 100644 index 000000000..bcd5f2dbc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentCmdBuilder.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.impl; + +/** + * AgentCmdBuilder + * + * @author: wangchao + * @date: 2025/4/18 11:40 + * @since 7.0.0-RC2 + **/ +public class AgentCmdBuilder { + private static final String STOP_AGENT_CMD_TEMPLATE = "pgrep -f %s/%s | xargs -r kill -9"; + private static final String START_AGENT_CMD_TEMPLATE + = "nohup java -Dquarkus.config.locations=%s/application.yml -jar %s/%s >/dev/null 2>&1 &"; + private static final String CHECK_DIR_EXIST_CMD_TEMPLATE = "[ -d \"%s\" ] && echo 'exist' || echo 'not_exist'"; + private static final String CREATE_DIR_CMD_TEMPLATE = "mkdir -p \"%s\""; + private static final String CHECK_DIR_EMPTY_CMD_TEMPLATE + = "[ \"$(ls -A %s)\" ] && echo 'not_empty' || echo 'empty'"; + private static final String CHECK_DIR_WRITABLE_CMD_TEMPLATE = "[ -w \"%s\" ] && echo 'writable' || echo 'readonly'"; + private static final String SHA256SUM_CMD_TEMPLATE = "sha256sum '%s/%s' | cut -d ' ' -f 1"; + private static final String DELETE_AGENT_DIR_CMD_TEMPLATE = "rm -rf %s"; + private static final String JAVA_VERSION_CMD_TEMPLATE = "source ~/.bashrc | java -version 2>&1 |" + + " awk -F\\\" '/version/ {print $2}'"; + private static final String CHECK_AGENT_RUNNING_CMD_TEMPLATE = "pgrep -f \"%s\""; + + /** + * stop Agent "pgrep -f %s/%s | xargs -r kill -9" + * + * @param installPath installPath + * @param agentName agentName + * @return command + */ + public static String buildStopAgentCommand(String installPath, String agentName) { + return String.format(STOP_AGENT_CMD_TEMPLATE, installPath, agentName); + } + + /** + * start Agent nohup java -jar %s/%s >/dev/null 2>&1 & + * + * @param installPath installPath + * @param agentName agentName + * @return command + */ + public static String buildStartAgentCommand(String installPath, String agentName) { + return String.format(START_AGENT_CMD_TEMPLATE, installPath, installPath, agentName); + } + + /** + * check dir exist + * + * @param dir dir + * @return command + */ + public static String buildCheckDirExistCommand(String dir) { + return String.format(CHECK_DIR_EXIST_CMD_TEMPLATE, dir); + } + + /** + * create dir "mkdir -p \"%s\"" + * + * @param dir dir + * @return command + */ + public static String buildCreateDirCommand(String dir) { + return String.format(CREATE_DIR_CMD_TEMPLATE, dir); + } + + /** + * check dir is empty [ \"$(ls -A %s)\" ] && echo 'not_empty' || echo 'empty' + * + * @param dir dir + * @return command + */ + public static String buildCheckDirEmptyCommand(String dir) { + return String.format(CHECK_DIR_EMPTY_CMD_TEMPLATE, dir); + } + + /** + * check dir writable "[ -w \"%s\" ] && echo 'writable' || echo 'readonly'" + * + * @param dir dir + * @return command + */ + public static String buildCheckDirWritableCommand(String dir) { + return String.format(CHECK_DIR_WRITABLE_CMD_TEMPLATE, dir); + } + + /** + * delete agent dir "rm -rf \"%s\"" + * + * @param installPath installPath + * @return command + */ + public static String buildDeleteAgentDirCommand(String installPath) { + return String.format(DELETE_AGENT_DIR_CMD_TEMPLATE, installPath); + } + + /** + * calculate remote file sha256sum , + * back command (if cmd sha256sum invalid) : "openssl dgst -sha256 '%s' | awk '{print $2}'" + * + * @param installPath installPath + * @param agentName agentName + * @return command + */ + public static String buildSha256CheckCommand(String installPath, String agentName) { + return String.format(SHA256SUM_CMD_TEMPLATE, installPath, agentName); + } + + // 路径拼接工具方法(兼容多平台) + private static String joinPath(String... paths) { + return String.join("/", paths); + } + + /** + * check Java java -version 2>&1 | awk -F\\\" '/version/ {print $2}' + * + * @return command + */ + public static String buildCheckJavaVersionCommand() { + return JAVA_VERSION_CMD_TEMPLATE; + } + + /** + * check agent is running "ps -ef | grep '%s' | grep -v grep" + * + * @param installPath installPath + * @param agentName agentName + * @return command + */ + public static String buildCheckAgentRunningCommand(String installPath, String agentName) { + String agentPath = joinPath(installPath, agentName); + return String.format(CHECK_AGENT_RUNNING_CMD_TEMPLATE, agentPath); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentEnvironmentService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentEnvironmentService.java new file mode 100644 index 000000000..be28793f2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentEnvironmentService.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.impl; + +import com.jcraft.jsch.Session; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.agent.AgentInstallEntity; +import org.opengauss.admin.common.exception.ops.AgentTaskException; +import org.opengauss.admin.system.plugin.beans.SshLogin; +import org.opengauss.admin.system.service.JschExecutorService; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import jakarta.annotation.Resource; + +/** + * AgentEnvironmentService + * + * @author: wangchao + * @date: 2025/5/8 11:40 + * @since 7.0.0-RC2 + **/ +@Slf4j +@Service +public class AgentEnvironmentService { + @Resource + private JschExecutorService jschExecutorService; + @Resource + private AgentSshLoginService agentSshLoginService; + + /** + *
+     * check agent install environment
+     * the environment check is checked by ssh command, the check items are:
+     * 1. checked java version
+     * 2. checked agent port
+     * 3. checked agent install path,
+     * check path exists empty writable
+     * 4. checked agent running status
+     * 
+ * + * @param agent agent + * @return check result + */ + public Map checkEnvironment(AgentInstallEntity agent) { + Map checkRes = new HashMap<>(); + if (Objects.isNull(agent)) { + checkRes.put("agent", "agent is null"); + return checkRes; + } + SshLogin sshLogin = agentSshLoginService.getSshLogin(agent); + jschExecutorService.executeWithSession(sshLogin, (session -> { + List checkers = Arrays.asList(new JavaVersionChecker(), + new AgentPortChecker(agent.getAgentPort()), new InstallPathChecker(agent.getInstallPath()), + new AgentRunningChecker(agent.getInstallPath(), agent.getAgentName())); + checkers.forEach(checker -> { + try { + checker.check(session, checkRes); + } catch (AgentTaskException e) { + log.error("agent check failed for {}: {}", checker.getClass().getSimpleName(), e.getMessage()); + checkRes.put(checker.getCheckKey(), "check error: " + e.getMessage()); + } + }); + })); + return checkRes; + } + + /** + * Agent running environment checker + */ + private interface EnvironmentChecker { + /** + * check environment + * + * @param session session + * @param checkRes checkRes + */ + void check(Session session, Map checkRes); + + /** + * get check key + * + * @return check key + */ + String getCheckKey(); + } + + /** + * Agent port checker for agent environment + */ + @AllArgsConstructor + private class AgentPortChecker implements EnvironmentChecker { + private static final String CHECK_KEY = "agent_port"; + + private final Integer port; + + @Override + public void check(Session session, Map checkRes) { + boolean isConflict = jschExecutorService.checkOsPortConflict(session, port); + if (isConflict) { + checkRes.put(getCheckKey(), "agent port " + port + " is in used"); + } else { + checkRes.put(getCheckKey(), "agent port " + port + " is available"); + } + } + + @Override + public String getCheckKey() { + return CHECK_KEY; + } + } + + /** + * Java version checker for agent environment + */ + private class JavaVersionChecker implements EnvironmentChecker { + private static final String CHECK_KEY = "java_version"; + + @Override + public void check(Session session, Map checkRes) { + String javaVersionCmd = AgentCmdBuilder.buildCheckJavaVersionCommand(); + String versionOutput = jschExecutorService.execCommand(session, javaVersionCmd); + if (versionOutput == null || versionOutput.isEmpty()) { + checkRes.put(getCheckKey(), "Java Environment not found"); + return; + } + int majorVersion = Integer.parseInt(versionOutput.split("\\.")[0]); + if (majorVersion < 17) { + checkRes.put(getCheckKey(), majorVersion + " Java version required 17+ , current is: " + versionOutput); + } else { + checkRes.put(getCheckKey(), "java version meets the requirement : " + versionOutput); + } + } + + @Override + public String getCheckKey() { + return CHECK_KEY; + } + } + + /** + * Agent install path checker for agent environment + */ + @AllArgsConstructor + private class InstallPathChecker implements EnvironmentChecker { + private static final String CHECK_KEY = "install_path"; + private static final String SUB_KEY_EXIST = "exist"; + private static final String SUB_KEY_EMPTY = "empty"; + private static final String SUB_KEY_WRITABLE = "writable"; + + private final String installPath; + + @Override + public void check(Session session, Map checkRes) { + String checkExistCmd = AgentCmdBuilder.buildCheckDirExistCommand(installPath); + String existFlag = jschExecutorService.execCommand(session, checkExistCmd).trim(); + if ("not_exist".equals(existFlag)) { + checkRes.put(getSubItem(SUB_KEY_EXIST), "install path " + installPath + " is not exist"); + } else { + checkRes.put(getSubItem(SUB_KEY_EXIST), "install path " + installPath + " is exist"); + } + String checkEmptyCmd = AgentCmdBuilder.buildCreateDirCommand(installPath); + String emptyFlag = jschExecutorService.execCommand(session, checkEmptyCmd).trim(); + if ("not_empty".equals(emptyFlag)) { + checkRes.put(getSubItem(SUB_KEY_EMPTY), "install path " + installPath + " is not empty"); + } else { + checkRes.put(getSubItem(SUB_KEY_EMPTY), "install path " + installPath + " is empty"); + } + String checkwritableCmd = AgentCmdBuilder.buildCheckDirWritableCommand(installPath); + String permissionFlag = jschExecutorService.execCommand(session, checkwritableCmd).trim(); + if ("writable".equals(permissionFlag)) { + checkRes.put(getSubItem(SUB_KEY_WRITABLE), "install path " + installPath + " is writable"); + } else { + checkRes.put(getSubItem(SUB_KEY_WRITABLE), "install path " + installPath + " is not writable"); + } + } + + private String getSubItem(String checkKey) { + return getCheckKey() + "." + checkKey; + } + + @Override + public String getCheckKey() { + return CHECK_KEY; // 主键,子项通过后缀区分; + } + } + + /** + * Agent running checker for agent environment + */ + @AllArgsConstructor + private class AgentRunningChecker implements EnvironmentChecker { + private static final String CHECK_KEY = "agent_status"; + + private final String installPath; + private final String agentName; + + @Override + public void check(Session session, Map checkRes) { + String checkRunningCmd = AgentCmdBuilder.buildCheckAgentRunningCommand(installPath, agentName); + String runningFlag = jschExecutorService.execCommand(session, checkRunningCmd).trim(); + if (StrUtil.isEmpty(runningFlag)) { + checkRes.put(getCheckKey(), "agent is not running"); + } else { + checkRes.put(getCheckKey(), "agent is running"); + } + } + + @Override + public String getCheckKey() { + return CHECK_KEY; + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentHeartbeatServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentHeartbeatServiceImpl.java new file mode 100644 index 000000000..a78498180 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentHeartbeatServiceImpl.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.impl; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.agent.AgentInstallEntity; +import org.opengauss.admin.common.core.domain.model.agent.AgentHeartbeatStatus; +import org.opengauss.admin.common.core.domain.model.agent.HeartbeatReport; +import org.opengauss.admin.common.enums.agent.AgentStatus; +import org.opengauss.agent.event.AgentStatusEvent; +import org.opengauss.agent.service.IAgentHeartbeatService; +import org.opengauss.agent.service.IAgentInstallService; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; + +/** + * IAgentHeartbeatService + * + * @author: wangchao + * @Date: 2025/4/14 11:01 + * @Description: IAgentHeartbeatService + * @since 7.0.0-RC2 + **/ +@Slf4j +@Service +public class AgentHeartbeatServiceImpl implements IAgentHeartbeatService { + private final ConcurrentMap agentStatusCache = new ConcurrentHashMap<>(); + private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + @Resource + private ApplicationEventPublisher eventPublisher; + @Resource + @Lazy + private IAgentInstallService agentInstallService; + + /** + * init heartbeat service + */ + @PostConstruct + public void init() { + startHeartbeatService(); + } + + /** + * start heartbeat schedule service + */ + @Override + public void startHeartbeatService() { + scheduler.scheduleAtFixedRate(this::checkAgentsStatus, 5, 2, TimeUnit.SECONDS); + } + + /** + * check host agent status + */ + public void checkAgentsStatus() { + Instant now = Instant.now(); + List list = agentInstallService.list(); + list.forEach(agent -> { + String agentId = agent.getAgentId(); + AgentHeartbeatStatus heartbeatStatus = agentStatusCache.get(agentId); + if (Objects.isNull(heartbeatStatus)) { + agentInstallService.refreshAgentStatus(agentId, AgentStatus.STOP); + return; + } + heartbeatStatus.updateOnlineStatus(); + // last heartbeat time older 5sec ,status mark down + if (Duration.between(heartbeatStatus.getLastHeartbeat(), now).getSeconds() > 6) { + heartbeatStatus.markAsDown(); + log.warn("agent health status is down {}:{}:{}", agent.getAgentIp(), agent.getInstallUser(), + agent.getInstallPath()); + } + if (heartbeatStatus.checkAndResetStatusChanged()) { + triggerStatusEvent(agentId, heartbeatStatus.isOnline()); + } + }); + } + + @Override + public boolean isAgentOnline(String agentId) { + AgentHeartbeatStatus status = agentStatusCache.get(agentId); + return status != null && status.isOnline(); + } + + @Override + public void receiveHeartbeat(HeartbeatReport heart) { + String agentId = heart.getAgentId(); + if (agentId == null || agentId.isBlank()) { + log.warn("Invalid heartbeat: missing agentId"); + return; + } + AgentInstallEntity agent = agentInstallService.getByAgentId(agentId); + if (Objects.isNull(agent)) { + log.warn("Invalid heartbeat: agentId {}", agentId); + return; + } + AgentHeartbeatStatus agentStatus = agentStatusCache.compute(agentId, (id, status) -> { + if (status == null) { + status = new AgentHeartbeatStatus(); + log.info("New agent registered: {}", agentId); + } + status.updateFromHeartbeat(heart.getTimestamps()); + return status; + }); + log.info("receive heartbeat agent=[{} : {} : {}] ,status={}", agent.getAgentIp(), agent.getInstallUser(), + agent.getInstallPath(), agentStatus); + } + + /** + * Trigger an event if the status of an agent has changed. + * + * @param agentId agentId + * @param isOnline isOnline + */ + private void triggerStatusEvent(String agentId, boolean isOnline) { + eventPublisher.publishEvent(new AgentStatusEvent(this, agentId, isOnline)); + agentInstallService.refreshAgentStatus(agentId, isOnline ? AgentStatus.RUNNING : AgentStatus.STOP); + log.info("agent status changed and trigger status event {} -> {}", agentId, isOnline ? "ONLINE" : "OFFLINE"); + } + + @Override + public void deregister(HeartbeatReport requestDown) { + String agentId = requestDown.getAgentId(); + if (agentId != null) { + agentStatusCache.remove(agentId); + triggerStatusEvent(agentId, false); + log.info("Agent deregistered: {}", agentId); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentInstallServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentInstallServiceImpl.java new file mode 100644 index 000000000..0aa1c95ff --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentInstallServiceImpl.java @@ -0,0 +1,601 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.jcraft.jsch.Session; + +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.constant.AgentConstants; +import org.opengauss.admin.common.core.domain.entity.agent.AgentInstallEntity; +import org.opengauss.admin.common.core.domain.model.agent.AgentStartResult; +import org.opengauss.admin.common.core.domain.model.agent.HeartbeatReport; +import org.opengauss.admin.common.enums.agent.AgentStatus; +import org.opengauss.admin.common.exception.ops.AgentConnectionException; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.CommonUtils; +import org.opengauss.admin.common.utils.OpsAssert; +import org.opengauss.admin.common.utils.YamlUtils; +import org.opengauss.admin.common.utils.ip.IpUtils; +import org.opengauss.admin.system.mapper.agent.AgentInstallMapper; +import org.opengauss.admin.system.plugin.beans.SshLogin; +import org.opengauss.admin.system.service.ISysSettingExtService; +import org.opengauss.admin.system.service.JschExecutorService; +import org.opengauss.agent.service.AgentHttpProxy; +import org.opengauss.agent.service.IAgentHeartbeatService; +import org.opengauss.agent.service.IAgentInstallService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Function; +import java.util.stream.Collectors; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.annotation.Resource; + +/** + * IAgentInstallService + * + * @author: wangchao + * @Date: 2025/4/10 09:30 + * @Description: AgentInstallServiceImpl + * @since 7.0.0-RC2 + **/ +@Slf4j +@Service +public class AgentInstallServiceImpl extends ServiceImpl + implements IAgentInstallService { + private static final String AGENT_CONFIG_FILE_NAME = "application.yml"; + private static final int AGENT_STATUS_CHECK_MAX_RETRIES = 6; + + private final Map serverIpAndMask = new HashMap<>(); + @Value("${server.port}") + private String serverPort; + @Value("${agent.localPath}") + private String agentLocalPath; + @Value("${agent.agentName}") + private String agentName; + @Resource + private JschExecutorService jschExecutorService; + @Resource + private AgentSshLoginService agentSshLoginService; + @Resource + private IAgentHeartbeatService agentHeartbeatService; + @Resource + private ISysSettingExtService sysSettingExtService; + @Resource + private AgentHttpProxy agentHttpProxy; + + /** + * init ServerIp and Mask + */ + @PostConstruct + public void initServerIpList() { + serverIpAndMask.putAll(IpUtils.getLocalIpAddressMap()); + } + + @Override + public void installAgent(AgentInstallEntity agentInstall) { + String agentId = agentInstall.getAgentId(); + log.info("start install Agent[{}]", agentId); + CommonUtils.pathValidate(agentInstall.getInstallPath()); + SshLogin sshLogin = agentSshLoginService.getSshLogin(agentInstall); + jschExecutorService.executeWithSession(sshLogin, (session -> { + agentInstall.setAgentIp(sshLogin.getHost()); + checkJavaVersion(session); + log.info("check agent {} java version success", agentId); + checkAndPrepareAgentDir(session, agentInstall.getInstallPath()); + log.info("check agent {} install path success", agentId); + checkAgentPort(session, agentInstall.getAgentPort()); + log.info("check agent {} port success", agentId); + installAndConfigHostAgent(session, agentInstall); + log.info("config install agent {} success", agentId); + agentInstall.setCreateTime(Instant.now()); + })); + save(agentInstall); + log.info("save agent install info {}", agentId); + startAgent(agentId); + log.info("start agent {} and agent install finished", agentId); + } + + private void installAndConfigHostAgent(Session session, AgentInstallEntity install) { + try { + scpAgent(session, getAgentLocalFilePath(), install.getInstallPath()); + Map properties = loadAndConfigAgentProperties(install); + refreshRemoteHostAgentConfig(session, properties, install.getInstallPath()); + } catch (OpsException e) { + cleanAgentInstall(session, install); + throw new OpsException("Failed to install agent: " + e.getMessage()); + } + } + + private void cleanAgentInstall(Session session, AgentInstallEntity install) { + String deleteCmd = AgentCmdBuilder.buildDeleteAgentDirCommand(install.getInstallPath()); + jschExecutorService.execCommand(session, deleteCmd); + } + + private String getAgentLocalFilePath() { + return new File(agentLocalPath, agentName).getPath(); + } + + private void refreshRemoteHostAgentConfig(Session session, Map properties, String installPath) { + String content = YamlUtils.dumpYaml(properties); + jschExecutorService.remoteWrite(session, content, installPath + "/" + AGENT_CONFIG_FILE_NAME); + } + + private Map loadAndConfigAgentProperties(AgentInstallEntity install) { + Map properties = loadAgentDefaultYml(); + configAgentProperties(properties, install); + return properties; + } + + private void configAgentProperties(Map properties, AgentInstallEntity install) { + String serverHost = sysSettingExtService.queryAdminServerHost(); + ConfigAgentProperties configAgentProperties = new ConfigAgentProperties(serverHost, serverIpAndMask, + serverPort); + configAgentProperties.configAgentProperties(properties, install); + } + + private Map loadAgentDefaultYml() { + File propertiesFile = new File(agentLocalPath, AGENT_CONFIG_FILE_NAME); + if (!propertiesFile.exists()) { + throw new OpsException("agent.properties file not found"); + } + return YamlUtils.readYaml(propertiesFile.getPath()); + } + + private void scpAgent(Session session, String localJarPath, String installPath) { + if (!Files.exists(Path.of(localJarPath))) { + throw new OpsException("agent jar file not found"); + } + jschExecutorService.scp(session, localJarPath, installPath); + } + + private void checkAgentPort(Session session, Integer port) { + OpsAssert.isTrue(jschExecutorService.checkOsPortConflict(session, port), "agent port " + port + " is in used"); + } + + private void checkAndPrepareAgentDir(Session session, String targetDir) { + String checkExistCmd = AgentCmdBuilder.buildCheckDirExistCommand(targetDir); + String existFlag = jschExecutorService.execCommand(session, checkExistCmd).trim(); + if ("not_exist".equals(existFlag)) { + String createCmd = AgentCmdBuilder.buildCreateDirCommand(targetDir); + String exitCode = jschExecutorService.execCommand(session, createCmd); + log.info("{}:{} create dir result: [{}] ", session.getHost(), session.getUserName(), exitCode); + } + String checkEmptyCmd = AgentCmdBuilder.buildCheckDirEmptyCommand(targetDir); + String emptyFlag = jschExecutorService.execCommand(session, checkEmptyCmd).trim(); + if ("not_empty".equals(emptyFlag)) { + throw new OpsException("dir is not empty: " + targetDir); + } + String permissionCmd = AgentCmdBuilder.buildCheckDirWritableCommand(targetDir); + String permissionFlag = jschExecutorService.execCommand(session, permissionCmd).trim(); + if (!"writable".equals(permissionFlag)) { + throw new OpsException("dir does not permission writable: " + targetDir); + } + } + + private void checkJavaVersion(Session session) { + String javaVersionCmd = AgentCmdBuilder.buildCheckJavaVersionCommand(); + String versionOutput = jschExecutorService.execCommand(session, javaVersionCmd); + if (versionOutput == null || versionOutput.isEmpty()) { + throw new OpsException("Java environment not found"); + } + int majorVersion = CommonUtils.getJavaVersionMajor(versionOutput); + if (majorVersion < 17) { + throw new OpsException("Java version required 17+ , current version is " + versionOutput); + } + } + + @Override + public void uninstallAgent(String agentId) { + AgentInstallEntity agentInstall = getByAgentId(agentId); + OpsAssert.nonNull(agentInstall, "uninstall agent failed, agentId:" + agentId + " must be available"); + String stopCmd = AgentCmdBuilder.buildStopAgentCommand(agentInstall.getInstallPath(), agentName); + SshLogin sshLogin = agentSshLoginService.getSshLogin(agentInstall); + String execResult = jschExecutorService.execCommand(sshLogin, stopCmd); + log.info("stop agent result: {}", execResult); + String deleteCmd = AgentCmdBuilder.buildDeleteAgentDirCommand(agentInstall.getInstallPath()); + execResult = jschExecutorService.execCommand(sshLogin, deleteCmd); + log.info("delete agent install dir result: {}", execResult); + removeByAgentId(agentId); + agentHeartbeatService.deregister( + new HeartbeatReport(agentId, Instant.now(), AgentConstants.Status.HEARTBEAT_STATUS_DOWN)); + log.info("uninstall agent success, agentId: {}", agentId); + } + + @Override + public void removeByAgentId(String agentId) { + remove(Wrappers.lambdaQuery(AgentInstallEntity.class).eq(AgentInstallEntity::getAgentId, agentId)); + } + + @Override + public AgentStartResult startAgent(String agentId) { + AgentInstallEntity agentInstall = getByAgentId(agentId); + OpsAssert.nonNull(agentInstall, "start agent failed, agentId:" + agentId + " must be available"); + return startAgent(agentInstall); + } + + @Override + public void updateAgentPort(String agentId, Integer port) { + AgentInstallEntity agent = getByAgentId(agentId); + OpsAssert.nonNull(agent, "update agent port failed, agentId:" + agentId + " must be available"); + agent.setAgentPort(port); + jschExecutorService.executeWithSession(agentSshLoginService.getSshLogin(agent), (session) -> { + Map properties = loadAndConfigAgentProperties(agent); + refreshRemoteHostAgentConfig(session, properties, agent.getInstallPath()); + log.info("agent is updated, agentId: {}", agentId); + }); + update(agent, Wrappers.lambdaUpdate(AgentInstallEntity.class) + .eq(AgentInstallEntity::getAgentId, agentId) + .set(AgentInstallEntity::getAgentPort, port) + .set(AgentInstallEntity::getUpdateTime, Instant.now())); + restartAgent(agent); + } + + /** + * Start the Agent process via remote SSH command . After executing the start command, it is necessary to check + * whether the Agent process has been successfully started. Verifies successful Agent process startup through + * cyclic checks. + * + *

Loop detection (maximum detection attempts: {@link #AGENT_STATUS_CHECK_MAX_RETRIES}).

+ *

Each detection cycle process:

+ *
    + *
  1. Check if the Agent process exists.
  2. + *
  3. If the process exists, check if the Agent health interface is started normally
  4. + *
  5. If the health interface is accessible normally, the Agent has been successfully started. if the agent has + * started,exit the loop detection.
  6. + *
  7. Otherwise, the startup failed, and continue to the next cycle
  8. + *
+ * If all cycles fail to execute, confirm that the Agent startup is abnormal + * + * @param agentInstall agent install info + * @return AgentStartResult agent start result + * @see AgentCmdBuilder#buildStartAgentCommand + */ + public AgentStartResult startAgent(AgentInstallEntity agentInstall) { + String startAgentCmd = AgentCmdBuilder.buildStartAgentCommand(agentInstall.getInstallPath(), agentName); + jschExecutorService.execCommand(agentSshLoginService.getSshLogin(agentInstall), startAgentCmd); + log.info("start agent [{}:{}] [{}]", agentInstall.getAgentIp(), agentInstall.getInstallUser(), startAgentCmd); + refreshAgentStatus(agentInstall.getAgentId(), AgentStatus.STARTING); + if (isAgentProcessAlive(agentInstall)) { + return new AgentStartResult(agentInstall.getAgentId(), true, "start success"); + } else { + refreshAgentStatus(agentInstall.getAgentId(), AgentStatus.STOP); + return new AgentStartResult(agentInstall.getAgentId(), false, + "agent start failed,please check the agent start log."); + } + } + + private boolean isAgentProcessAlive(AgentInstallEntity agentInstall) { + String checkStatusCmd = AgentCmdBuilder.buildCheckAgentRunningCommand(agentInstall.getInstallPath(), agentName); + String checkResult; + boolean isAgentProcessAlive = false; + for (int i = 0; i < AGENT_STATUS_CHECK_MAX_RETRIES; i++) { + checkResult = jschExecutorService.execCommand(agentSshLoginService.getSshLogin(agentInstall), + checkStatusCmd); + log.info("agent [{}:{}] process pid=[{}]", agentInstall.getAgentIp(), agentInstall.getInstallUser(), + checkResult); + if (StrUtil.isNotEmpty(checkResult)) { + try { + String healthStatus = agentHttpProxy.checkAgentHealth(agentInstall); + isAgentProcessAlive = StrUtil.equalsIgnoreCase(healthStatus, "success"); + break; + } catch (AgentConnectionException ex) { + log.debug("agent [{}:{}] health error=[{}]", agentInstall.getAgentIp(), + agentInstall.getInstallUser(), ex.getMessage()); + } + } + ThreadUtil.sleep(1000); + } + log.warn("Agent process and health alive=[{}] agent: [{}:{}] cmd=[{}]", isAgentProcessAlive, + agentInstall.getAgentIp(), agentInstall.getInstallUser(), checkStatusCmd); + return isAgentProcessAlive; + } + + @Override + public List listByUserId(String userId) { + return list(Wrappers.lambdaQuery(AgentInstallEntity.class).eq(AgentInstallEntity::getInstallUserId, userId)); + } + + @Override + public AgentInstallEntity getByAgentId(String agentId) { + return getOne(Wrappers.lambdaQuery(AgentInstallEntity.class).eq(AgentInstallEntity::getAgentId, agentId)); + } + + @Override + public void stopAgent(String agentId) { + AgentInstallEntity agent = getByAgentId(agentId); + OpsAssert.nonNull(agent, "stop agent failed, agentId:" + agentId + " must be available"); + stopAgent(agent); + } + + private void stopAgent(AgentInstallEntity agent) { + String stopCmd = AgentCmdBuilder.buildStopAgentCommand(agent.getInstallPath(), agentName); + String execResult = jschExecutorService.execCommand(agentSshLoginService.getSshLogin(agent), stopCmd); + log.info("stop agent {} {} result: {} {}", agent.getAgentIp(), agent.getInstallUser(), stopCmd, execResult); + update(Wrappers.lambdaUpdate(AgentInstallEntity.class) + .eq(AgentInstallEntity::getAgentId, agent.getAgentId()) + .set(AgentInstallEntity::getStatus, AgentStatus.STOP)); + log.info("stop agent success, agentId: {}", agent.getAgentId()); + } + + private boolean restartAgent(AgentInstallEntity agent) { + stopAgent(agent); + AgentStartResult agentStartStatus = startAgent(agent); + return agentStartStatus.isSuccess(); + } + + @Override + public void refreshAgentStatus(String agentId, AgentStatus status) { + update(Wrappers.lambdaUpdate(AgentInstallEntity.class) + .eq(AgentInstallEntity::getAgentId, agentId) + .set(AgentInstallEntity::getStatus, status) + .set(AgentInstallEntity::getUpdateTime, Instant.now())); + } + + @Override + public void updateAgent(String agentId) { + AgentInstallEntity agent = getByAgentId(agentId); + OpsAssert.nonNull(agent, "update agent failed, agentId:" + agentId + " must be available"); + String localHash = getSha256SumOfLocalAgentFile(getAgentLocalFilePath()); + String command = AgentCmdBuilder.buildSha256CheckCommand(agent.getInstallPath(), agentName); + SshLogin sshLogin = agentSshLoginService.getSshLogin(agent); + String remoteHash = jschExecutorService.execCommand(sshLogin, command); + if (localHash.equalsIgnoreCase(remoteHash)) { + log.info("agent is lasted, agentId: {}", agentId); + } else { + jschExecutorService.executeWithSession(sshLogin, (session) -> { + scpAgent(session, getAgentLocalFilePath(), agent.getInstallPath()); + Map properties = loadAndConfigAgentProperties(agent); + refreshRemoteHostAgentConfig(session, properties, agent.getInstallPath()); + log.info("agent is updated, agentId: {}", agentId); + }); + } + update(Wrappers.lambdaUpdate(AgentInstallEntity.class) + .eq(AgentInstallEntity::getAgentId, agentId) + .set(AgentInstallEntity::getUpdateTime, Instant.now())); + } + + @Override + public List startAllOfAgent() { + List allAgents = list(); + List startAgentList = new LinkedList<>(); + log.info("start all agent, {} agent are starting", allAgents.size()); + CompletableFuture allFuture = CompletableFuture.allOf( + allAgents.stream().map((agent -> CompletableFuture.supplyAsync(() -> { + if (restartAgent(agent)) { + startAgentList.add(agent); + return true; + } else { + return false; + } + }))).toArray(CompletableFuture[]::new)); + try { + allFuture.get(); + log.info("all agent restart success"); + } catch (InterruptedException | ExecutionException e) { + log.error("error occurred while starting agents :", e); + } + return startAgentList; + } + + @Override + public AgentStatus getAgentStatus(String hostId) { + return Optional.ofNullable(getById(hostId)).map(AgentInstallEntity::getStatus).orElse(AgentStatus.UNINSTALL); + } + + @Override + public Map queryAgentStatus() { + return list().stream().collect(Collectors.toMap(AgentInstallEntity::getAgentId, AgentInstallEntity::getStatus)); + } + + /** + * stop all of agent ,when application is stop + */ + @PreDestroy + public void destroyAgent() { + list().parallelStream().forEach(this::stopAgent); + } + + @Override + public void updateAgentStatus(String agentId, AgentStatus status) { + update(Wrappers.lambdaUpdate(AgentInstallEntity.class) + .eq(AgentInstallEntity::getAgentId, agentId) + .set(AgentInstallEntity::getStatus, status) + .set(AgentInstallEntity::getUpdateTime, Instant.now())); + } + + @Override + public Map queryAgentInstallInfo() { + return list().stream().collect(Collectors.toMap(AgentInstallEntity::getAgentId, Function.identity())); + } + + @Override + public boolean hasInstall(String hostId) { + return !Objects.equals(AgentStatus.UNINSTALL, getAgentStatus(hostId)); + } + + @Override + public long countByHostUserId(String hostUserId) { + LambdaQueryWrapper userInstallCounter = Wrappers.lambdaQuery(AgentInstallEntity.class) + .eq(AgentInstallEntity::getInstallUserId, hostUserId); + return count(userInstallCounter); + } + + private String getSha256SumOfLocalAgentFile(String agentLocalFilePath) { + Path sha256Path = Path.of(agentLocalFilePath + ".sha256"); + String sha256Hash = readSha256FromFile(sha256Path); + if (sha256Hash.isEmpty()) { + sha256Hash = calculateLocalSHA256(agentLocalFilePath); + log.debug("Calculated SHA256 for {}: {}", agentLocalFilePath, sha256Hash); + } else { + log.info("Using cached SHA256 for {}: {}", agentLocalFilePath, sha256Hash); + } + return sha256Hash; + } + + /** + * Calculates the SHA256 hash of the file at the given path. + * + * @param sha256Path Path to the SHA256 file. + * @return SHA256 hash of the file. + */ + private String readSha256FromFile(Path sha256Path) { + if (!Files.exists(sha256Path)) { + return ""; + } + try { + String content = Files.readString(sha256Path, StandardCharsets.UTF_8); + String[] parts = content.split("\\s+"); // 兼容多个空白字符分隔 + if (parts.length < 1) { + log.warn("Invalid SHA256 file format: {}", sha256Path); + return ""; + } + return parts[0].trim(); + } catch (IOException e) { + log.error("Failed to read SHA256 file {}: {}", sha256Path, e.getMessage(), e); + return ""; + } + } + + /** + * calculates the SHA256 hash of the file at the given path. + * + * @param filePath Path to the SHA256 file. + * @return SHA256 hash of the file. + */ + private String calculateLocalSHA256(String filePath) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + try (InputStream is = new FileInputStream(filePath)) { + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = is.read(buffer)) != -1) { + digest.update(buffer, 0, bytesRead); + } + } + return bytesToHex(digest.digest()); + } catch (NoSuchAlgorithmException | IOException e) { + throw new OpsException("calculateLocalSHA256 error " + e.getMessage()); + } + } + + private String bytesToHex(byte[] bytes) { + StringBuilder hex = new StringBuilder(); + for (byte b : bytes) { + hex.append(String.format("%02x", b)); + } + return hex.toString(); + } + + private static class ConfigAgentProperties { + private final String agentId = "agent.id"; + private final String agentName = "agent.name"; + private final String agentServer = "agent.server"; + private final String agentPort = "quarkus.http.port"; + private final String agentLogPath = "quarkus.log.file.path"; + private Map serverIpAndMaskMap; + private String defaultServerHost; + private String serverPort; + + /** + * constructor + * + * @param serverHost serverHost + * @param serverIpAndMask server ip and mask map + * @param serverPort server port + */ + public ConfigAgentProperties(String serverHost, Map serverIpAndMask, String serverPort) { + this.serverIpAndMaskMap = serverIpAndMask; + this.defaultServerHost = serverHost; + this.serverPort = serverPort; + } + + /** + * config agent properties + * + * @param properties agent properties + * @param install agent install entity + */ + public void configAgentProperties(Map properties, AgentInstallEntity install) { + try { + if (properties == null || install == null) { + throw new OpsException("properties and install cannot be null"); + } + update(properties, agentId, install.getAgentId()); + update(properties, agentName, install.getAgentName()); + update(properties, agentServer, loadServerUrl(install.getAgentIp())); + update(properties, agentPort, install.getAgentPort()); + Object defaultPath = YamlUtils.getProperties(properties, agentLogPath); + update(properties, agentLogPath, buildDefaultLogPath(defaultPath.toString(), install.getInstallPath())); + } catch (OpsException e) { + throw new OpsException("Failed to load dataKit server url" + e.getMessage()); + } + } + + private void update(Map properties, String key, Object value) throws OpsException { + YamlUtils.updateProperties(properties, key, value); + } + + private String loadServerUrl(String agentIp) { + if (StrUtil.isNotEmpty(defaultServerHost)) { + return "http://" + defaultServerHost + ":" + this.serverPort; + } + return this.serverIpAndMaskMap.entrySet().stream().filter(entry -> { + try { + return IpUtils.isIpInSubnet(agentIp, entry.getKey(), entry.getValue()); + } catch (UnknownHostException e) { + log.error("Failed to load server url", e); + return false; + } + }).findFirst().map(entry -> "http://" + entry.getKey() + ":" + this.serverPort).orElse(""); + } + + private Object buildDefaultLogPath(String defaultPath, String installPath) { + if (StrUtil.isBlank(defaultPath)) { + return Paths.get(installPath, "datakit_agent.log").toString(); + } + return defaultPath.replace("/path", installPath); + } + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentServerReceiveServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentServerReceiveServiceImpl.java new file mode 100644 index 000000000..302e8b92e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentServerReceiveServiceImpl.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.impl; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskConfig; +import org.opengauss.admin.common.core.domain.model.agent.HostBaseInfo; +import org.opengauss.admin.common.utils.OpsAssert; +import org.opengauss.admin.system.service.HostBasicService; +import org.opengauss.admin.system.service.ops.IHostService; +import org.opengauss.agent.data.CustomEventConfig; +import org.opengauss.agent.data.CustomMetricData; +import org.opengauss.agent.event.producer.DynamicMetricEventProducer; +import org.opengauss.agent.event.producer.PipelineEventProducer; +import org.opengauss.agent.service.IAgentServerReceiveService; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import jakarta.annotation.Resource; + +/** + * IAgentServerReceiveService + * + * @author: wangchao + * @Date: 2025/4/12 16:38 + * @Description: IAgentServerReceiveService + * @since 7.0.0-RC2 + **/ +@Slf4j +@Service +public class AgentServerReceiveServiceImpl implements IAgentServerReceiveService { + @Resource + private IHostService hostService; + @Resource + private HostBasicService hostBasicService; + @Resource + private AgentTaskConfigService agentTaskConfigService; + @Resource + private PipelineEventProducer pipelineEventProducer; + @Resource + private DynamicMetricEventProducer dynamicMetricEventProducer; + + @Override + public void refreshBaseHostFixedInfo(HostBaseInfo hostBaseInfo) { + OpsAssert.nonNull(hostBaseInfo, "hostBaseInfo is null"); + String agentId = hostBaseInfo.getAgentId(); + OpsHostEntity host = hostService.getById(agentId); + OpsAssert.nonNull(host, "agent id is not existed, agentId: " + agentId + " cannot find host info"); + host.setHostname(hostBaseInfo.getHostName()); + host.setOs(hostBaseInfo.getOsName()); + host.setOsVersion(hostBaseInfo.getOsVersion()); + host.setOsBuild(hostBaseInfo.getOsBuild()); + + host.setCpuArch(hostBaseInfo.getCpuArchitecture()); + host.setCpuModel(hostBaseInfo.getCpuModel()); + host.setCpuFreq(hostBaseInfo.getCpuFreq()); + host.setPhysicalCores(hostBaseInfo.getPhysicalCores()); + host.setLogicalCores(hostBaseInfo.getLogicalCores()); + + hostService.updateById(host); + hostBasicService.getHostBasicInfo(agentId); + log.info("refresh host info success,hostId:{} : {}", agentId, hostBaseInfo); + } + + @Override + public void receiveMetricData(List taskIds, Long agentId, List metricDataList) { + log.debug("receive metric: taskId={},agentId={} data: {}", taskIds, agentId, metricDataList.size()); + taskIds.forEach(taskId -> { + AgentTaskConfig taskConfig = agentTaskConfigService.getAgentTaskConfig(taskId); + if (taskConfig == null) { + log.error("Failed to load config for taskId={}", taskId); + return; + } + dynamicMetricEventProducer.onDataTranslate( + CustomEventConfig.builder().taskId(taskId).agentId(agentId).taskConfig(taskConfig).build(), + Collections.unmodifiableList(metricDataList)); + }); + } + + @Override + public void receivePipelineData(Long taskId, Long agentId, List> dataList) { + AgentTaskConfig taskConfig = agentTaskConfigService.getAgentTaskConfig(taskId); + if (taskConfig == null) { + log.error("Failed to load config for taskId={}", taskId); + return; + } + pipelineEventProducer.onDataTranslate( + CustomEventConfig.builder().taskId(taskId).agentId(agentId).taskConfig(taskConfig).build(), + Collections.unmodifiableList(dataList)); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentServerServiceImpl.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentServerServiceImpl.java new file mode 100644 index 000000000..fc294a110 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentServerServiceImpl.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.impl; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.agent.AgentInstallEntity; +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskConfig; +import org.opengauss.admin.common.enums.agent.AgentStatus; +import org.opengauss.agent.event.AgentStatusEvent; +import org.opengauss.agent.service.AgentTaskManager; +import org.opengauss.agent.service.IAgentHeartbeatService; +import org.opengauss.agent.service.IAgentInstallService; +import org.opengauss.agent.service.IAgentServerService; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import jakarta.annotation.Resource; + +/** + * IAgentServerService + * + * @author: wangchao + * @Date: 2025/4/12 16:38 + * @Description: AgentServerService + * @since 7.0.0-RC2 + **/ +@Slf4j +@Service +public class AgentServerServiceImpl implements IAgentServerService { + private static final ConcurrentMap AGENT_STATUS_MAP = new ConcurrentHashMap<>(); + + @Resource + private IAgentInstallService agentInstallService; + @Resource + private AgentEnvironmentService agentEnvironmentService; + @Resource + private AgentTaskManager agentTaskManager; + + @Override + public Map anomalyChecking(String agentId) { + AgentInstallEntity agent = agentInstallService.getByAgentId(agentId); + Map result = new HashMap<>(); + boolean hasInstallRecord = Objects.nonNull(agent); + result.put("server_install_record", String.valueOf(hasInstallRecord)); + if (!hasInstallRecord) { + return result; + } + result.put("server_status", agent.getStatus().getValue()); + Map execEnvCheckRes = agentEnvironmentService.checkEnvironment(agent); + result.putAll(execEnvCheckRes); + return result; + } + + @Override + public void taskCallbackStarted(String agentId) { + log.info("agent {} task callback started", agentId); + AgentInstallEntity agent = agentInstallService.getByAgentId(agentId); + int size = agentTaskManager.startAgentTask(agent); + log.info("agent {} task callback : {}", agent.getAgentId(), size); + } + + @Override + public void startAgentTaskByTaskId(Long id) { + AgentTaskConfig taskConfig = agentTaskManager.queryAgentTaskInstance(id); + AgentInstallEntity agent = agentInstallService.getByAgentId(taskConfig.getAgentId()); + agentTaskManager.startAgentTask(agent, taskConfig); + } + + @Override + public boolean checkClusterNodeAgentInstallAndOnline(String hostId) { + AgentInstallEntity agent = agentInstallService.getByAgentId(hostId); + if (Objects.isNull(agent)) { + return false; + } + return AGENT_STATUS_MAP.getOrDefault(hostId, false); + } + + /** + * listen to agent status change event + * when agent up, send task notification to agent + * + * @param event event + */ + @EventListener + @Async("agentTaskExecutor") + public void handleStatusChange(AgentStatusEvent event) { + if (event.getSource() instanceof IAgentHeartbeatService) { + if (event.isOnline()) { + AgentInstallEntity agent = agentInstallService.getByAgentId(event.getAgentId()); + int size = agentTaskManager.startAgentTask(agent); + log.info("agent {} online, start agent task: {}", agent.getAgentId(), size); + agentInstallService.updateAgentStatus(event.getAgentId(), AgentStatus.RUNNING); + } else { + agentInstallService.updateAgentStatus(event.getAgentId(), AgentStatus.STOP); + } + AGENT_STATUS_MAP.put(event.getAgentId(), event.isOnline()); + } + logStateTransition(event); + } + + /** + * log state transition + * + * @param event event + */ + private void logStateTransition(AgentStatusEvent event) { + log.info("Agent Status Event: " + event.getAgentId() + " -> " + (event.isOnline() ? "online" : "offline")); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentSshLoginService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentSshLoginService.java new file mode 100644 index 000000000..4e7039769 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentSshLoginService.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.impl; + +import org.opengauss.admin.common.core.domain.entity.agent.AgentInstallEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.utils.OpsAssert; +import org.opengauss.admin.system.plugin.beans.SshLogin; +import org.opengauss.admin.system.service.ops.IHostService; +import org.opengauss.admin.system.service.ops.IHostUserService; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; + +/** + * AgentSshLoginService + * + * @author: wangchao + * @Date: 2025/4/12 17:54 + * @Description: AgentLoginService + * @since 7.0.0-RC2 + **/ +@Service +public class AgentSshLoginService { + private static final String AGENT_INVALID_MSG = "host user or username can not be exist [host:%s user:%s]"; + + @Resource + private EncryptionUtils encryptionUtils; + @Resource + private IHostService hostService; + @Resource + private IHostUserService hostUserService; + + /** + * create agent ssh login + * + * @param agentInstall agentInstall + * @return SshLogin + */ + public SshLogin getSshLogin(AgentInstallEntity agentInstall) { + OpsAssert.nonNull(agentInstall, "agent can not be null"); + String agentId = agentInstall.getAgentId(); + OpsHostEntity host = hostService.getById(agentId); + OpsAssert.nonNull(host, "host or agentId can not be exist [" + agentId + "]"); + String installUser = agentInstall.getInstallUser(); + String hostId = String.valueOf(agentInstall.getAgentId()); + OpsHostUserEntity user = hostUserService.getHostUserByUsername(hostId, installUser); + OpsAssert.nonNull(user, String.format(AGENT_INVALID_MSG, hostId, installUser)); + return new SshLogin(host.getPublicIp(), host.getPort(), user.getUsername(), + encryptionUtils.decrypt(user.getPassword())); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentTaskConfigService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentTaskConfigService.java new file mode 100644 index 000000000..a22feab2a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/impl/AgentTaskConfigService.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.impl; + +import static org.opengauss.admin.common.constant.AgentConstants.Field.COMMON_FIELD_LIST; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.agent.TaskMetricsDefinition; +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskConfig; +import org.opengauss.agent.service.AgentTaskManager; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Locale; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.stream.Collectors; + +import jakarta.annotation.Resource; + +/** + * AgentTaskConfigService + * + * @author: wangchao + * @Date: 2025/5/27 19:15 + * @since 7.0.0-RC2 + **/ +@Slf4j +@Service +public class AgentTaskConfigService { + private final ConcurrentMap taskConfigMap = new ConcurrentHashMap<>(); + private final ConcurrentMap> taskFieldNamesMap = new ConcurrentHashMap<>(); + private final ConcurrentMap> taskMetricNamesMap = new ConcurrentHashMap<>(); + private final ConcurrentMap taskTemplateNameMap = new ConcurrentHashMap<>(); + @Resource + private AgentTaskManager agentTaskManager; + + /** + * get agent task config + * + * @param taskId task id + * @return agent task config + */ + public AgentTaskConfig getAgentTaskConfig(Long taskId) { + return taskConfigMap.compute(taskId, (k, v) -> { + if (v == null) { + AgentTaskConfig newConfig = agentTaskManager.queryAgentTaskInstance(taskId); + if (newConfig == null) { + log.warn("taskId={} config not found", taskId); + return null; + } + return newConfig; + } + return v; + }); + } + + /** + * get agent task fields + * + * @param taskId task id + * @return agent task fields + */ + public List getAgentTaskFields(Long taskId) { + return taskFieldNamesMap.compute(taskId, (k, v) -> { + if (v == null) { + AgentTaskConfig taskConfig = getAgentTaskConfig(taskId); + if (taskConfig == null) { + return null; + } + return taskConfig.getMetricsDefinitionList() + .stream() + .filter(columnDef -> !COMMON_FIELD_LIST.contains(columnDef.getFieldName().toLowerCase(Locale.ROOT))) + .map(TaskMetricsDefinition::getFieldName) + .collect(Collectors.toList()); + } + return v; + }); + } + + /** + * get agent task metric names + * + * @param taskId task id + * @return agent task metric names + */ + public List getAgentTaskNames(Long taskId) { + return taskMetricNamesMap.compute(taskId, (k, v) -> { + if (v == null) { + AgentTaskConfig taskConfig = getAgentTaskConfig(taskId); + if (taskConfig == null) { + return null; + } + return taskConfig.getMetricsDefinitionList() + .stream() + .filter(columnDef -> !COMMON_FIELD_LIST.contains(columnDef.getFieldName().toLowerCase(Locale.ROOT))) + .map(TaskMetricsDefinition::getName) + .collect(Collectors.toList()); + } + return v; + }); + } + + /** + * get agent task template name + * + * @param taskId task id + * @return agent task template name + */ + public String getAgentTaskTemplateName(Long taskId) { + return taskTemplateNameMap.compute(taskId, (k, v) -> { + if (v == null) { + AgentTaskConfig newConfig = agentTaskManager.queryAgentTaskInstance(taskId); + if (newConfig == null) { + log.warn("taskId={} config not found", taskId); + return null; + } + return newConfig.getTemplateDefinition().getName(); + } + return v; + }); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/DynamicTableService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/DynamicTableService.java new file mode 100644 index 000000000..a36f90fb2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/DynamicTableService.java @@ -0,0 +1,358 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.repository; + +import static org.opengauss.admin.common.constant.AgentConstants.Field.COMMON_FIELD_LIST; +import static org.opengauss.admin.common.constant.AgentConstants.Field.FINGERPRINT_TABLE_REQUIRED_FIELD; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.constant.AgentConstants; +import org.opengauss.admin.common.core.domain.entity.agent.TaskMetricsDefinition; +import org.opengauss.admin.common.core.domain.entity.agent.TaskTemplateDefinition; +import org.opengauss.admin.common.enums.agent.StoragePolicy; +import org.opengauss.admin.common.exception.ops.AgentTaskException; +import org.opengauss.admin.system.base.DbDataLocation; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import jakarta.annotation.Resource; + +/** + * DynamicTableService + * + * @author: wangchao + * @Date: 2025/5/26 16:25 + * @since 7.0.0-RC2 + **/ +@Slf4j +@Service +public class DynamicTableService { + @Resource + private JdbcTemplate jdbcTemplate; + @Resource + private TaskTableRelationService taskTableRelationService; + + @Resource + private DbDataLocation dbDataLocation; + + /** + *
+     *     建表类型包含四种类型:
+     *       实时数据类型表 real_data_type
+     *       历史数据类型表 history_data_type
+     *       树状结构数据表 tree_data_type
+     *       指纹去除类型数据表 fingerprint_data_type
+     *     表名规范: agent_pipe_${taskName}
+     *    通用字段:
+     *       task_id long
+     *       agent_id long
+     *       cluster_id String
+     *       create_time timestamp
+     * 
+ * + * @param taskTemplate taskTemplate + * @param columnList columnList + */ + @Transactional + public void createPipeDataStorageTable(TaskTemplateDefinition taskTemplate, + List columnList) { + Objects.requireNonNull(columnList, "columnList must not be null"); + String nodeTableFields = buildTableColumnDefinition(columnList); + String templateName = taskTemplate.getName(); + TableNameGenerator.validateTaskId(taskTemplate.getName()); + switch (taskTemplate.getStoragePolicy()) { + case REAL_TIME: + jdbcTemplate.execute(SqlTemplate.buildRealTimeTableSql(templateName, nodeTableFields, + dbDataLocation.getDataLocationEnum())); + break; + case HISTORY: + jdbcTemplate.execute(SqlTemplate.buildHistoryTableSql(templateName, nodeTableFields, + dbDataLocation.getDataLocationEnum())); + break; + case FINGERPRINT: + // fingerprint table definition ,must include the field: fingerprint_id + // check if the column list contains the required field fingerprint_id + boolean hasFingerprintId = columnList.stream() + .map(TaskMetricsDefinition::getFieldName) + .anyMatch(FINGERPRINT_TABLE_REQUIRED_FIELD::equalsIgnoreCase); + // if the column list does not contain the required field, throw an exception + if (!hasFingerprintId) { + String existFields = columnList.stream() + .map(TaskMetricsDefinition::getFieldName) + .collect(Collectors.joining(",", "[", "]")); + throw new AgentTaskException(String.format( + "fingerprint table definition must include the field: fingerprint_id, but found: %s", + existFields)); + } + jdbcTemplate.execute(SqlTemplate.buildFingerprintTableSql(templateName, nodeTableFields, + dbDataLocation.getDataLocationEnum())); + break; + case TREE: + // 校验任务ID合法性 + TableNameGenerator.validateTaskId(templateName); + // 执行建表语句 + jdbcTemplate.execute(SqlTemplate.buildTreeTableSql(templateName, dbDataLocation.getDataLocationEnum())); + jdbcTemplate.execute(SqlTemplate.buildTreeNodeTableSql(templateName, nodeTableFields, + dbDataLocation.getDataLocationEnum())); + // 执行索引创建 + Arrays.stream(SqlTemplate.buildTreeIndexSql(templateName).split(";")).forEach(jdbcTemplate::execute); + break; + default: + throw new IllegalArgumentException("Unsupported storage policy: " + taskTemplate.getStoragePolicy()); + } + String realTableName = getTemplateRealTableName(templateName, taskTemplate.getStoragePolicy()); + log.info("database data location {}", dbDataLocation); + taskTableRelationService.saveTaskTemplateTable(templateName, realTableName); + log.info("create table {} success", realTableName); + } + + private String buildTableColumnDefinition(List columnList) { + return columnList.stream() + .filter(columnDef -> !COMMON_FIELD_LIST.contains(columnDef.getFieldName().toLowerCase(Locale.ROOT))) + .map(columnDef -> columnDef.getFieldName() + " " + translateJavaDataTypeOpenGauss(columnDef.getDataType())) + .collect(Collectors.joining(",")); + } + + private String translateJavaDataTypeOpenGauss(String dataType) { + String openGaussDataType = ""; + switch (dataType) { + case "integer": + openGaussDataType = "INT"; + break; + case "long": + openGaussDataType = "BIGINT"; + break; + case "float": + openGaussDataType = "FLOAT"; + break; + case "double": + openGaussDataType = "DOUBLE"; + break; + case "boolean": + openGaussDataType = "BOOLEAN"; + break; + case "date": + case "timestamp": + openGaussDataType = "TIMESTAMP"; + break; + case "string": + default: + openGaussDataType = "VARCHAR(255)"; + } + return openGaussDataType; + } + + /** + * check table exists + * + * @param name task name + * @param storagePolicy storage policy + * @return true if exists + */ + public boolean checkTemplateTableExist(String name, StoragePolicy storagePolicy) { + String realTableName = getTemplateRealTableName(name, storagePolicy); + String sql = SqlTemplate.buildCheckTableExistsSql(realTableName, dbDataLocation.getDataLocationEnum()); + return jdbcTemplate.queryForObject(sql, Boolean.class) == true; + } + + /** + * get template real table name + * + * @param name task name + * @param storagePolicy storage policy + * @return real table name + */ + public String getTemplateRealTableName(String name, StoragePolicy storagePolicy) { + String realTableName = ""; + switch (storagePolicy) { + case REAL_TIME: + realTableName = TableNameGenerator.getRealTimeTableName(name); + break; + case HISTORY: + realTableName = TableNameGenerator.getHistoryTableName(name); + break; + case FINGERPRINT: + realTableName = TableNameGenerator.getFingerprintTableName(name); + break; + case TREE: + realTableName = TableNameGenerator.getTreeTableName(name); + break; + default: + realTableName = null; + } + return realTableName; + } + + /** + * insert data into pipe table + * + * @param sql insert sql + * @param nameList task definition field name list,but not include common field + * @param commonVal common field value + * @param rowData row data + * @return result id + */ + public long saveHistoryRowData(String sql, List nameList, Map commonVal, + Map rowData) { + KeyHolder keyHolder = new GeneratedKeyHolder(); + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + int index = 1; + setPreparedStatement(ps, index++, commonVal.get(AgentConstants.Field.BATCH_ID)); + setPreparedStatement(ps, index++, commonVal.get(AgentConstants.Field.TASK_ID)); + setPreparedStatement(ps, index++, commonVal.get(AgentConstants.Field.AGENT_ID)); + setPreparedStatement(ps, index++, commonVal.get(AgentConstants.Field.CLUSTER_NODE_ID)); + for (String columnDef : nameList) { + setPreparedStatement(ps, index++, rowData.get(columnDef)); + } + setPreparedStatement(ps, index, commonVal.getOrDefault(AgentConstants.Field.CREATE_TIME, Instant.now())); + return ps; + }, keyHolder); + return keyHolder.getKey().longValue(); + } + + private void setPreparedStatement(PreparedStatement ps, int index, Object value) throws SQLException { + if (value instanceof Long longVal) { + ps.setLong(index, longVal); + } else if (value instanceof String stringVal) { + ps.setString(index, stringVal); + } else if (value instanceof Instant instantVal) { + ps.setTimestamp(index, Timestamp.from(instantVal)); + } else { + ps.setObject(index, value); + } + } + + /** + * insert data into pipe table + * + * @param sql insert sql + * @param nameList task definition field name list,but not include common field + * @param commonVal common field value + * @param rowData row data + */ + public void saveRealtimeRowData(String sql, List nameList, Map commonVal, + Map rowData) { + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + int index = 1; + setPreparedStatement(ps, index++, commonVal.get(AgentConstants.Field.ID)); + setPreparedStatement(ps, index++, commonVal.get(AgentConstants.Field.TASK_ID)); + setPreparedStatement(ps, index++, commonVal.get(AgentConstants.Field.AGENT_ID)); + setPreparedStatement(ps, index++, commonVal.getOrDefault(AgentConstants.Field.CLUSTER_NODE_ID, "")); + setPreparedStatement(ps, index++, commonVal.get(AgentConstants.Field.ROW_ID)); + for (String columnDef : nameList) { + setPreparedStatement(ps, index++, rowData.get(columnDef)); + } + setPreparedStatement(ps, index, commonVal.getOrDefault(AgentConstants.Field.CREATE_TIME, Instant.now())); + return ps; + }); + } + + /** + * check row exists + * + * @param existsSql exists sql + * @param rowId row id + * @return true if exists + */ + public boolean checkRowExists(String existsSql, long rowId) { + try { + return Boolean.TRUE.equals( + jdbcTemplate.queryForObject(existsSql, new Object[] {rowId}, (rs, rowNum) -> rs.getBoolean(1))); + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + /** + * update row data + * + * @param updateSql update sql + * @param nameList task definition field name list,but not include common field + * @param commonVal common field value + * @param rowData row data + */ + public void updateRowData(String updateSql, List nameList, Map commonVal, + Map rowData) { + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(updateSql, Statement.RETURN_GENERATED_KEYS); + int index = 1; + for (String columnDef : nameList) { + ps.setObject(index++, rowData.get(columnDef)); + } + setPreparedStatement(ps, index++, commonVal.getOrDefault(AgentConstants.Field.CREATE_TIME, Instant.now())); + setPreparedStatement(ps, index, commonVal.get(AgentConstants.Field.ID)); + return ps; + }); + } + + /** + * delete row data + * + * @param deleteSql delete sql + * @param commonVal common field value + */ + public void deleteRealtimeExpiredData(String deleteSql, Map commonVal) { + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(deleteSql, Statement.RETURN_GENERATED_KEYS); + int index = 1; + setPreparedStatement(ps, index++, commonVal.get(AgentConstants.Field.TASK_ID)); + setPreparedStatement(ps, index++, commonVal.get(AgentConstants.Field.AGENT_ID)); + setPreparedStatement(ps, index, commonVal.getOrDefault(AgentConstants.Field.CLUSTER_NODE_ID, "")); + return ps; + }); + } + + /** + * delete pipe history storage data by keep period + *
+     *     delete condition: task_id and create_time less than current time - keep_period
+     *     delete sql: delete from table_name where task_id = ? and create_time < ?
+     * 
+ * + * @param deleteSql delete sql + * @param commonProperty delete condition + * @return delete count + */ + public int deleteHistoryExpiredData(final String deleteSql, Map commonProperty) { + return jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement(deleteSql, Statement.RETURN_GENERATED_KEYS); + int index = 1; + setPreparedStatement(ps, index++, commonProperty.get(AgentConstants.Field.TASK_ID)); + setPreparedStatement(ps, index, + commonProperty.getOrDefault(AgentConstants.Field.CREATE_TIME, Instant.now())); + return ps; + }); + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/MetricHistoricalStorageService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/MetricHistoricalStorageService.java new file mode 100644 index 000000000..eaca44867 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/MetricHistoricalStorageService.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.repository; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.thread.ThreadUtil; + +import org.opengauss.admin.common.core.domain.entity.agent.MetricHistorical; +import org.opengauss.admin.system.mapper.agent.MetricHistoricalMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + +/** + * MetricHistoricalStorageService + * + * @author: wangchao + * @Date: 2025/4/25 16:10 + * @Description: MetricHistoricalStorageService + * @since 7.0.0-RC2 + **/ +@Service +public class MetricHistoricalStorageService extends ServiceImpl + implements IService { + private static final int BATCH_SIZE = 1000; + + private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"); + + /** + * Delete old history data + * + * @param taskIds taskIds + * @param cutoffTime cutoffTime + * @return the number of deleted rows + */ + @Transactional(rollbackFor = Exception.class) + public int deleteOldHistoryData(List taskIds, Instant cutoffTime) { + if (CollUtil.isEmpty(taskIds) || cutoffTime == null) { + return 0; + } + int totalDeleted = 0; + boolean hasMore; + do { + int deleted = deleteByTaskIdAndCutoffTime(taskIds, cutoffTime); + if (deleted > 0) { + totalDeleted += deleted; + } + hasMore = deleted == BATCH_SIZE; + if (hasMore) { + ThreadUtil.safeSleep(100); + } + } while (hasMore); + return totalDeleted; + } + + private int deleteByTaskIdAndCutoffTime(List taskIds, Instant cutoffTime) { + ZonedDateTime zonedCutoffTime = cutoffTime.atZone(ZoneId.of("GMT+8")) + .withZoneSameInstant(ZoneId.of("Asia/Shanghai")); // 或 ZoneId.of("UTC+8") + return baseMapper.delete(Wrappers.lambdaQuery(MetricHistorical.class) + .in(MetricHistorical::getTaskId, taskIds) + .lt(MetricHistorical::getCreateTime, zonedCutoffTime.format(formatter)) + .last("limit " + BATCH_SIZE)); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/MetricRealTimeStorageService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/MetricRealTimeStorageService.java new file mode 100644 index 000000000..ddf8f4eee --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/MetricRealTimeStorageService.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.repository; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import org.opengauss.admin.common.core.domain.entity.agent.MetricRealTime; +import org.opengauss.admin.system.mapper.agent.MetricRealTimeMapper; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * MetricRealTimeStorageService + * + * @author: wangchao + * @Date: 2025/4/25 16:10 + * @Description: MetricRealTimeStorageService + * @since 7.0.0-RC2 + **/ +@Service +public class MetricRealTimeStorageService extends ServiceImpl + implements IService { + /** + * query real time by cluster node id + * + * @param clusterNodeId cluster node id + * @return list + */ + public List queryRealTimeByClusterNodeId(String clusterNodeId) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(MetricRealTime.class); + wrapper.eq(MetricRealTime::getClusterNodeId, clusterNodeId); + return list(wrapper); + } + + /** + * query real time by agent id + * + * @param hostId agent id + * @return list + */ + public List listAgentHostInfo(String hostId) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(MetricRealTime.class); + wrapper.eq(MetricRealTime::getAgentId, hostId); + return list(wrapper); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/SqlTemplate.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/SqlTemplate.java new file mode 100644 index 000000000..ef7a99928 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/SqlTemplate.java @@ -0,0 +1,483 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.repository; + +import cn.hutool.core.util.StrUtil; + +import org.opengauss.admin.common.enums.DbDataLocationEnum; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * SqlTemplate + * support dynamic sql template for opengauss and intarkdb + * + * @author: wangchao + * @Date: 2025/5/26 16:26 + * @since 7.0.0-RC2 + **/ +public class SqlTemplate { + /** + * build real-time table create sql + * + * @param taskId task id + * @param extendTableFields extend table fields + * @param dataLocationEnum dataLocationEnum + * @return String + */ + public static String buildRealTimeTableSql(String taskId, String extendTableFields, + DbDataLocationEnum dataLocationEnum) { + String tableName = TableNameGenerator.getRealTimeTableName(taskId); + SqlGenerator generator = SqlGeneratorFactory.getGenerator(dataLocationEnum); + return generator.buildRealTimeTableSql(tableName, extendTableFields); + } + + /** + * build real time table insert sql + * insert into real_time_table (task_id, agent_id, cluster_id, field1, field2, ...) + * + * @param taskId task id + * @param fieldNameList field name list + * @return insert sql + */ + public static String buildRealTimeTableInsertSql(String taskId, List fieldNameList) { + String placeholders = StrUtil.join(",", Collections.nCopies(fieldNameList.size(), "?")); + String fields = fieldNameList.stream().map(String::trim).collect(Collectors.joining(", ")); + // @formatter:off + return String.format("INSERT INTO %s (id,task_id, agent_id, cluster_node_id,row_id, %s ,create_time)\n" + + " VALUES (?,?, ?, ?,?, %s,?)\n", + // @formatter:on + TableNameGenerator.getRealTimeTableName(taskId), fields, placeholders); + } + + /** + * build real time table exists sql + * + * @param taskId task id + * @return exists sql + */ + public static String buildRealTimeRowExistsSql(String taskId) { + return String.format("SELECT 1 FROM %s WHERE id = ?", TableNameGenerator.getRealTimeTableName(taskId)); + } + + /** + * build real time table update sql + * + * @param taskId task id + * @param fieldNameList field name list + * @return update sql + */ + public static String buildRealTimeTableUpdateSql(String taskId, List fieldNameList) { + return String.format("UPDATE %s SET %s , create_time=? WHERE id = ?", + TableNameGenerator.getRealTimeTableName(taskId), + fieldNameList.stream().map(field -> field + " = ?").collect(Collectors.joining(", "))); + } + + /** + * build real time table delete sql + * + * @param taskId task id + * @return delete sql + */ + public static String buildRealTimeDeleteSql(String taskId) { + return String.format("DELETE FROM %s WHERE task_id=? and agent_id=? and cluster_node_id = ?", + TableNameGenerator.getRealTimeTableName(taskId)); + } + + /** + * build history table insert sql + * + * @param taskId task id + * @param fieldNameList field name list + * @return insert sql + */ + public static String buildHistoryTableInsertSql(String taskId, List fieldNameList) { + String placeholders = StrUtil.join(",", Collections.nCopies(fieldNameList.size(), "?")); + String fields = fieldNameList.stream().map(String::trim).collect(Collectors.joining(", ")); + // @formatter:off + return String.format("INSERT INTO %s (batch_id,task_id, agent_id, cluster_node_id, %s ,create_time)\n" + + " VALUES (?,?, ?, ?, %s,?) RETURNING id\n", + // @formatter:on + TableNameGenerator.getRealTimeTableName(taskId), fields, placeholders); + } + + /** + * build history table create sql + * + * @param taskId task id + * @param extendTableFields extend table fields + * @param dataLocationEnum dataLocationEnum + * @return String + */ + public static String buildHistoryTableSql(String taskId, String extendTableFields, + DbDataLocationEnum dataLocationEnum) { + String tableName = TableNameGenerator.getHistoryTableName(taskId); + SqlGenerator generator = SqlGeneratorFactory.getGenerator(dataLocationEnum); + return generator.buildHistoryTableSql(tableName, extendTableFields); + } + + /** + * build fingerprint table create sql + * + * @param taskId task id + * @param extendTableFields extend table fields + * @param dataLocationEnum dataLocationEnum + * @return String + */ + public static String buildFingerprintTableSql(String taskId, String extendTableFields, + DbDataLocationEnum dataLocationEnum) { + String tableName = TableNameGenerator.getFingerprintTableName(taskId); + SqlGenerator generator = SqlGeneratorFactory.getGenerator(dataLocationEnum); + return generator.buildFingerprintTableSql(tableName, extendTableFields); + } + + /** + * build tree table create sql + * + * @param taskId task id + * @param dataLocationEnum dataLocationEnum + * @return String + */ + public static String buildTreeTableSql(String taskId, DbDataLocationEnum dataLocationEnum) { + String tableName = TableNameGenerator.getTreeTableName(taskId); + SqlGenerator generator = SqlGeneratorFactory.getGenerator(dataLocationEnum); + return generator.buildTreeTableSql(tableName); + } + + /** + * build tree node table create sql + * + * @param taskId task id + * @param nodeTableFields node table fields + * @param dataLocationEnum dataLocationEnum + * @return String + */ + public static String buildTreeNodeTableSql(String taskId, String nodeTableFields, + DbDataLocationEnum dataLocationEnum) { + String tableName = TableNameGenerator.getTreeTableName(taskId); + String nodeTable = TableNameGenerator.getTreeNodeTableName(taskId); + SqlGenerator generator = SqlGeneratorFactory.getGenerator(dataLocationEnum); + return generator.buildTreeNodeTableSql(tableName, nodeTable, nodeTableFields); + } + + /** + * build tree table index sql + * + * @param taskId task id + * @return String + */ + public static String buildTreeIndexSql(String taskId) { + String nodeTable = TableNameGenerator.getTreeNodeTableName(taskId); + // @formatter:off + return String.format(" CREATE INDEX idx_tree_path_%s ON %s (tree_id, path_level, node_path);\n" + + " CREATE INDEX idx_parent_tree_%s ON %s (tree_id, parent_id);", taskId, nodeTable, taskId, nodeTable); + // @formatter:on + } + + /** + * build check table exists sql + * + * @param tableName tableName + * @param dataLocationEnum dataLocationEnum + * @return String + */ + public static String buildCheckTableExistsSql(String tableName, DbDataLocationEnum dataLocationEnum) { + SqlGenerator generator = SqlGeneratorFactory.getGenerator(dataLocationEnum); + return generator.buildCheckTableExistsSql(tableName); + } + + /** + * build history table delete sql + * + * @param taskTemplateName task template name + * @return String + */ + public static String buildHistoryTableDeleteSql(String taskTemplateName) { + return String.format("DELETE FROM %s WHERE task_id=? and create_time < ?", + TableNameGenerator.getHistoryTableName(taskTemplateName)); + } + + /** + * sql generator factory + */ + static class SqlGeneratorFactory { + private static final Map generators = new HashMap<>(); + + static { + generators.put(DbDataLocationEnum.INTARKDB, new IntarkSqlGenerator()); + generators.put(DbDataLocationEnum.OPENGAUSS, new OpenGaussSqlGenerator()); + } + + /** + * get sql generator by db type + * + * @param dbType db type + * @return sql generator + */ + public static SqlGenerator getGenerator(DbDataLocationEnum dbType) { + return generators.getOrDefault(dbType, new OpenGaussSqlGenerator()); + } + } + + /** + * sql generator interface + */ + interface SqlGenerator { + /** + * build real time table table create sql for specific database + * + * @param tableName table name + * @param extendTableFields extend table fields + * @return String + */ + String buildRealTimeTableSql(String tableName, String extendTableFields); + + /** + * build history table create sql for specific database + * + * @param tableName table name + * @param extendTableFields extend table fields + * @return String + */ + String buildHistoryTableSql(String tableName, String extendTableFields); + + /** + * build fingerprint table create sql for specific database + * + * @param tableName table name + * @param extendTableFields extend table fields + * @return String + */ + String buildFingerprintTableSql(String tableName, String extendTableFields); + + /** + * build tree table create sql for specific database + * + * @param tableName table name + * @return String + */ + String buildTreeTableSql(String tableName); + + /** + * build tree node table create sql for specific database + * + * @param tableName table name + * @param nodeTable node table name + * @param nodeTableFields extend table fields + * @return String + */ + String buildTreeNodeTableSql(String tableName, String nodeTable, String nodeTableFields); + + /** + * build check table exists sql for specific database + * + * @param tableName table name + * @return String + */ + String buildCheckTableExistsSql(String tableName); + } + + static class OpenGaussSqlGenerator implements SqlGenerator { + @Override + public String buildRealTimeTableSql(String tableName, String extendTableFields) { + // @formatter:off + return String.format( + " CREATE TABLE IF NOT EXISTS %s (\n" + + " id BIGINT PRIMARY KEY,\n" + + " task_id BIGINT NOT NULL,\n" + + " agent_id BIGINT NOT NULL,\n" + + " row_id BIGINT NOT NULL,\n" + + " cluster_node_id VARCHAR(64),\n" + + " %s,\n" + + " create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n" + + " )", + // @formatter:on + tableName, extendTableFields); + } + + @Override + public String buildHistoryTableSql(String tableName, String extendTableFields) { + // @formatter:off + return String.format( + " CREATE TABLE IF NOT EXISTS %s (\n" + + " id BIGSERIAL PRIMARY KEY,\n" + + " batch_id BIGINT NOT NULL,\n" + + " task_id BIGINT NOT NULL,\n" + + " agent_id BIGINT NOT NULL,\n" + + " cluster_node_id VARCHAR(64),\n" + + " %s,\n" + + " create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n" + + " )", + // @formatter:on + tableName, extendTableFields); + } + + @Override + public String buildFingerprintTableSql(String tableName, String extendTableFields) { + // @formatter:off + return String.format( + " CREATE TABLE IF NOT EXISTS %s (\n" + + " id BIGSERIAL PRIMARY KEY,\n" + + " fingerprint_id VARCHAR(64) NOT NULL ,\n" + + " task_id BIGINT NOT NULL,\n" + + " agent_id BIGINT NOT NULL,\n" + + " cluster_node_id VARCHAR(64),\n" + + " %s,\n" + + " create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n" + + " )", + // @formatter:on + tableName, extendTableFields); + } + + @Override + public String buildTreeTableSql(String tableName) { + // @formatter:off + return String.format( + " CREATE TABLE IF NOT EXISTS %s (\n" + + " tree_id BIGSERIAL PRIMARY KEY,\n" + + " tree_name VARCHAR(255) NOT NULL UNIQUE,\n" + + " task_id BIGINT NOT NULL,\n" + + " agent_id BIGINT NOT NULL,\n" + + " cluster_node_id VARCHAR(64),\n" + + " separator CHAR(1) DEFAULT '/' NOT NULL,\n" + + " create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n" + + " )", + // @formatter:on + tableName); + } + + @Override + public String buildTreeNodeTableSql(String treeTable, String nodeTable, String nodeTableFields) { + // @formatter:off + return String.format( + " CREATE TABLE IF NOT EXISTS %s (\n" + + " node_id BIGSERIAL PRIMARY KEY,\n" + + " tree_id BIGINT NOT NULL REFERENCES %s(tree_id) ON DELETE CASCADE,\n" + + " node_path VARCHAR(4096) NOT NULL CHECK (node_path ~ '^/(\\\\d+/)*$'),\n" + + " parent_id BIGINT REFERENCES %s(node_id) ON DELETE CASCADE,\n" + + " %s" + + " path_level INT GENERATED ALWAYS AS (\n" + + " (length(node_path) - length(replace(node_path, '/', '')) - 1)) STORED,\n" + + " create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n" + + " )", nodeTable, treeTable, nodeTable, nodeTableFields); + // @formatter:on + } + + @Override + public String buildCheckTableExistsSql(String tableName) { + // @formatter:off + return String.format("SELECT EXISTS (SELECT 1 FROM pg_tables WHERE tablename='%s' and schemaname='public')", + // @formatter:on + tableName); + } + } + + static class IntarkSqlGenerator implements SqlGenerator { + @Override + public String buildRealTimeTableSql(String tableName, String extendTableFields) { + // @formatter:off + return String.format( + " CREATE TABLE IF NOT EXISTS %s (\n" + + " id BIGINT PRIMARY KEY,\n" + + " task_id BIGINT NOT NULL,\n" + + " agent_id BIGINT NOT NULL,\n" + + " row_id BIGINT NOT NULL,\n" + + " cluster_node_id VARCHAR(64),\n" + + " %s,\n" + + " create_time TIMESTAMP DEFAULT now()\n" + + " )", + // @formatter:on + tableName, extendTableFields); + } + + @Override + public String buildHistoryTableSql(String tableName, String extendTableFields) { + // @formatter:off + return String.format( + " CREATE TABLE IF NOT EXISTS %s (\n" + + " id BIGINT PRIMARY KEY AUTOINCREMENT,\n" + + " batch_id BIGINT NOT NULL,\n" + + " task_id BIGINT NOT NULL,\n" + + " agent_id BIGINT NOT NULL,\n" + + " cluster_node_id VARCHAR(64),\n" + + " %s,\n" + + " create_time TIMESTAMP DEFAULT now()\n" + + " )", + // @formatter:on + tableName, extendTableFields); + } + + @Override + public String buildFingerprintTableSql(String tableName, String extendTableFields) { + // @formatter:off + return String.format( + " CREATE TABLE IF NOT EXISTS %s (\n" + + " id BIGINT PRIMARY KEY AUTOINCREMENT,\n" + + " fingerprint_id VARCHAR(64) NOT NULL ,\n" + + " task_id BIGINT NOT NULL,\n" + + " agent_id BIGINT NOT NULL,\n" + + " cluster_node_id VARCHAR(64),\n" + + " %s,\n" + + " create_time TIMESTAMP DEFAULT now()\n" + + " )", + // @formatter:on + tableName, extendTableFields); + } + + @Override + public String buildTreeTableSql(String tableName) { + // @formatter:off + return String.format( + " CREATE TABLE IF NOT EXISTS %s (\n" + + " tree_id BIGINT PRIMARY KEY AUTOINCREMENT,\n" + + " tree_name VARCHAR(255) NOT NULL UNIQUE,\n" + + " task_id BIGINT NOT NULL,\n" + + " agent_id BIGINT NOT NULL,\n" + + " cluster_node_id VARCHAR(64),\n" + + " separator CHAR(1) DEFAULT '/' NOT NULL,\n" + + " create_time TIMESTAMP DEFAULT now()\n" + + " )", + // @formatter:on + tableName); + } + + @Override + public String buildTreeNodeTableSql(String treeTable, String nodeTable, String nodeTableFields) { + // @formatter:off + return String.format( + " CREATE TABLE IF NOT EXISTS %s (\n" + + " node_id BIGINT PRIMARY KEY AUTOINCREMENT,\n" + + " tree_id BIGINT NOT NULL REFERENCES %s(tree_id) ON DELETE CASCADE,\n" + + " node_path VARCHAR(4096) NOT NULL CHECK (node_path ~ '^/(\\\\d+/)*$'),\n" + + " parent_id BIGINT REFERENCES %s(node_id) ON DELETE CASCADE,\n" + + " %s" + + " path_level INT GENERATED ALWAYS AS (\n" + + " (length(node_path) - length(replace(node_path, '/', '')) - 1)) STORED,\n" + + " create_time TIMESTAMP DEFAULT now()\n" + + " )", nodeTable, treeTable, nodeTable, nodeTableFields); + // @formatter:on + } + + @Override + public String buildCheckTableExistsSql(String tableName) { + return String.format("SELECT EXISTS (SELECT 1 FROM \"SYS_TABLES\" WHERE name = '%s')", tableName); + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TableNameGenerator.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TableNameGenerator.java new file mode 100644 index 000000000..11c61ac1e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TableNameGenerator.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.repository; + +import org.opengauss.admin.common.exception.ops.AgentTaskException; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * TableNameGenerator + * + * @author: wangchao + * @Date: 2025/5/26 16:26 + * @since 7.0.0-RC2 + **/ +public class TableNameGenerator { + private static final int MAX_CORE_LENGTH = 21; + private static final int HASH_LENGTH = 6; // 哈希长度6位 + private static final String BASE62 = "0123456789abcdefghijklmnopqrstuvwxyz"; + private static MessageDigest digest; + private static final Pattern COMPILE = Pattern.compile("[a-zA-Z0-9]"); + + static { + try { + digest = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new AgentTaskException("SHA-1 algorithm not found: ", e); + } + } + + /** + * build dynamic realtime table name by taskId + * + * @param taskId taskId + * @return table name + */ + public static String getRealTimeTableName(String taskId) { + return "agent_pipe_real_" + processCore(taskId); + } + + /** + * build dynamic history table name by taskId + * + * @param taskId taskId + * @return table name + */ + public static String getHistoryTableName(String taskId) { + return "agent_pipe_hist_" + processCore(taskId); + } + + /** + * build dynamic fingerprint table name by taskId + * + * @param taskId taskId + * @return table name + */ + public static String getFingerprintTableName(String taskId) { + return "agent_pipe_fing_" + processCore(taskId); + } + + /** + * build dynamic tree table name by taskId + * + * @param taskId taskId + * @return table name + */ + public static String getTreeTableName(String taskId) { + return "agent_pipe_tree_" + processCore(taskId); + } + + /** + * build dynamic tree node table name by taskId + * + * @param taskId taskId + * @return table name + */ + public static String getTreeNodeTableName(String taskId) { + return "agent_pipe_tree_node_" + processCore(taskId); + } + + /** + * validate taskId + * + * @param taskId taskId + */ + public static void validateTaskId(String taskId) { + if (taskId == null || taskId.isEmpty()) { + throw new AgentTaskException("Task ID cannot be empty"); + } + if (!taskId.matches("^[a-zA-Z0-9_]+$")) { + throw new AgentTaskException("Invalid Task ID characters"); + } + } + + private static String processCore(String taskId) { + if (taskId.length() <= MAX_CORE_LENGTH) { + return taskId.toLowerCase(Locale.ROOT); + } + int dynamicTruncateLength = MAX_CORE_LENGTH - HASH_LENGTH; + int startIndex = Math.max(0, taskId.length() - dynamicTruncateLength); + String truncated = taskId.substring(startIndex).toLowerCase(Locale.ROOT); + Matcher matcher = COMPILE.matcher(truncated); + if (matcher.find()) { + int firstValidCharIndex = matcher.start(); + truncated = truncated.substring(firstValidCharIndex); + } + truncated = truncated.length() > dynamicTruncateLength + ? truncated.substring(0, dynamicTruncateLength) + : truncated; + String hashSuffix = calculateHashSuffix(taskId); + return truncated.endsWith("_") ? truncated + hashSuffix : truncated + "_" + hashSuffix; + } + + private static String longToBase36(long value, int length) { + StringBuilder sb = new StringBuilder(); + long tmp = value & Long.MAX_VALUE; + for (int i = 0; i < length; i++) { + int index = (int) (tmp % 36); + sb.insert(0, BASE62.charAt(index)); + tmp /= 36; + } + return sb.toString(); + } + + private static String calculateHashSuffix(String taskId) { + String salt = Integer.toHexString(taskId.hashCode()).toLowerCase(); + byte[] hashBytes = digest.digest((taskId + salt + "db_salt").getBytes(StandardCharsets.UTF_8)); + byte[] last8Bytes = Arrays.copyOfRange(hashBytes, hashBytes.length - 8, hashBytes.length); + byte[] first8Bytes = Arrays.copyOfRange(hashBytes, 0, 8); + BigInteger combined = new BigInteger(1, concatenate(first8Bytes, last8Bytes)); + return longToBase36(combined.longValue(), HASH_LENGTH); + } + + private static byte[] concatenate(byte[] a, byte[] b) { + byte[] result = new byte[a.length + b.length]; + System.arraycopy(a, 0, result, 0, a.length); + System.arraycopy(b, 0, result, a.length, b.length); + return result; + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskInstanceService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskInstanceService.java new file mode 100644 index 000000000..5bb01de5c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskInstanceService.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.repository; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import cn.hutool.core.collection.CollUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.agent.TaskInstanceEntity; +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskStatus; +import org.opengauss.admin.system.mapper.agent.TaskInstanceMapper; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.List; + +/** + * TaskInstanceService + * + * @author: wangchao + * @Date: 2025/4/25 16:15 + * @Description: TaskInstanceService + * @since 7.0.0-RC2 + **/ +@Slf4j +@Service +public class TaskInstanceService extends ServiceImpl + implements IService { + /** + * batch update task status to failed + * + * @param taskIds task ids + * @param errorMessage error message + */ + public void batchUpdateTaskFailedStatus(List taskIds, String errorMessage) { + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(TaskInstanceEntity.class); + updateWrapper.set(TaskInstanceEntity::getTaskStatus, AgentTaskStatus.FAILED) + .set(TaskInstanceEntity::getEndTime, Instant.now()) + .set(TaskInstanceEntity::getErrorMsg, errorMessage) + .in(TaskInstanceEntity::getId, taskIds); + update(updateWrapper); + log.info("update agent task instance status to failed, taskId: {}", taskIds); + } + + /** + * batch update task status to success + * + * @param taskId task id + */ + public void startTask(Long taskId) { + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(TaskInstanceEntity.class); + updateWrapper.set(TaskInstanceEntity::getTaskStatus, AgentTaskStatus.RUNNING) + .set(TaskInstanceEntity::getStartTime, Instant.now()) + .eq(TaskInstanceEntity::getId, taskId); + update(updateWrapper); + log.info("update agent task instance status to running, taskId: {}", taskId); + } + + /** + * query agent task instance + * + * @param agentId agent id + * @return task instance list + */ + public List queryAgentTaskInstance(String agentId) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(TaskInstanceEntity.class); + wrapper.eq(TaskInstanceEntity::getAgentId, agentId); + return list(wrapper); + } + + /** + * batch remove agent task instance + * + * @param agentId agent id + * @return remove count + */ + public int removeAgentAllTask(String agentId) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(TaskInstanceEntity.class); + wrapper.select(TaskInstanceEntity::getId); + wrapper.eq(TaskInstanceEntity::getAgentId, agentId); + List list = list(wrapper); + if (CollUtil.isEmpty(list)) { + return 0; + } + if (removeByIds(list.stream().map(TaskInstanceEntity::getId).toList())) { + log.info("remove agent task instance by agent id: {} ,size={}", agentId, list.size()); + return list.size(); + } else { + return 0; + } + } + + /** + * remove agent task instance by cluster node id + * + * @param clusterNodeId cluster node id + */ + public void removeAgentTaskByClusterNodeId(String clusterNodeId) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(TaskInstanceEntity.class); + wrapper.eq(TaskInstanceEntity::getClusterNodeId, clusterNodeId); + remove(wrapper); + log.info("remove agent task instance by cluster node id: {}", clusterNodeId); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskMetricsDefinitionService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskMetricsDefinitionService.java new file mode 100644 index 000000000..bf7989b91 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskMetricsDefinitionService.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.repository; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import org.opengauss.admin.common.core.domain.entity.agent.TaskMetricsDefinition; +import org.opengauss.admin.system.mapper.agent.TaskMetricsDefinitionMapper; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * TaskMetricsDefinitionService + * + * @author: wangchao + * @Date: 2025/4/25 16:18 + * @Description: TaskMetricsDefinitionService + * @since 7.0.0-RC2 + **/ +@Service +public class TaskMetricsDefinitionService extends ServiceImpl + implements IService { + /** + * query task metrics definition by name in list + * + * @param collectMetric collect metric + * @return task metrics definition list + */ + public List findByNameIn(List collectMetric) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(TaskMetricsDefinition.class) + .in(TaskMetricsDefinition::getName, collectMetric); + return list(queryWrapper); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskSchemaDefinitionService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskSchemaDefinitionService.java new file mode 100644 index 000000000..ae358a474 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskSchemaDefinitionService.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.repository; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import cn.hutool.core.util.StrUtil; + +import org.opengauss.admin.common.core.domain.entity.agent.TaskSchemaDefinition; +import org.opengauss.admin.system.mapper.agent.TaskSchemaDefinitionMapper; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * TaskSchemaDefinitionService + * + * @author: wangchao + * @Date: 2025/4/25 16:21 + * @Description: TaskSchemaDefinitionService + * @since 7.0.0-RC2 + **/ +@Service +public class TaskSchemaDefinitionService extends ServiceImpl + implements IService { + /** + * query task schema definition by name in list + * + * @param collectMetric collectMetric + * @return list + */ + public List getMetricsByName(String collectMetric) { + if (StrUtil.isBlank(collectMetric)) { + return Collections.emptyList(); + } + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(TaskSchemaDefinition.class) + .select(TaskSchemaDefinition::getMetric) + .eq(TaskSchemaDefinition::getName, collectMetric); + return baseMapper.selectList(queryWrapper) + .stream() + .map(TaskSchemaDefinition::getMetric) + .distinct() + .collect(Collectors.toList()); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskTableRelationService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskTableRelationService.java new file mode 100644 index 000000000..07c1ce3b9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskTableRelationService.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.repository; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import org.opengauss.admin.common.core.domain.entity.agent.TaskTableRelation; +import org.opengauss.admin.system.mapper.agent.TaskTableRelationMapper; +import org.springframework.stereotype.Service; + +/** + * AgentClusterRelationService + * + * @author: wangchao + * @Date: 2025/4/25 16:10 + * @Description: AgentClusterRelationService + * @since 7.0.0-RC2 + **/ +@Service +public class TaskTableRelationService extends ServiceImpl + implements IService { + /** + * save task template table + * + * @param templateName templateName + * @param realTableName realTableName + */ + public void saveTaskTemplateTable(String templateName, String realTableName) { + TaskTableRelation taskTableRelation = new TaskTableRelation(); + taskTableRelation.setTaskId(templateName); + taskTableRelation.setTaskTableName(realTableName); + saveOrUpdate(taskTableRelation); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskTemplateDefinitionService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskTemplateDefinitionService.java new file mode 100644 index 000000000..ee51295aa --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/repository/TaskTemplateDefinitionService.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.repository; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import org.opengauss.admin.common.core.domain.entity.agent.TaskTemplateDefinition; +import org.opengauss.admin.common.enums.agent.TaskType; +import org.opengauss.admin.system.mapper.agent.TaskTemplateDefinitionMapper; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * TaskTemplateDefinitionService + * + * @author: wangchao + * @Date: 2025/4/25 16:23 + * @Description: TaskTemplateDefinitionService + * @since 7.0.0-RC2 + **/ +@Service +public class TaskTemplateDefinitionService extends ServiceImpl + implements IService { + /** + * queryPipeTaskTemplateDefinition + * + * @return List + */ + public List queryPipeTaskTemplateDefinition() { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(TaskTemplateDefinition.class); + wrapper.in(TaskTemplateDefinition::getType, TaskType.DB_PIPE, TaskType.OS_PIPE); + return list(wrapper); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/AgentHttpProxy.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/AgentHttpProxy.java new file mode 100644 index 000000000..f9eaa520c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/AgentHttpProxy.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service; + +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; +import reactor.util.retry.Retry; + +import org.opengauss.admin.common.core.domain.entity.agent.AgentInstallEntity; +import org.opengauss.admin.common.exception.ops.AgentConnectionException; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +import java.time.Duration; +import java.util.Locale; +import java.util.concurrent.TimeoutException; + +import jakarta.annotation.Resource; + +/** + * AgentHttpProxy + * + * @author: wangchao + * @Date: 2025/7/28 22:24 + * @since 7.0.0-RC2 + **/ +@Slf4j +@Service +public class AgentHttpProxy { + private static final String AGENT_PUB_KEY_URL = "http://%s:%d/agent/pubKey"; + private static final String AGENT_HEALTH_URL = "http://%s:%d/agent/health"; + + @Resource + private WebClient webClient; + private final Retry retryPolicy = Retry.backoff(3, Duration.ofSeconds(1)) + .filter( + throwable -> throwable instanceof TimeoutException || throwable instanceof WebClientRequestException || ( + throwable instanceof WebClientResponseException ex && ex.getStatusCode().is5xxServerError())) + .onRetryExhaustedThrow( + (retryBackoffSpec, retrySignal) -> new AgentConnectionException("agent connection failed", + retrySignal.failure())); + + /** + * fetch agent public key + * + * @param agent agent + */ + public void fetchAgentPubKey(AgentInstallEntity agent) { + String url = String.format(Locale.getDefault(), AGENT_PUB_KEY_URL, agent.getAgentIp(), agent.getAgentPort()); + webClient.get() + .uri(url) + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .onStatus(HttpStatusCode::isError, response -> handleHealthHttpError(response, agent)) + .bodyToMono(String.class) + .timeout(Duration.ofSeconds(2)) + .retryWhen(retryPolicy) + .doOnNext(agent::setPublicKey) + .doOnError(error -> log.error("Failed to fetch public key from {}:{} - {}", agent.getAgentIp(), + agent.getAgentPort(), error.getMessage())) + .then() + .block(); + } + + /** + * check agent health + * + * @param agent agent install + * @return health status + */ + public String checkAgentHealth(AgentInstallEntity agent) { + if (agent == null) { + throw new IllegalArgumentException("Agent entity cannot be null"); + } + String url = String.format(Locale.getDefault(), AGENT_HEALTH_URL, agent.getAgentIp(), agent.getAgentPort()); + return webClient.get() + .uri(url) + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .onStatus(HttpStatusCode::isError, response -> handleHealthHttpError(response, agent)) + .bodyToMono(String.class) + .timeout(Duration.ofSeconds(2)) + .retryWhen(retryPolicy) + .doOnNext( + response -> log.debug("Agent health check successful for {}:{} - Response: {}", agent.getAgentIp(), + agent.getAgentPort(), truncateResponse(response))) + .doOnError(error -> logHealthError(agent, error)) + .block(); + } + + private String truncateResponse(String response) { + if (response == null) { + return "null"; + } + return response.length() > 100 ? response.substring(0, 100) + "..." : response; + } + + private void logHealthError(AgentInstallEntity agent, Throwable error) { + String errorMessage = error.getMessage(); + if (error instanceof AgentConnectionException) { + errorMessage = error.getMessage(); + } + log.error("Agent health check failed for {}:{} - Reason: {}", agent.getAgentIp(), agent.getAgentPort(), + errorMessage); + } + + private Mono handleHealthHttpError(ClientResponse response, AgentInstallEntity agent) { + return response.bodyToMono(String.class).defaultIfEmpty("No error details").flatMap(body -> { + String errorMsg = String.format(Locale.getDefault(), "agent connection failed for %s:%s - HTTP %d: %s", + agent.getAgentIp(), agent.getInstallUser(), response.statusCode().value(), body); + return Mono.error(new AgentConnectionException(errorMsg)); + }); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/AgentTaskManager.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/AgentTaskManager.java new file mode 100644 index 000000000..7d69a17ba --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/AgentTaskManager.java @@ -0,0 +1,368 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; +import reactor.util.retry.Retry; + +import org.opengauss.admin.common.constant.AgentConstants; +import org.opengauss.admin.common.core.domain.entity.agent.AgentInstallEntity; +import org.opengauss.admin.common.core.domain.entity.agent.TaskInstanceEntity; +import org.opengauss.admin.common.core.domain.entity.agent.TaskMetricsDefinition; +import org.opengauss.admin.common.core.domain.entity.agent.TaskTemplateDefinition; +import org.opengauss.admin.common.core.domain.model.agent.AgentClusterVo; +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskConfig; +import org.opengauss.admin.common.core.domain.model.agent.AgentTaskParam; +import org.opengauss.admin.common.enums.agent.ObjectType; +import org.opengauss.admin.common.exception.ops.AgentTaskException; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.ChainAssert; +import org.opengauss.admin.common.utils.OpsAssert; +import org.opengauss.admin.common.utils.RsaUtils; +import org.opengauss.agent.data.ClusterOriginal; +import org.opengauss.agent.repository.DynamicTableService; +import org.opengauss.agent.repository.TaskInstanceService; +import org.opengauss.agent.repository.TaskMetricsDefinitionService; +import org.opengauss.agent.repository.TaskSchemaDefinitionService; +import org.opengauss.agent.repository.TaskTemplateDefinitionService; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; + +/** + * AgentTaskManager + * + * @author: wangchao + * @Date: 2025/4/18 15:42 + * @Description: AgentTaskManager + * @since 7.0.0-RC2 + **/ +@Slf4j +@Service +public class AgentTaskManager { + private static final String AGENT_TASK_START_URL = "http://%s:%d/agent/task/start"; + + @Resource + private TaskInstanceService taskInstanceService; + @Resource + private TaskSchemaDefinitionService taskSchemaDefinitionService; + @Resource + private TaskMetricsDefinitionService taskMetricsDefinitionService; + @Resource + private TaskTemplateDefinitionService taskTemplateDefinitionService; + @Resource + private IAgentHeartbeatService agentHeartbeatService; + @Resource + private DynamicTableService dynamicTableService; + @Resource + private WebClient webClient; + @Resource + private AgentHttpProxy agentHttpProxy; + + /** + * create task + * + * @param agentTaskParam agent task param + * @return task id + */ + @Transactional(rollbackFor = Exception.class) + public Long createTask(AgentTaskParam agentTaskParam) { + validateInputParameters(agentTaskParam); + TaskTemplateDefinition taskTemplateDefinition = fetchTaskTemplate(agentTaskParam.getTaskTemplateId()); + validateBusinessRules(agentTaskParam, taskTemplateDefinition); + TaskInstanceEntity taskInstanceEntity = agentTaskParam.toTaskInstanceEntity(); + taskInstanceService.save(taskInstanceEntity); + log.info("create agent task success, task id: {}", taskInstanceEntity.getId()); + return taskInstanceEntity.getId(); + } + + /** + * query agent task instance + * + * @param agentId agent id + */ + public void cleanAgentTaskInstance(String agentId) { + int count = taskInstanceService.removeAgentAllTask(agentId); + log.info("clean agent task instance success, agent id: {}, count: {}", agentId, count); + } + + /** + * query agent task instance config + * + * @param agentTaskId agent task instance id + * @return agent task instance config + */ + public AgentTaskConfig queryAgentTaskInstance(Long agentTaskId) { + TaskInstanceEntity agentTask = Optional.of(taskInstanceService.getById(agentTaskId)) + .orElseThrow(() -> new OpsException("task id not found")); + return buildAgentTaskConfig(agentTask); + } + + /** + *
+     * build agent task instance
+     * 1. transfer task entity to agent task instance
+     * 2. fetch task template definition
+     * 3. fetch cluster config, if the task template definition is DB type. this contains cluster configuration
+     * information.
+     * 4. fetch schema definition list,this contains collect metric list
+     * 5. fetch metrics definition list, this contains collect metric definition list
+     * 
+ * + * @param agentTask agent task entity + * @return agent task instance + */ + private AgentTaskConfig buildAgentTaskConfig(TaskInstanceEntity agentTask) { + AgentTaskConfig config = new AgentTaskConfig(fetchTaskTemplate(agentTask.getTaskTemplateId())); + config.setTaskId(agentTask.getId()); + config.setTaskName(agentTask.getTaskName()); + config.setAgentId(agentTask.getAgentId()); + if (config.isDbTemplate()) { + config.setClusterConfig(fetchClusterConfig(agentTask)); + } + List collectorMetricDetails = fetchSchemaDefinitionList(config.getCollectMetric()); + config.setCollectorMetricDetails(collectorMetricDetails); + config.setMetricsDefinitionList(fetchMetricsDefinitionList(collectorMetricDetails)); + return config; + } + + private AgentClusterVo fetchClusterConfig(TaskInstanceEntity task) { + ClusterOriginal clusterOriginal = safeParseClusterOriginal(task.getClusterOriginal()); + ChainAssert.of(clusterOriginal).nonNull("Invalid cluster original: " + task.getClusterOriginal()); + ChainAssert.of(task.getClusterNodeId()).nonNull("Cluster ID cannot be empty"); + return clusterOriginal.load(task.getClusterNodeId()); + } + + private void refreshTaskStatusToError(List taskIds, String errorMessage) { + taskInstanceService.batchUpdateTaskFailedStatus(taskIds, errorMessage); + } + + private boolean checkAgentStatus(String agentId) { + return agentHeartbeatService.isAgentOnline(agentId); + } + + private void refreshTaskStatusToRunning(Long taskId) { + taskInstanceService.startTask(taskId); + } + + private void sendStartTaskRequest(AgentInstallEntity agent, AgentTaskConfig taskConfig) { + String url = String.format(Locale.getDefault(), AGENT_TASK_START_URL, agent.getAgentIp(), agent.getAgentPort()); + webClient.post() + .uri(url) + .accept(MediaType.ALL) + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(taskConfig) + .retrieve() + .bodyToMono(Void.class) + .doOnSuccess(v -> { + refreshTaskStatusToRunning(taskConfig.getTaskId()); + log.info("task request {}:{} send successfully", taskConfig.getTaskName(), taskConfig.getTaskId()); + }) + .onErrorResume(WebClientResponseException.class, ex -> { + log.error("API error (status {}): {}", ex.getStatusCode(), ex.getResponseBodyAsString()); + refreshTaskStatusToError(Collections.singletonList(taskConfig.getTaskId()), ex.getMessage()); + return Mono.error(new AgentTaskException("task request send failed: " + ex.getStatusCode())); + }) + .onErrorResume(WebClientRequestException.class, ex -> { + log.error("Network error: {}", ex.getMessage(), ex); + refreshTaskStatusToError(Collections.singletonList(taskConfig.getTaskId()), ex.getMessage()); + return Mono.error(new AgentTaskException("Network connection failed: " + ex.getMessage())); + }) + .onErrorResume(TimeoutException.class, ex -> { + log.error("Request timed out", ex); + refreshTaskStatusToError(Collections.singletonList(taskConfig.getTaskId()), ex.getMessage()); + return Mono.error(new AgentTaskException("Request timed out")); + }) + .onErrorResume(ex -> { + log.error("Unexpected error: {}", ex.getMessage(), ex); + refreshTaskStatusToError(Collections.singletonList(taskConfig.getTaskId()), ex.getMessage()); + return Mono.error(new AgentTaskException("Unexpected error: " + ex.getMessage())); + }) + .retryWhen(Retry.backoff(3, Duration.ofSeconds(1)) + .filter(throwable -> throwable instanceof WebClientRequestException + || throwable instanceof TimeoutException)) + .subscribe(success -> { + refreshTaskStatusToRunning(taskConfig.getTaskId()); + log.info("task request {}:{} send successfully", taskConfig.getTaskName(), taskConfig.getTaskId()); + }, error -> { + log.error("Unexpected error: {}", error.getMessage(), error); + Mono.error(new AgentTaskException("task request send failed")); + }, () -> log.debug("Request completed successfully")); + } + + private List fetchSchemaDefinitionList(String collectMetric) { + return taskSchemaDefinitionService.getMetricsByName(collectMetric); + } + + private List fetchMetricsDefinitionList(List collectMetric) { + return taskMetricsDefinitionService.findByNameIn(collectMetric); + } + + private void validateInputParameters(AgentTaskParam agentTaskParam) { + ChainAssert.of(agentTaskParam) + .nonNull("Agent task parameter cannot be null") + .validateFieldNoneNull(AgentTaskParam::getAgentId, "Agent ID cannot be empty") + .validateField(AgentTaskParam::getTaskTemplateId, "Task template ID cannot be empty"); + } + + private TaskTemplateDefinition fetchTaskTemplate(String taskTemplateId) { + return Optional.of(taskTemplateDefinitionService.getById(taskTemplateId)) + .orElseThrow(() -> new OpsException("Task template not found for ID: " + taskTemplateId)); + } + + private void validateBusinessRules(AgentTaskParam agentTaskParam, TaskTemplateDefinition template) { + if (template.getOperateObjType() == ObjectType.DB) { + ChainAssert.of(agentTaskParam) + .validateField(AgentTaskParam::getClusterNodeId, "cluster node id cannot be empty") + .validateField(AgentTaskParam::getClusterOriginal, "cluster original cannot be empty"); + ClusterOriginal original = safeParseClusterOriginal(agentTaskParam.getClusterOriginal()); + ChainAssert.of(original).nonNull("Invalid cluster original: " + agentTaskParam.getClusterOriginal()); + AgentClusterVo agentClusterVo = original.load(agentTaskParam.getClusterNodeId()); + ChainAssert.of(agentClusterVo).nonNull("Cluster not found"); + } + } + + private ClusterOriginal safeParseClusterOriginal(String value) { + try { + return ClusterOriginal.valueOf(value.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException e) { + throw new AgentTaskException("invalid cluster original: " + value); + } + } + + /** + * start agent task + * + * @param agent agent + * @param id task id + */ + public void startAgentTaskByTaskId(AgentInstallEntity agent, Long id) { + TaskInstanceEntity task = taskInstanceService.getById(id); + AgentTaskConfig taskConfig = buildAgentTaskConfig(task); + if (StrUtil.isNotEmpty(agent.getPublicKey())) { + AgentClusterVo clusterConfig = taskConfig.getClusterConfig(); + clusterConfig.setDbPassword(RsaUtils.encrypt(clusterConfig.getDbPassword(), agent.getPublicKey())); + taskConfig.setClusterConfig(clusterConfig); + } + sendStartTaskRequest(agent, taskConfig); + refreshTaskStatusToRunning(taskConfig.getTaskId()); + } + + /** + * start agent task + * + * @param agent agent + * @return start task count + */ + public int startAgentTask(AgentInstallEntity agent) { + agentHttpProxy.fetchAgentPubKey(agent); + List list = taskInstanceService.queryAgentTaskInstance(agent.getAgentId()); + for (TaskInstanceEntity task : list) { + try { + AgentTaskConfig taskConfig = buildAgentTaskConfig(task); + startAgentTask(agent, taskConfig); + } catch (OpsException e) { + log.error("start agent task failed, task id: {}", task.getId(), e); + } + } + return list.size(); + } + + /** + * start agent task + * + * @param agent agent + * @param taskConfig task config + */ + public void startAgentTask(AgentInstallEntity agent, AgentTaskConfig taskConfig) { + sendStartTaskRequest(agent, taskConfig); + refreshTaskStatusToRunning(taskConfig.getTaskId()); + } + + /** + * query all of agent task + * + * @param list list + * @return agent task map + */ + public Map> queryAllOfAgentTask(List list) { + return list.stream() + .flatMap(agent -> taskInstanceService.queryAgentTaskInstance(agent.getAgentId()) + .stream() + .map(task -> buildAgentTaskConfig(task))) + .collect(Collectors.groupingBy(AgentTaskConfig::getAgentId)); + } + + /** + * initialize pipe task data storage table + */ + @PostConstruct + public void initializedPipeDataStorageTable() { + List pipeTemplateList = taskTemplateDefinitionService.queryPipeTaskTemplateDefinition(); + for (TaskTemplateDefinition template : pipeTemplateList) { + if (dynamicTableService.checkTemplateTableExist(template.getName(), template.getStoragePolicy())) { + continue; + } else { + List collectorMetricDetails = fetchSchemaDefinitionList(template.getCollectMetric()); + if (CollUtil.isEmpty(collectorMetricDetails)) { + log.warn("template {} has no collector metric details", template.getName()); + continue; + } + List columnList = fetchMetricsDefinitionList(collectorMetricDetails); + dynamicTableService.createPipeDataStorageTable(template, columnList); + } + } + } + + /** + * query host metrics definition + * + * @return host metrics definition + */ + public List queryHostMetricsDefinition() { + List collectorMetricDetails = fetchSchemaDefinitionList( + AgentConstants.TaskTemplate.HOST_DYNAMIC_METRICS); + OpsAssert.isTrue(CollUtil.isNotEmpty(collectorMetricDetails), "metric definition is not found"); + return fetchMetricsDefinitionList(collectorMetricDetails); + } + + /** + * delete agent task by cluster node id + * + * @param clusterNodeId cluster node id + */ + public void delAgentTaskByClusterId(String clusterNodeId) { + taskInstanceService.removeAgentTaskByClusterNodeId(clusterNodeId); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/IAgentHeartbeatService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/IAgentHeartbeatService.java new file mode 100644 index 000000000..8ffa2eb5b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/IAgentHeartbeatService.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service; + +import org.opengauss.admin.common.core.domain.model.agent.HeartbeatReport; + +/** + * IAgentHeartbeatService + * + * @author: wangchao + * @Date: 2025/4/14 11:01 + * @Description: IAgentHeartbeatService + * @since 7.0.0-RC2 + **/ +public interface IAgentHeartbeatService { + /** + * Start the heartbeat service + */ + void startHeartbeatService(); + + /** + * Check if the agent is online + * + * @param agentId agent id + * @return true if the agent is online, otherwise false + */ + boolean isAgentOnline(String agentId); + + /** + * Agent heartbeat + * + * @param heart heartbeat information + */ + + void receiveHeartbeat(HeartbeatReport heart); + + /** + * Agent deregister + * + * @param requestDown deregister information + */ + void deregister(HeartbeatReport requestDown); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/IAgentInstallService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/IAgentInstallService.java new file mode 100644 index 000000000..79da75d2b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/IAgentInstallService.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service; + +import com.baomidou.mybatisplus.extension.service.IService; + +import org.opengauss.admin.common.core.domain.entity.agent.AgentInstallEntity; +import org.opengauss.admin.common.core.domain.model.agent.AgentStartResult; +import org.opengauss.admin.common.enums.agent.AgentStatus; + +import java.util.List; +import java.util.Map; + +/** + * IAgentInstallService + * + * @author: wangchao + * @Date: 2025/4/10 09:30 + * @Description: IAgentInstallService + * @since 7.0.0-RC2 + **/ +public interface IAgentInstallService extends IService { + /** + * get agent by agentId + * + * @param agentId agentId + * @return AgentInstallEntity + */ + AgentInstallEntity getByAgentId(String agentId); + + /** + * get agent by userId ,used for Host User delete checked + * + * @param userId userId + * @return List + */ + List listByUserId(String userId); + + /** + * delete agent by agentId + * + * @param agentId agentId + */ + void removeByAgentId(String agentId); + + /** + * install agent + * + * @param agent agent + */ + void installAgent(AgentInstallEntity agent); + + /** + * uninstall agent + * + * @param agentId agentId + */ + void uninstallAgent(String agentId); + + /** + * start agent + * + * @param agentId agentId + * @return agent start result + */ + AgentStartResult startAgent(String agentId); + + /** + * update agent port + * + * @param agentId agentId + * @param port port + */ + void updateAgentPort(String agentId, Integer port); + + /** + * stop agent + * + * @param agentId agentId + */ + void stopAgent(String agentId); + + /** + * update agent + * + * @param agentId agentId + */ + void updateAgent(String agentId); + + /** + * refresh agent status + * + * @param agentId agent id + * @param status agnet status + */ + void refreshAgentStatus(String agentId, AgentStatus status); + + /** + * start all of agents + * + * @return List + */ + List startAllOfAgent(); + + /** + * query host agent status + * + * @param hostId hostId + * @return String + */ + AgentStatus getAgentStatus(String hostId); + + /** + * query agent status + * + * @return Map + */ + Map queryAgentStatus(); + + /** + * update agent status + * + * @param agentId agentId + * @param status status + */ + void updateAgentStatus(String agentId, AgentStatus status); + + /** + * query agent install info + * + * @return Map + */ + Map queryAgentInstallInfo(); + + /** + * check agent install info + * + * @param hostId hostId + * @return boolean + */ + boolean hasInstall(String hostId); + + /** + * count agent install info by hostUserId + * + * @param hostUserId hostUserId + * @return int + */ + long countByHostUserId(String hostUserId); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/IAgentServerReceiveService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/IAgentServerReceiveService.java new file mode 100644 index 000000000..ccebe82e7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/IAgentServerReceiveService.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service; + +import org.opengauss.admin.common.core.domain.model.agent.HostBaseInfo; +import org.opengauss.agent.data.CustomMetricData; + +import java.util.List; +import java.util.Map; + +/** + * IAgentServerReceiveService + * + * @author: wangchao + * @Date: 2025/4/10 09:30 + * @Description: IAgentInstallService + * @since 7.0.0-RC2 + **/ +public interface IAgentServerReceiveService { + /** + * refreshBaseHostFixedInfo + * + * @param hostBaseInfo hostBaseInfo + */ + void refreshBaseHostFixedInfo(HostBaseInfo hostBaseInfo); + + /** + * receiveMetricData + * + * @param taskIds task ids + * @param agentId agent id + * @param metricDataList metric data list + */ + void receiveMetricData(List taskIds, Long agentId, List metricDataList); + + /** + * receivePipelineData + * + * @param taskId taskId + * @param agentId agentId + * @param dataList dataList + */ + void receivePipelineData(Long taskId, Long agentId, List> dataList); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/IAgentServerService.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/IAgentServerService.java new file mode 100644 index 000000000..acf10af4c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/service/IAgentServerService.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.service; + +import java.util.Map; + +/** + * IAgentServerService + * + * @author: wangchao + * @Date: 2025/4/10 09:30 + * @Description: IAgentInstallService + * @since 7.0.0-RC2 + **/ +public interface IAgentServerService { + /** + *
+     * agent anomaly checking , check items:
+     * 1. check agent server_install_record
+     * 2. check agent server_status
+     * 3. check agent java_version
+     * 4. check agent agent_port
+     * 5. check agent install_path
+     * 6. check agent install_path is empty
+     * 7. check agent install_path is writeable
+     * 8. check agent agent_status
+     * 
+ * + * @param agentId agentId + * @return result + */ + Map anomalyChecking(String agentId); + + /** + * task callback start + * + * @param agentId agentId + */ + void taskCallbackStarted(String agentId); + + /** + * start task by taskId, send task to agent proxy + * + * @param id taskId + */ + void startAgentTaskByTaskId(Long id); + + /** + *
+     *     check agent install and online
+     * 
+ * + * @param hostId hostId + * @return true: install and online, false: not install or not online + */ + boolean checkClusterNodeAgentInstallAndOnline(String hostId); +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/utils/AgentTaskUtils.java b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/utils/AgentTaskUtils.java new file mode 100644 index 000000000..42c85824b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/java/org/opengauss/agent/utils/AgentTaskUtils.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.agent.utils; + +import static org.opengauss.admin.common.constant.AgentConstants.Default.NONE; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; + +import org.apache.commons.codec.digest.MurmurHash3; +import org.opengauss.admin.common.constant.AgentConstants; +import org.opengauss.admin.common.enums.ops.DbTypeEnum; + +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Objects; + +/** + * AgentTaskUtils + * + * @author: wangchao + * @Date: 2025/6/21 14:51 + * @since 7.0.0-RC2 + **/ +public class AgentTaskUtils { + private static final String TASK_NAME_TEMPLATE = "task_%s"; + + /** + * Generate a task name based on the task template name. + * + * @param taskTemplateName task template name + * @return generated task name + */ + public static String generateTaskName(String taskTemplateName) { + return String.format(TASK_NAME_TEMPLATE, taskTemplateName); + } + + /** + * Get the task template ID based on the database type. + * + * @param dbTypeEnum database type + * @return task template ID + */ + public static String getDbTaskTemplateId(DbTypeEnum dbTypeEnum) { + return Objects.equals(dbTypeEnum, DbTypeEnum.MYSQL) + ? AgentConstants.TaskTemplate.MYSQL_MONITOR_TASK_TEMPLATE + : AgentConstants.TaskTemplate.OPENGAUSS_MONITOR_TASK_TEMPLATE; + } + + /** + * Calculate the primary ID of a row based on the task ID, agent ID, cluster node ID, and row index. + * + * @param taskId task ID + * @param agentId agent id + * @param clusterNodeId cluster node id + * @param rowIdx row index + * @return primary ID of the row + */ + public static long calcRowPrimaryId(Long taskId, String agentId, String clusterNodeId, long rowIdx) { + return MurmurHash3.hash32x86((taskId + agentId + clusterNodeId + rowIdx).getBytes(StandardCharsets.UTF_8)); + } + + /** + * Get the property value based on the metric name, attribute value map, and property key map. + * + * @param metricName metric name + * @param attrValueMap attribute value map + * @param propertyKeyMap property key map + * @return property value + */ + public static String getProperty(String metricName, Map attrValueMap, + Map propertyKeyMap) { + if (CollUtil.isEmpty(attrValueMap) || CollUtil.isEmpty(propertyKeyMap)) { + return NONE; + } + String key = propertyKeyMap.getOrDefault(metricName, AgentConstants.Default.EMPTY); + if (StrUtil.equals(key, AgentConstants.Default.EMPTY)) { + return NONE; + } + return attrValueMap.getOrDefault(key, NONE).toString(); + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/ops/.gitkeep b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/ops/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/ops/OpsHostMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/ops/OpsHostMapper.xml new file mode 100644 index 000000000..b50a779f0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/ops/OpsHostMapper.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/ops/OpsHostTagMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/ops/OpsHostTagMapper.xml new file mode 100644 index 000000000..66a21d319 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/ops/OpsHostTagMapper.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysLogConfigMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysLogConfigMapper.xml new file mode 100644 index 000000000..8997ce916 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysLogConfigMapper.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysMenuMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 000000000..366463dfb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysOperLogMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysOperLogMapper.xml new file mode 100644 index 000000000..ac60ebfcd --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginConfigDataMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginConfigDataMapper.xml new file mode 100644 index 000000000..67c098274 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginConfigDataMapper.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginConfigMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginConfigMapper.xml new file mode 100644 index 000000000..a4951d6cc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginConfigMapper.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginLogoMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginLogoMapper.xml new file mode 100644 index 000000000..98925de52 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginLogoMapper.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginMapper.xml new file mode 100644 index 000000000..57d6d430e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginMapper.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + SELECT + id,plugin_id,bootstrap_class,plugin_desc,logo_path,plugin_type,plugin_version, + plugin_provider,plugin_status,is_need_configured,theme,plugin_desc_en + FROM sys_plugins + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginRepositoryMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginRepositoryMapper.xml new file mode 100644 index 000000000..9faadbb68 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysPluginRepositoryMapper.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + UPDATE sys_plugins_repository + SET is_downloaded = #{isDownload} + WHERE + + plugin_id IN + + #{pluginId} + + + + 1 = 1 + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysRoleMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 000000000..ed3164a16 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + SELECT DISTINCT + r.role_id, + r.role_name, + r.role_key, + r.role_sort, + r.data_scope, + r.menu_check_strictly, + r.dept_check_strictly, + r.status, + r.del_flag, + r.create_time, + r.remark + FROM sys_role r + LEFT JOIN sys_user_role ur ON ur.role_id = r.role_id + LEFT JOIN sys_user u ON u.user_id = ur.user_id + + + + + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 000000000..8e3ac1c86 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + insert into sys_role_menu(role_id, menu_id) values + + (#{item.roleId},#{item.menuId}) + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysTaskMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysTaskMapper.xml new file mode 100644 index 000000000..30e7b981f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysTaskMapper.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + SELECT + id,task_name,task_type,exec_status,exec_params,exec_host_id,exec_progress, + create_time,finish_time,exec_time,plugin_id + FROM sys_task + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysUserMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 000000000..465abb040 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysUserRoleMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 000000000..f798ad8c1 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + delete from sys_user_role where user_id=#{userId} + + + + insert into sys_user_role(user_id, role_id) values + + (#{item.userId},#{item.roleId}) + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysWhiteListMapper.xml b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysWhiteListMapper.xml new file mode 100644 index 000000000..bcb94c5b5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-service/src/main/resources/mapper/system/SysWhiteListMapper.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + SELECT + id,title,ip_list,create_time + FROM sys_white_list + + + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/.browserslistrc b/ER-Model/openGauss-datakit/visualtool-ui/.browserslistrc new file mode 100644 index 000000000..dc3bc09a2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/.browserslistrc @@ -0,0 +1,4 @@ +> 1% +last 2 versions +not dead +not ie 11 diff --git a/ER-Model/openGauss-datakit/visualtool-ui/.editorconfig b/ER-Model/openGauss-datakit/visualtool-ui/.editorconfig new file mode 100644 index 000000000..caa0bf5ed --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/.editorconfig @@ -0,0 +1,20 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.py] +indent_style = space +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false + +[COMMIT_EDITMSG] +max_line_length = 80 diff --git a/ER-Model/openGauss-datakit/visualtool-ui/.env.development b/ER-Model/openGauss-datakit/visualtool-ui/.env.development new file mode 100644 index 000000000..5871d32ba --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/.env.development @@ -0,0 +1,14 @@ +# Development environment configuration +ENV = 'development' + +# development environment +VUE_APP_BASE_API = '/dev-api' + +# test api address +API_BASE_URL= 'http://localhost:9494' + +# Test environment port +port = 4001 + +# ws address +VUE_APP_WS_BASE_URL='localhost:9494' diff --git a/ER-Model/openGauss-datakit/visualtool-ui/.env.production b/ER-Model/openGauss-datakit/visualtool-ui/.env.production new file mode 100644 index 000000000..8b4016e62 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/.env.production @@ -0,0 +1,8 @@ +# production environment configuration +ENV = 'production' + +# environment port +port = 4001 + +# ws address +VUE_APP_WS_BASE_URL='localhost:9494' diff --git a/ER-Model/openGauss-datakit/visualtool-ui/.eslintignore b/ER-Model/openGauss-datakit/visualtool-ui/.eslintignore new file mode 100644 index 000000000..7a8eaf260 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/.eslintignore @@ -0,0 +1,6 @@ +# Ignore public/*.js +public/*.js +# Ignore src/assets directory +src/assets +# Ignore src/views/modeling/style directory +src/views/modeling/style diff --git a/ER-Model/openGauss-datakit/visualtool-ui/.eslintrc.js b/ER-Model/openGauss-datakit/visualtool-ui/.eslintrc.js new file mode 100644 index 000000000..186a2a627 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/.eslintrc.js @@ -0,0 +1,94 @@ +module.exports = { + root: true, + env: { + node: true, + 'vue/setup-compiler-macros': true + }, + 'extends': [ + 'plugin:vue/vue3-essential', + 'eslint:recommended', + '@vue/typescript/recommended' + ], + parserOptions: { + ecmaVersion: 2020 + }, + rules: { + 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'vue/multi-word-component-names': 'off', + '@typescript-eslint/ban-ts-comment': [ + 'error', + { + 'ts-ignore': 'allow-with-description', + 'minimumDescriptionLength': 10 + } + ], + '@typescript-eslint/no-explicit-any': 'off', + 'arrow-spacing': [2, { + 'before': true, + 'after': true + }], + 'block-spacing': [2, 'always'], + 'brace-style': [2, '1tbs', { + 'allowSingleLine': true + }], + 'quotes': [2, 'single', { + 'avoidEscape': true, + 'allowTemplateLiterals': true + }], + 'camelcase': [0, { + 'properties': 'always' + }], + 'comma-dangle': [2, 'never'], + 'comma-spacing': [2, { + 'before': false, + 'after': true + }], + 'comma-style': [2, 'last'], + 'jsx-quotes': [2, 'prefer-single'], + 'key-spacing': [2, { + 'beforeColon': false, + 'afterColon': true + }], + 'keyword-spacing': [2, { + 'before': true, + 'after': true + }], + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty': 'off', + 'no-empty-character-class': 2, + 'no-empty-pattern': 2, + 'no-lone-blocks': 2, + 'no-mixed-spaces-and-tabs': 2, + 'no-multi-spaces': 2, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [2, { + 'max': 1 + }], + 'require-atomic-updates': 'off', + 'semi': ['error', 'never'], + 'semi-spacing': [2, { + 'before': false, + 'after': true + }], + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'always'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': [2, { + 'words': true, + 'nonwords': false + }], + 'spaced-comment': [2, 'always', { + 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] + }], + 'template-curly-spacing': [2, 'never'], + 'object-curly-spacing': [2, 'always', { + objectsInObjects: false + }], + 'array-bracket-spacing': [2, 'never'] + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/.gitignore b/ER-Model/openGauss-datakit/visualtool-ui/.gitignore new file mode 100644 index 000000000..403adbc1e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules +/dist + + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/ER-Model/openGauss-datakit/visualtool-ui/README.md b/ER-Model/openGauss-datakit/visualtool-ui/README.md new file mode 100644 index 000000000..cd7269d5f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/README.md @@ -0,0 +1,127 @@ +# 前端工程 + +### 安装依赖 +``` +yarn +``` + +### 开发环境 +```sh +# 配置.env.development中的VUE_APP_BASE_API,默认为访问接口地址的/dev-api +# 访问本地后端服务,需修改vue.config.js的proxy的target地址 +yarn dev +``` + +### 生产构建 +``` +yarn build +``` + +### 代码校验 +``` +yarn lint +``` + +## 前端规范 + +### 开发工具 + +- 统一使用vs code开发 +- 前端依赖包安装使用yarn,镜像源使用淘宝镜像 +- 编码时tab统一为2个空格 + +### 命名规范 + +- 组件目录使用小驼峰命名方式,多单词拼接首字母写,如:userInfo +- 组件文件名使用大驼峰命名方式,多单词拼接首字母写,如:Manage.vue +- 变量名、方法名遵循驼峰命名规则:多单词拼接首字母写,如:userInfo +- 变量声明(let,const),定义常量使用const,定义变量尽量使用let + +### 使用ts es6风格进行编码 + +- 解构赋值 +- 箭头函数 +- 正确使用模块,如果模块只有一个输出值,就使用 export default,如果模块有多个输出值,就不使用 export default,export default 与普通的 export 尽量不要同时使用 +- 多个异步操作时使用Promise对象进行封装 +- 除了三目运算,if,else 等禁止简写 +- 有ts红色警告的地方需要进行处理 + +### 注释规则--先用中文,后期统一换成英文 + +- 公共组件的使用说明 +- 各组件中响应变量、重要函数或者类的说明 +- 复杂的业务逻辑处理说明 +- 已注释掉的代码要说明注释原因 +- 多重 if 判断语句需添加说明 +- 注释块必须以/**多行说明 */,单行注释使用// + +### 指令规范 + +- v-for 循环必须加上 key 属性,在整个 for 循环中 key 需要唯一 +- 避免 v-if 和 v-for 同时用在一个元素上 +- props 定义应该尽量详细 + +### 目录说明 + +- api 前端接口目录,其中interceptor.ts为统一拦截器 +- assets 静态文件如图片及公共样式,icons为svg图标,images是png图片目录,style为公共样式 +- components 公共组件目录 +- config 全局配置,主题、菜单获取方式、菜单宽度、菜单是否默认收起等 +- hooks 公共钩子方法 +- layout 布局组件 +- router 前端路由,菜单若采用了后端接口获取,此目录作为前端开发时使用的路由配置 +- store 状态管理目录,使用的是pinia +- types ts类型配置目录 +- utils 公共工具目录 +- views 页面目录,按文件夹划分,单个页面中使用的业务组件,需新建components文件夹放在其中 + +### 样式规范 + +- 界面级别vue组件使用统一dom结构 + +```html + + + + +``` + +- 组件的dom结构 + +```html + + + + +``` + +- 颜色值统一用css变量,不另外写色值,便于颜色的统一管理 +- 自定义的css变量统一在src/assets/style/global.less文件里面管理 + +### 提交规范 + +- 提交前先更新代码 +- 提交前执行yarn lint进行代码格式校验 +- 提交说明须简要描述当次提交的内容 + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/babel.config.js b/ER-Model/openGauss-datakit/visualtool-ui/babel.config.js new file mode 100644 index 000000000..e9558405f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/babel.config.js @@ -0,0 +1,5 @@ +module.exports = { + presets: [ + '@vue/cli-plugin-babel/preset' + ] +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/lib/opendesign-icons-1.0.3.tgz b/ER-Model/openGauss-datakit/visualtool-ui/lib/opendesign-icons-1.0.3.tgz new file mode 100644 index 000000000..9e302607c Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/lib/opendesign-icons-1.0.3.tgz differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/lib/opendesign2-2.0.22.tgz b/ER-Model/openGauss-datakit/visualtool-ui/lib/opendesign2-2.0.22.tgz new file mode 100644 index 000000000..a507b647a Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/lib/opendesign2-2.0.22.tgz differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/package-lock.json b/ER-Model/openGauss-datakit/visualtool-ui/package-lock.json new file mode 100644 index 000000000..056490682 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/package-lock.json @@ -0,0 +1,17487 @@ +{ + "name": "visualtool-ui", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "visualtool-ui", + "version": "1.0.0", + "dependencies": { + "@antv/x6": "1.32.10", + "@antv/x6-vue-shape": "1.4.2", + "@arco-design/web-vue": "2.41.1", + "@computing/opendesign-icons": "file:lib\\opendesign-icons-1.0.3.tgz", + "@computing/opendesign2": "file:lib\\opendesign2-2.0.22.tgz", + "@highlightjs/vue-plugin": "2.1.0", + "@vueuse/core": "7.3.0", + "animate.css": "4.1.1", + "axios": "0.27.2", + "bootstrap-icons": "^1.11.3", + "core-js": "3.24.1", + "dayjs": "1.11.4", + "echarts": "5.2.2", + "highlight.js": "11.6.0", + "jsencrypt": "3.0.0-rc.1", + "mitt": "3.0.0", + "nprogress": "0.2.0", + "pinia": "2.0.17", + "query-string": "7.1.1", + "vue": "3.2.13", + "vue-echarts": "6.2.3", + "vue-grid-layout": "^3.0.0-beta1", + "vue-i18n": "^9.3.0-beta.6", + "vue-router": "4.0.3", + "vue-winbox": "^0.1.0", + "wujie-polyfill": "^1.1.2", + "wujie-vue3": "^1.0.0", + "xlsx": "^0.18.5", + "xterm": "5.1.0", + "xterm-addon-attach": "0.7.0", + "xterm-addon-fit": "0.7.0" + }, + "devDependencies": { + "@computing/opendesign-icons": "file:lib\\opendesign-icons-1.0.3.tgz", + "@computing/opendesign2": "file:lib\\opendesign2-2.0.22.tgz", + "@types/nprogress": "0.2.0", + "@typescript-eslint/eslint-plugin": "5.4.0", + "@typescript-eslint/parser": "5.4.0", + "@vue/cli-plugin-babel": "~5.0.0", + "@vue/cli-plugin-eslint": "~5.0.0", + "@vue/cli-plugin-router": "~5.0.0", + "@vue/cli-plugin-typescript": "~5.0.0", + "@vue/cli-service": "~5.0.0", + "@vue/eslint-config-typescript": "9.1.0", + "eslint": "7.32.0", + "eslint-plugin-vue": "8.0.3", + "husky": "1.3.1", + "less": "4.0.0", + "less-loader": "8.0.0", + "lint-staged": "^15.2.11", + "sass": "^1.77.2", + "sass-loader": "^14.2.1", + "svg-sprite-loader": "6.0.11", + "typescript": "4.9" + } + }, + "node_modules/@achrinza/node-ipc": { + "version": "9.2.9", + "resolved": "https://registry.npmmirror.com/@achrinza/node-ipc/-/node-ipc-9.2.9.tgz", + "integrity": "sha512-7s0VcTwiK/0tNOVdSX9FWMeFdOEcsAOz9HesBldXxFMaGvIak7KC2z9tV9EgsQXn6KUsWsfIkViMNuIo0GoZDQ==", + "dev": true, + "dependencies": { + "@node-ipc/js-queue": "2.0.3", + "event-pubsub": "4.3.0", + "js-message": "1.0.7" + }, + "engines": { + "node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21 || 22" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@antv/x6": { + "version": "1.32.10", + "resolved": "https://registry.npmmirror.com/@antv/x6/-/x6-1.32.10.tgz", + "integrity": "sha512-qlTWNJoGdH6VDvG6t5zo3SUhH5yt+cdxfKPWn0EGH36roGt14RGJBn/JHe7Oe2E2TOxVBu8cMyzlKFI8xkyGMA==", + "dependencies": { + "csstype": "^3.0.3", + "jquery": "^3.5.1", + "jquery-mousewheel": "^3.1.13", + "lodash-es": "^4.17.15", + "mousetrap": "^1.6.5", + "utility-types": "^3.10.0" + } + }, + "node_modules/@antv/x6-vue-shape": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/@antv/x6-vue-shape/-/x6-vue-shape-1.4.2.tgz", + "integrity": "sha512-2V0MjZ/rE2xIURNzl54I5OywcbbsbgLXfG7ko/69WRRbISTbSdYQRfOPseO19KIpHHZlGj00HIDad0JyHnN6vQ==", + "dependencies": { + "vue-demi": "latest" + }, + "peerDependencies": { + "@antv/x6": ">=1.0.0", + "@vue/composition-api": "^1.0.0-rc.6", + "vue": "^2.6.12 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@antv/x6-vue-shape/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@arco-design/color": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/@arco-design/color/-/color-0.4.0.tgz", + "integrity": "sha512-s7p9MSwJgHeL8DwcATaXvWT3m2SigKpxx4JA1BGPHL4gfvaQsmQfrLBDpjOJFJuJ2jG2dMt3R3P8Pm9E65q18g==", + "dependencies": { + "color": "^3.1.3" + } + }, + "node_modules/@arco-design/web-vue": { + "version": "2.41.1", + "resolved": "https://registry.npmmirror.com/@arco-design/web-vue/-/web-vue-2.41.1.tgz", + "integrity": "sha512-Srs/VyjLToWprdy9+DAIjAtgbGdWuGFYg543RjahqbNPL4zrny6YOmGktzhwtENYlpBpXxZ1GnJ6D9sHqY6BnQ==", + "dependencies": { + "@arco-design/color": "^0.4.0", + "b-tween": "^0.3.3", + "b-validate": "^1.3.1", + "compute-scroll-into-view": "^1.0.17", + "dayjs": "^1.10.3", + "number-precision": "^1.5.0", + "resize-observer-polyfill": "^1.5.1", + "scroll-into-view-if-needed": "^2.2.28" + }, + "peerDependencies": { + "vue": "^3.1.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", + "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.27.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.10" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", + "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.9", + "resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.25.9.tgz", + "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", + "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", + "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", + "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", + "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", + "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", + "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", + "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", + "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", + "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", + "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", + "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", + "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.0.tgz", + "integrity": "sha512-LOAozRVbqxEVjSKfhGnuLoE4Kz4Oc5UJzuvFUhSsQzdCdaAQu06mG8zDv2GFSerM62nImUZ7K92vxnQcLSDlCQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.0.tgz", + "integrity": "sha512-dGopk9nZrtCs2+nfIem25UuHyt5moSJamArzIoh9/vezUQPmYDOzjaHDCkAzuGJibCIkPup8rMT2+wYB6S73cA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/preset-env/-/preset-env-7.28.0.tgz", + "integrity": "sha512-VmaxeGOwuDqzLl5JUkIRM1X2Qu2uKGxHEQWh+cvvbl7JuJRgKGJSfsEF/bUaxFhJl/XAyxBe7q7qSuTbKFuCyg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.0", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.0", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.27.1", + "@babel/plugin-transform-classes": "^7.28.0", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.0", + "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.0", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "core-js-compat": "^3.43.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmmirror.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.0.tgz", + "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@computing/opendesign-icons": { + "version": "1.0.3", + "resolved": "file:lib/opendesign-icons-1.0.3.tgz", + "integrity": "sha512-fPc0ktjXFcDg2SODP8DIJaa0fHqsTXeR7FdmddaazPSVLE86f6vI7S0y0AucCj7r6EET+Vv4UpMzxmcweP6Asw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@computing/opendesign2": { + "version": "2.0.22", + "resolved": "file:lib/opendesign2-2.0.22.tgz", + "integrity": "sha512-cNmMsXIUKWeh8qT4pRWYwapOKRRBF7HYTDqcY4XQoWHC6XhDAaxZL9zAXmZmIlJK1ebJg+n4qWUlFxt/G4Vn2w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "element-plus": ">=2.6.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmmirror.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@element-plus/icons-vue": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz", + "integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==", + "dev": true, + "peer": true, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.2", + "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.2.tgz", + "integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==", + "dev": true, + "peer": true, + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.2", + "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.2.tgz", + "integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==", + "dev": true, + "peer": true, + "dependencies": { + "@floating-ui/core": "^1.7.2", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "dev": true, + "peer": true + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmmirror.com/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@highlightjs/vue-plugin": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/@highlightjs/vue-plugin/-/vue-plugin-2.1.0.tgz", + "integrity": "sha512-E+bmk4ncca+hBEYRV2a+1aIzIV0VSY/e5ArjpuSN9IO7wBJrzUE2u4ESCwrbQD7sAy+jWQjkV5qCCWgc+pu7CQ==", + "peerDependencies": { + "highlight.js": "^11.0.1", + "vue": "^3" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@interactjs/actions": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/actions/-/actions-1.10.27.tgz", + "integrity": "sha512-FCRg5KwB+stkPcAMx/Cn0fgGP6p4LyMX9S/Upcn/W+hpYme31bPi54PCqmOebzz6myTthN6zFf9jMyLOqtI/gg==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/auto-scroll": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/auto-scroll/-/auto-scroll-1.10.27.tgz", + "integrity": "sha512-zPg5TnVsZv+9Hnt4qnbxLvBMf+rIWHkoJVoSETEbLNaj90C8hIyr0pVwukSUySSgDhCgQ7np0f3pg4INLq9beQ==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/auto-start": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/auto-start/-/auto-start-1.10.27.tgz", + "integrity": "sha512-ECLBO/nxmaF1knncJKIE5F7la3KKRgEkn0Cu2JTPOYj9xy/LpfYElo3wkRHsodgOqF651nR70GK2/IzPR2lO9A==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/core": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/core/-/core-1.10.27.tgz", + "integrity": "sha512-SliUr/3ZbLAdED8LokzYzWHWMdCB5Cq+UnpXuRy+BIod1j97m4IUFf/D1iIKUBBjBcucgXbz28z96WnenVCB7Q==", + "peerDependencies": { + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/dev-tools": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/dev-tools/-/dev-tools-1.10.27.tgz", + "integrity": "sha512-YolmBwRaKH1gWbvyLeV3m5QSwtD38lOZnCBA87PCAlcd9PQAC2gb03fEPeEyD336bE20oLB8f0WZt4Wre+afiw==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27", + "vue": "3" + }, + "peerDependencies": { + "@interactjs/modifiers": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/inertia": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/inertia/-/inertia-1.10.27.tgz", + "integrity": "sha512-S/SVj/M0D+wWWPVXHcXN/YUWOK51LFJsEA+CTgVnFhlSU04+1FUvNLwilCZcHgECu1RJxZNKDwZysDATg+r8jQ==", + "dependencies": { + "@interactjs/offset": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/modifiers": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/interact": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/interact/-/interact-1.10.27.tgz", + "integrity": "sha512-XdH3A2UUzjEFGGJgFuJlhiz99tE8jB8xNh/DmnoMuL6uOQPxNA+sWRnzEVjG0+zY2P3/dbhEpi4Cn3FLPzydwA==", + "dependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/interactjs": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/interactjs/-/interactjs-1.10.27.tgz", + "integrity": "sha512-UwhfUZMZVXUY72efPABuKSBz1sUY+r+49v8t6Ku9o5Jq76AKg9mwmdGszIlOn3ppnFDDjvtzK/8TL+Sbd0EQEA==", + "dependencies": { + "@interactjs/actions": "1.10.27", + "@interactjs/auto-scroll": "1.10.27", + "@interactjs/auto-start": "1.10.27", + "@interactjs/core": "1.10.27", + "@interactjs/dev-tools": "1.10.27", + "@interactjs/inertia": "1.10.27", + "@interactjs/interact": "1.10.27", + "@interactjs/modifiers": "1.10.27", + "@interactjs/offset": "1.10.27", + "@interactjs/pointer-events": "1.10.27", + "@interactjs/reflow": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/modifiers": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/modifiers/-/modifiers-1.10.27.tgz", + "integrity": "sha512-ei/qfoQ+9/8k6WzNzdNqHI6cWkIV576N4Ap16r5CoqOWwhA6Xzj3OMHf1g0t1O4eSq2HdJsVJn3eLNfw9HsbeQ==", + "dependencies": { + "@interactjs/snappers": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/offset": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/offset/-/offset-1.10.27.tgz", + "integrity": "sha512-AezsLiuK+Qv4jXdYuRa65HJ2pMFMZPlqiAep6ZRLwhP9HE7O75c0EAm+gfx+dpPrHNHs6J9LaiKSZl+B+A2qAw==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/pointer-events": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/pointer-events/-/pointer-events-1.10.27.tgz", + "integrity": "sha512-Yo5SS6PhWfC93gHNxnwwW0wvebo5hSYJKGaSnAHO4f9Lh25yibecMnmPBmiEfWVcdMboK/kXrme43mHQaRegVg==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/reflow": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/reflow/-/reflow-1.10.27.tgz", + "integrity": "sha512-Msm0QdYFr40oSsPFxyCR3dHN/pQx34k7QSkdN1uIsUn/drrm+YSFvrvVOu99DFOwr7gTThr5vNe06Sz4vubTSA==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/snappers": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/snappers/-/snappers-1.10.27.tgz", + "integrity": "sha512-HZLZ0XSi6HI08OmTv/HKG6AltQoaKAALLQ+KDW92utj3XSgw7oren0KsWUKPhaPg3Av7R1jFQd08s+uafqIlLw==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/utils": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/utils/-/utils-1.10.27.tgz", + "integrity": "sha512-+qfLOio2OxQqg1cXSnRaCl+N8MQDQLDS9w+aOGxH8YLAhIMyt7Asxx/46//sT8orgsi16pmlBPtngPHT9s8zKw==" + }, + "node_modules/@intlify/core-base": { + "version": "9.14.4", + "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.14.4.tgz", + "integrity": "sha512-vtZCt7NqWhKEtHa3SD/322DlgP5uR9MqWxnE0y8Q0tjDs9H5Lxhss+b5wv8rmuXRoHKLESNgw9d+EN9ybBbj9g==", + "dependencies": { + "@intlify/message-compiler": "9.14.4", + "@intlify/shared": "9.14.4" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "9.14.4", + "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.14.4.tgz", + "integrity": "sha512-vcyCLiVRN628U38c3PbahrhbbXrckrM9zpy0KZVlDk2Z0OnGwv8uQNNXP3twwGtfLsCf4gu3ci6FMIZnPaqZsw==", + "dependencies": { + "@intlify/shared": "9.14.4", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/shared": { + "version": "9.14.4", + "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.14.4.tgz", + "integrity": "sha512-P9zv6i1WvMc9qDBWvIgKkymjY2ptIiQ065PjDv7z7fDqH3J/HBRBN5IoiR46r/ujRcU7hCuSIZWvCAFCyuOYZA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.10", + "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.10.tgz", + "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true + }, + "node_modules/@node-ipc/js-queue": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/@node-ipc/js-queue/-/js-queue-2.0.3.tgz", + "integrity": "sha512-fL1wpr8hhD5gT2dA1qifeVaoDFlQR5es8tFuKqjHX+kdOtdNHnxkVZbtIrR2rxnMFvehkjaZRNV2H/gPXlb0hw==", + "dev": true, + "dependencies": { + "easy-stack": "1.0.1" + }, + "engines": { + "node": ">=1.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true + }, + "node_modules/@popperjs/core": { + "name": "@sxzz/popperjs-es", + "version": "2.11.7", + "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", + "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==", + "dev": true, + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmmirror.com/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dev": true, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true + }, + "node_modules/@soda/friendly-errors-webpack-plugin": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz", + "integrity": "sha512-h2ooWqP8XuFqTXT+NyAFbrArzfQA7R6HTezADrvD9Re8fxMLTPPniLdqVTdDaO0eIoLaAwKT+d6w+5GeTk7Vbg==", + "dev": true, + "dependencies": { + "chalk": "^3.0.0", + "error-stack-parser": "^2.0.6", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/@soda/get-current-script": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/@soda/get-current-script/-/get-current-script-1.0.2.tgz", + "integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==", + "dev": true + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmmirror.com/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmmirror.com/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.56.12", + "resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-8.56.12.tgz", + "integrity": "sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmmirror.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.23", + "resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.23.tgz", + "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.6", + "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true + }, + "node_modules/@types/http-proxy": { + "version": "1.17.16", + "resolved": "https://registry.npmmirror.com/@types/http-proxy/-/http-proxy-1.17.16.tgz", + "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", + "dev": true, + "peer": true + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "dev": true, + "peer": true, + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "node_modules/@types/node": { + "version": "24.0.10", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-24.0.10.tgz", + "integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==", + "dev": true, + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmmirror.com/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmmirror.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@types/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/@types/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmmirror.com/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmmirror.com/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.8", + "resolved": "https://registry.npmmirror.com/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmmirror.com/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.16", + "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz", + "integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==", + "dev": true, + "peer": true + }, + "node_modules/@types/webpack-env": { + "version": "1.18.8", + "resolved": "https://registry.npmmirror.com/@types/webpack-env/-/webpack-env-1.18.8.tgz", + "integrity": "sha512-G9eAoJRMLjcvN4I08wB5I7YofOb/kaJNd5uoCMX+LbKXTPCF+ZIHuqTnFaK9Jz1rgs035f9JUPUhNFtqgucy/A==", + "dev": true + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.4.0.tgz", + "integrity": "sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "5.4.0", + "@typescript-eslint/scope-manager": "5.4.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.4.0.tgz", + "integrity": "sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/typescript-estree": "5.4.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-5.4.0.tgz", + "integrity": "sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/typescript-estree": "5.4.0", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-5.4.0.tgz", + "integrity": "sha512-pRxFjYwoi8R+n+sibjgF9iUiAELU9ihPBtHzocyW8v8D8G8KeQvXTsW7+CBYIyTYsmhtNk50QPGLE3vrvhM5KA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/visitor-keys": "5.4.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-5.4.0.tgz", + "integrity": "sha512-GjXNpmn+n1LvnttarX+sPD6+S7giO+9LxDIGlRl4wK3a7qMWALOHYuVSZpPTfEIklYjaWuMtfKdeByx0AcaThA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.4.0.tgz", + "integrity": "sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/visitor-keys": "5.4.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz", + "integrity": "sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.4.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vue/babel-helper-vue-jsx-merge-props": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz", + "integrity": "sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA==", + "dev": true + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.4.0.tgz", + "integrity": "sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==", + "dev": true + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.4.0.tgz", + "integrity": "sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.9", + "@babel/types": "^7.26.9", + "@vue/babel-helper-vue-transform-on": "1.4.0", + "@vue/babel-plugin-resolve-type": "1.4.0", + "@vue/shared": "^3.5.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.4.0.tgz", + "integrity": "sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/parser": "^7.26.9", + "@vue/compiler-sfc": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-plugin-transform-vue-jsx": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.4.0.tgz", + "integrity": "sha512-Fmastxw4MMx0vlgLS4XBX0XiBbUFzoMGeVXuMV08wyOfXdikAFqBTuYPR0tlk+XskL19EzHc39SgjrPGY23JnA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0", + "html-tags": "^2.0.0", + "lodash.kebabcase": "^4.1.1", + "svg-tags": "^1.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-preset-app": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/babel-preset-app/-/babel-preset-app-5.0.8.tgz", + "integrity": "sha512-yl+5qhpjd8e1G4cMXfORkkBlvtPCIgmRf3IYCWYDKIQ7m+PPa5iTm4feiNmCMD6yGqQWMhhK/7M3oWGL9boKwg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.16", + "@babel/helper-compilation-targets": "^7.12.16", + "@babel/helper-module-imports": "^7.12.13", + "@babel/plugin-proposal-class-properties": "^7.12.13", + "@babel/plugin-proposal-decorators": "^7.12.13", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/plugin-transform-runtime": "^7.12.15", + "@babel/preset-env": "^7.12.16", + "@babel/runtime": "^7.12.13", + "@vue/babel-plugin-jsx": "^1.0.3", + "@vue/babel-preset-jsx": "^1.1.2", + "babel-plugin-dynamic-import-node": "^2.3.3", + "core-js": "^3.8.3", + "core-js-compat": "^3.8.3", + "semver": "^7.3.4" + }, + "peerDependencies": { + "@babel/core": "*", + "core-js": "^3", + "vue": "^2 || ^3.2.13" + }, + "peerDependenciesMeta": { + "core-js": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/babel-preset-jsx": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-preset-jsx/-/babel-preset-jsx-1.4.0.tgz", + "integrity": "sha512-QmfRpssBOPZWL5xw7fOuHNifCQcNQC1PrOo/4fu6xlhlKJJKSA3HqX92Nvgyx8fqHZTUGMPHmFA+IDqwXlqkSA==", + "dev": true, + "dependencies": { + "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.4.0", + "@vue/babel-sugar-composition-api-inject-h": "^1.4.0", + "@vue/babel-sugar-composition-api-render-instance": "^1.4.0", + "@vue/babel-sugar-functional-vue": "^1.4.0", + "@vue/babel-sugar-inject-h": "^1.4.0", + "@vue/babel-sugar-v-model": "^1.4.0", + "@vue/babel-sugar-v-on": "^1.4.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0", + "vue": "*" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/babel-sugar-composition-api-inject-h": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-composition-api-inject-h/-/babel-sugar-composition-api-inject-h-1.4.0.tgz", + "integrity": "sha512-VQq6zEddJHctnG4w3TfmlVp5FzDavUSut/DwR0xVoe/mJKXyMcsIibL42wPntozITEoY90aBV0/1d2KjxHU52g==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-composition-api-render-instance": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-composition-api-render-instance/-/babel-sugar-composition-api-render-instance-1.4.0.tgz", + "integrity": "sha512-6ZDAzcxvy7VcnCjNdHJ59mwK02ZFuP5CnucloidqlZwVQv5CQLijc3lGpR7MD3TWFi78J7+a8J56YxbCtHgT9Q==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-functional-vue": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.4.0.tgz", + "integrity": "sha512-lTEB4WUFNzYt2In6JsoF9sAYVTo84wC4e+PoZWSgM6FUtqRJz7wMylaEhSRgG71YF+wfLD6cc9nqVeXN2rwBvw==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-inject-h": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.4.0.tgz", + "integrity": "sha512-muwWrPKli77uO2fFM7eA3G1lAGnERuSz2NgAxuOLzrsTlQl8W4G+wwbM4nB6iewlKbwKRae3nL03UaF5ffAPMA==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-v-model": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.4.0.tgz", + "integrity": "sha512-0t4HGgXb7WHYLBciZzN5s0Hzqan4Ue+p/3FdQdcaHAb7s5D9WZFGoSxEZHrR1TFVZlAPu1bejTKGeAzaaG3NCQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.4.0", + "camelcase": "^5.0.0", + "html-tags": "^2.0.0", + "svg-tags": "^1.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-v-on": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.4.0.tgz", + "integrity": "sha512-m+zud4wKLzSKgQrWwhqRObWzmTuyzl6vOP7024lrpeJM4x2UhQtRDLgYjXAw9xBXjCwS0pP9kXjg91F9ZNo9JA==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.4.0", + "camelcase": "^5.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/cli-overlay": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-overlay/-/cli-overlay-5.0.8.tgz", + "integrity": "sha512-KmtievE/B4kcXp6SuM2gzsnSd8WebkQpg3XaB6GmFh1BJGRqa1UiW9up7L/Q67uOdTigHxr5Ar2lZms4RcDjwQ==", + "dev": true + }, + "node_modules/@vue/cli-plugin-babel": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-babel/-/cli-plugin-babel-5.0.8.tgz", + "integrity": "sha512-a4qqkml3FAJ3auqB2kN2EMPocb/iu0ykeELwed+9B1c1nQ1HKgslKMHMPavYx3Cd/QAx2mBD4hwKBqZXEI/CsQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.16", + "@vue/babel-preset-app": "^5.0.8", + "@vue/cli-shared-utils": "^5.0.8", + "babel-loader": "^8.2.2", + "thread-loader": "^3.0.0", + "webpack": "^5.54.0" + }, + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0" + } + }, + "node_modules/@vue/cli-plugin-eslint": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-5.0.8.tgz", + "integrity": "sha512-d11+I5ONYaAPW1KyZj9GlrV/E6HZePq5L5eAF5GgoVdu6sxr6bDgEoxzhcS1Pk2eh8rn1MxG/FyyR+eCBj/CNg==", + "dev": true, + "dependencies": { + "@vue/cli-shared-utils": "^5.0.8", + "eslint-webpack-plugin": "^3.1.0", + "globby": "^11.0.2", + "webpack": "^5.54.0", + "yorkie": "^2.0.0" + }, + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0", + "eslint": ">=7.5.0" + } + }, + "node_modules/@vue/cli-plugin-router": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-router/-/cli-plugin-router-5.0.8.tgz", + "integrity": "sha512-Gmv4dsGdAsWPqVijz3Ux2OS2HkMrWi1ENj2cYL75nUeL+Xj5HEstSqdtfZ0b1q9NCce+BFB6QnHfTBXc/fCvMg==", + "dev": true, + "dependencies": { + "@vue/cli-shared-utils": "^5.0.8" + }, + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0" + } + }, + "node_modules/@vue/cli-plugin-typescript": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-typescript/-/cli-plugin-typescript-5.0.8.tgz", + "integrity": "sha512-JKJOwzJshBqsmp4yLBexwVMebOZ4VGJgbnYvmHVxasJOStF2RxwyW28ZF+zIvASGdat4sAUuo/3mAQyVhm7JHg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.16", + "@types/webpack-env": "^1.15.2", + "@vue/cli-shared-utils": "^5.0.8", + "babel-loader": "^8.2.2", + "fork-ts-checker-webpack-plugin": "^6.4.0", + "globby": "^11.0.2", + "thread-loader": "^3.0.0", + "ts-loader": "^9.2.5", + "webpack": "^5.54.0" + }, + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0", + "cache-loader": "^4.1.0", + "typescript": ">=2", + "vue": "^2 || ^3.2.13", + "vue-template-compiler": "^2.0.0" + }, + "peerDependenciesMeta": { + "cache-loader": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/@vue/cli-plugin-vuex": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.8.tgz", + "integrity": "sha512-HSYWPqrunRE5ZZs8kVwiY6oWcn95qf/OQabwLfprhdpFWAGtLStShjsGED2aDpSSeGAskQETrtR/5h7VqgIlBA==", + "dev": true, + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0" + } + }, + "node_modules/@vue/cli-service": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-service/-/cli-service-5.0.8.tgz", + "integrity": "sha512-nV7tYQLe7YsTtzFrfOMIHc5N2hp5lHG2rpYr0aNja9rNljdgcPZLyQRb2YRivTHqTv7lI962UXFURcpStHgyFw==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.12.16", + "@soda/friendly-errors-webpack-plugin": "^1.8.0", + "@soda/get-current-script": "^1.0.2", + "@types/minimist": "^1.2.0", + "@vue/cli-overlay": "^5.0.8", + "@vue/cli-plugin-router": "^5.0.8", + "@vue/cli-plugin-vuex": "^5.0.8", + "@vue/cli-shared-utils": "^5.0.8", + "@vue/component-compiler-utils": "^3.3.0", + "@vue/vue-loader-v15": "npm:vue-loader@^15.9.7", + "@vue/web-component-wrapper": "^1.3.0", + "acorn": "^8.0.5", + "acorn-walk": "^8.0.2", + "address": "^1.1.2", + "autoprefixer": "^10.2.4", + "browserslist": "^4.16.3", + "case-sensitive-paths-webpack-plugin": "^2.3.0", + "cli-highlight": "^2.1.10", + "clipboardy": "^2.3.0", + "cliui": "^7.0.4", + "copy-webpack-plugin": "^9.0.1", + "css-loader": "^6.5.0", + "css-minimizer-webpack-plugin": "^3.0.2", + "cssnano": "^5.0.0", + "debug": "^4.1.1", + "default-gateway": "^6.0.3", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "fs-extra": "^9.1.0", + "globby": "^11.0.2", + "hash-sum": "^2.0.0", + "html-webpack-plugin": "^5.1.0", + "is-file-esm": "^1.0.0", + "launch-editor-middleware": "^2.2.1", + "lodash.defaultsdeep": "^4.6.1", + "lodash.mapvalues": "^4.6.0", + "mini-css-extract-plugin": "^2.5.3", + "minimist": "^1.2.5", + "module-alias": "^2.2.2", + "portfinder": "^1.0.26", + "postcss": "^8.2.6", + "postcss-loader": "^6.1.1", + "progress-webpack-plugin": "^1.0.12", + "ssri": "^8.0.1", + "terser-webpack-plugin": "^5.1.1", + "thread-loader": "^3.0.0", + "vue-loader": "^17.0.0", + "vue-style-loader": "^4.1.3", + "webpack": "^5.54.0", + "webpack-bundle-analyzer": "^4.4.0", + "webpack-chain": "^6.5.1", + "webpack-dev-server": "^4.7.3", + "webpack-merge": "^5.7.3", + "webpack-virtual-modules": "^0.4.2", + "whatwg-fetch": "^3.6.2" + }, + "bin": { + "vue-cli-service": "bin/vue-cli-service.js" + }, + "engines": { + "node": "^12.0.0 || >= 14.0.0" + }, + "peerDependencies": { + "vue-template-compiler": "^2.0.0", + "webpack-sources": "*" + }, + "peerDependenciesMeta": { + "cache-loader": { + "optional": true + }, + "less-loader": { + "optional": true + }, + "pug-plain-loader": { + "optional": true + }, + "raw-loader": { + "optional": true + }, + "sass-loader": { + "optional": true + }, + "stylus-loader": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/@vue/cli-shared-utils": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-shared-utils/-/cli-shared-utils-5.0.8.tgz", + "integrity": "sha512-uK2YB7bBVuQhjOJF+O52P9yFMXeJVj7ozqJkwYE9PlMHL1LMHjtCYm4cSdOebuPzyP+/9p0BimM/OqxsevIopQ==", + "dev": true, + "dependencies": { + "@achrinza/node-ipc": "^9.2.5", + "chalk": "^4.1.2", + "execa": "^1.0.0", + "joi": "^17.4.0", + "launch-editor": "^2.2.1", + "lru-cache": "^6.0.0", + "node-fetch": "^2.6.7", + "open": "^8.0.2", + "ora": "^5.3.0", + "read-pkg": "^5.1.1", + "semver": "^7.3.4", + "strip-ansi": "^6.0.0" + } + }, + "node_modules/@vue/cli-shared-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@vue/cli-shared-utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vue/cli-shared-utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.17", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.17.tgz", + "integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.27.5", + "@vue/shared": "3.5.17", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.17", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz", + "integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==", + "dev": true, + "dependencies": { + "@vue/compiler-core": "3.5.17", + "@vue/shared": "3.5.17" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.17", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz", + "integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.27.5", + "@vue/compiler-core": "3.5.17", + "@vue/compiler-dom": "3.5.17", + "@vue/compiler-ssr": "3.5.17", + "@vue/shared": "3.5.17", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.17", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.17", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz", + "integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==", + "dev": true, + "dependencies": { + "@vue/compiler-dom": "3.5.17", + "@vue/shared": "3.5.17" + } + }, + "node_modules/@vue/component-compiler-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", + "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==", + "dev": true, + "dependencies": { + "consolidate": "^0.15.1", + "hash-sum": "^1.0.2", + "lru-cache": "^4.1.2", + "merge-source-map": "^1.1.0", + "postcss": "^7.0.36", + "postcss-selector-parser": "^6.0.2", + "source-map": "~0.6.1", + "vue-template-es2015-compiler": "^1.9.0" + }, + "optionalDependencies": { + "prettier": "^1.18.2 || ^2.0.0" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + }, + "node_modules/@vue/component-compiler-utils/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/@vue/component-compiler-utils/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==" + }, + "node_modules/@vue/eslint-config-typescript": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/@vue/eslint-config-typescript/-/eslint-config-typescript-9.1.0.tgz", + "integrity": "sha512-j/852/ZYQ5wDvCD3HE2q4uqJwJAceer2FwoEch1nFo+zTOsPrbzbE3cuWIs3kvu5hdFsGTMYwRwjI6fqZKDMxQ==", + "dev": true, + "dependencies": { + "vue-eslint-parser": "^8.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0", + "eslint-plugin-vue": "^8.0.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.13.tgz", + "integrity": "sha512-j3ByCiRgrr4uEZpXJM8XowrbYKeNHMHlbmMZE/2QpVzVPIfrQWS2fpLmbchJeMrnwIrzEl+dub3hgwkV4KRn4w==", + "dependencies": { + "@vue/shared": "3.2.13" + } + }, + "node_modules/@vue/reactivity/node_modules/@vue/shared": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.13.tgz", + "integrity": "sha512-F/gs3kHQ8Xeo24F6EImOvBiIoYQsBjF9qoLzvk+LHxYN6ZhIDEL1NWrBFYzdFQ7NphjEYd4EvPZ+Qee+WX8P6w==" + }, + "node_modules/@vue/ref-transform": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/ref-transform/-/ref-transform-3.2.13.tgz", + "integrity": "sha512-q6GXHZFzXjpx1K3UFRF8fa+xSmD9xV/FjhGzTNnfrryBr8tBUNYgP2f0s5K5N+21Ay7+MlQ1XXMUp8McGvsryQ==", + "dependencies": { + "@babel/parser": "^7.15.0", + "@vue/compiler-core": "3.2.13", + "@vue/shared": "3.2.13", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7" + } + }, + "node_modules/@vue/ref-transform/node_modules/@vue/compiler-core": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.13.tgz", + "integrity": "sha512-H8MUuKVCfAT6C0vth/+1LAriKnM+RTFo/5MoFycwRPwworTvkpWq/EuGoIXdLBblo8Y2/bNsOmIBEEoOtrb/bQ==", + "dependencies": { + "@babel/parser": "^7.15.0", + "@vue/shared": "3.2.13", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "node_modules/@vue/ref-transform/node_modules/@vue/shared": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.13.tgz", + "integrity": "sha512-F/gs3kHQ8Xeo24F6EImOvBiIoYQsBjF9qoLzvk+LHxYN6ZhIDEL1NWrBFYzdFQ7NphjEYd4EvPZ+Qee+WX8P6w==" + }, + "node_modules/@vue/ref-transform/node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.13.tgz", + "integrity": "sha512-VQedL9Wa7yWMPVDrIkxzLCm6cWCDBoXcXc+jrsOJkqpWhEeA7+zGOsDsHzhLH8aaJD6vdnUR5Cy0EKvoJDqEWQ==", + "dependencies": { + "@vue/reactivity": "3.2.13", + "@vue/shared": "3.2.13" + } + }, + "node_modules/@vue/runtime-core/node_modules/@vue/shared": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.13.tgz", + "integrity": "sha512-F/gs3kHQ8Xeo24F6EImOvBiIoYQsBjF9qoLzvk+LHxYN6ZhIDEL1NWrBFYzdFQ7NphjEYd4EvPZ+Qee+WX8P6w==" + }, + "node_modules/@vue/runtime-dom": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.13.tgz", + "integrity": "sha512-DVG+ItkrnCOEa9HSrmGBTLwv/gBVYCO8wkm/yv+d5ChoTnyIILxP0oCiZEPJsgWZfUSRPNi5rXozwo7F99MiwQ==", + "dependencies": { + "@vue/runtime-core": "3.2.13", + "@vue/shared": "3.2.13", + "csstype": "^2.6.8" + } + }, + "node_modules/@vue/runtime-dom/node_modules/@vue/shared": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.13.tgz", + "integrity": "sha512-F/gs3kHQ8Xeo24F6EImOvBiIoYQsBjF9qoLzvk+LHxYN6ZhIDEL1NWrBFYzdFQ7NphjEYd4EvPZ+Qee+WX8P6w==" + }, + "node_modules/@vue/runtime-dom/node_modules/csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + }, + "node_modules/@vue/server-renderer": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.13.tgz", + "integrity": "sha512-KI+JFV+vRb95+Jb6IwRRm4Vhvj8wrJTNs+OlATfqwwIRpBGAyxn/4knDJYzlnUf/mrKAkrbw751mHhi+pEwILQ==", + "dependencies": { + "@vue/compiler-ssr": "3.2.13", + "@vue/shared": "3.2.13" + }, + "peerDependencies": { + "vue": "3.2.13" + } + }, + "node_modules/@vue/server-renderer/node_modules/@vue/compiler-core": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.13.tgz", + "integrity": "sha512-H8MUuKVCfAT6C0vth/+1LAriKnM+RTFo/5MoFycwRPwworTvkpWq/EuGoIXdLBblo8Y2/bNsOmIBEEoOtrb/bQ==", + "dependencies": { + "@babel/parser": "^7.15.0", + "@vue/shared": "3.2.13", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "node_modules/@vue/server-renderer/node_modules/@vue/compiler-dom": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.13.tgz", + "integrity": "sha512-5+2dYgQyNzM97EEgbdAusUpLjulcKkvLM26jOGpd14+qwEcW/KCnns5DGjlZD/tsdEwToOoTDCm+mjx7cO/G1Q==", + "dependencies": { + "@vue/compiler-core": "3.2.13", + "@vue/shared": "3.2.13" + } + }, + "node_modules/@vue/server-renderer/node_modules/@vue/compiler-ssr": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.13.tgz", + "integrity": "sha512-ZbO6uDhUWTdKBRguYNEZXj2FU3nh1cudoHBiidbxj9q5J0tVT+j1PSVFAXPq6SquUBdJpa4HvGkQ5kQzv6upXg==", + "dependencies": { + "@vue/compiler-dom": "3.2.13", + "@vue/shared": "3.2.13" + } + }, + "node_modules/@vue/server-renderer/node_modules/@vue/shared": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.13.tgz", + "integrity": "sha512-F/gs3kHQ8Xeo24F6EImOvBiIoYQsBjF9qoLzvk+LHxYN6ZhIDEL1NWrBFYzdFQ7NphjEYd4EvPZ+Qee+WX8P6w==" + }, + "node_modules/@vue/shared": { + "version": "3.5.17", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.17.tgz", + "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==", + "dev": true + }, + "node_modules/@vue/vue-loader-v15": { + "name": "vue-loader", + "version": "15.11.1", + "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-15.11.1.tgz", + "integrity": "sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==", + "dev": true, + "dependencies": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "peerDependencies": { + "css-loader": "*", + "webpack": "^3.0.0 || ^4.1.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "cache-loader": { + "optional": true + }, + "prettier": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/@vue/vue-loader-v15/node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + }, + "node_modules/@vue/web-component-wrapper": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz", + "integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==", + "dev": true + }, + "node_modules/@vueuse/core": { + "version": "7.3.0", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-7.3.0.tgz", + "integrity": "sha512-gPJyMMAquva9Qwqz63qGQT122m5hWI8Kuy8kfPV/JLQU7m01CXooyv8FIrX9TV8OxVcHBTPXPJHY0oyUiFoNgw==", + "dependencies": { + "@vueuse/shared": "7.3.0", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.1.0", + "vue": "^2.6.0 || ^3.2.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vueuse/core/node_modules/@vueuse/shared": { + "version": "7.3.0", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-7.3.0.tgz", + "integrity": "sha512-vOAeI84tIXKVkzm8s/Mxbrzhj0QN6NyVc/sC6LrW0AjVNdvpD8sB1dZiDn9yh8T77WJmloCEt4zZVIppeq7I+w==", + "dependencies": { + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.1.0", + "vue": "^2.6.0 || ^3.2.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz", + "integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==", + "dev": true, + "peer": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz", + "integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==", + "dev": true, + "peer": true, + "dependencies": { + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "dev": true, + "hasInstallScript": true, + "peer": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/animate.css": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/animate.css/-/animate.css-4.1.1.tgz", + "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==" + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmmirror.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmmirror.com/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", + "dev": true, + "peer": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmmirror.com/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/b-tween": { + "version": "0.3.3", + "resolved": "https://registry.npmmirror.com/b-tween/-/b-tween-0.3.3.tgz", + "integrity": "sha512-oEHegcRpA7fAuc9KC4nktucuZn2aS8htymCPcP3qkEGPqiBH+GfqtqoG2l7LxHngg6O0HFM7hOeOYExl1Oz4ZA==" + }, + "node_modules/b-validate": { + "version": "1.5.3", + "resolved": "https://registry.npmmirror.com/b-validate/-/b-validate-1.5.3.tgz", + "integrity": "sha512-iCvCkGFskbaYtfQ0a3GmcQCHl/Sv1GufXFGuUQ+FE+WJa7A/espLOuFIn09B944V8/ImPj71T4+rTASxO2PAuA==" + }, + "node_modules/babel-loader": { + "version": "8.4.1", + "resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-8.4.1.tgz", + "integrity": "sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.4", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.14", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.5", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmmirror.com/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/batch-processor": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/batch-processor/-/batch-processor-1.0.0.tgz", + "integrity": "sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==" + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/bonjour-service": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/bonjour-service/-/bonjour-service-1.3.0.tgz", + "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/bootstrap-icons": { + "version": "1.13.1", + "resolved": "https://registry.npmmirror.com/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz", + "integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ] + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cache-base/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "dev": true, + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-callsite/node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "dev": true, + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001726", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", + "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmmirror.com/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/class-utils/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmmirror.com/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmmirror.com/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/clipboardy": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/clipboardy/-/clipboardy-2.3.0.tgz", + "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==", + "dev": true, + "dependencies": { + "arch": "^2.1.1", + "execa": "^1.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmmirror.com/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmmirror.com/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/compression/-/compression-1.8.0.tgz", + "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.0.2", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/compute-scroll-into-view": { + "version": "1.0.20", + "resolved": "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", + "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmmirror.com/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "deprecated": "Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog", + "dev": true, + "dependencies": { + "bluebird": "^3.1.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz", + "integrity": "sha512-rxnR7PaGigJzhqETHGmAcxKnLZSR5u1Y3/bcIv/1FnqXedcL/E2ewK7ZCNrArJKCiSv8yVXhTqetJh8inDvfsA==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^11.0.3", + "normalize-path": "^3.0.0", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/core-js": { + "version": "3.24.1", + "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.24.1.tgz", + "integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.43.0", + "resolved": "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.43.0.tgz", + "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", + "dev": true, + "dependencies": { + "browserslist": "^4.25.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.4.1", + "resolved": "https://registry.npmmirror.com/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", + "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-loader": { + "version": "6.11.0", + "resolved": "https://registry.npmmirror.com/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmmirror.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "dev": true, + "dependencies": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.15", + "resolved": "https://registry.npmmirror.com/cssnano/-/cssnano-5.1.15.tgz", + "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "dev": true, + "dependencies": { + "cssnano-preset-default": "^5.2.14", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.14", + "resolved": "https://registry.npmmirror.com/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", + "dev": true, + "dependencies": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.2", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dayjs": { + "version": "1.11.4", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.4.tgz", + "integrity": "sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g==" + }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/default-gateway/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/default-gateway/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-gateway/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-gateway/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/default-gateway/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmmirror.com/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domready": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/domready/-/domready-1.0.8.tgz", + "integrity": "sha512-uIzsOJUNk+AdGE9a6VDeessoMCzF8RrZvJCX/W8QtyfgdR6Uofn/MvRonih3OtCO79b2VDzDOymuiABrQ4z3XA==", + "dev": true + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmmirror.com/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/easy-stack": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/easy-stack/-/easy-stack-1.0.1.tgz", + "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/echarts": { + "version": "5.2.2", + "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.2.2.tgz", + "integrity": "sha512-yxuBfeIH5c+0FsoRP60w4De6omXhA06c7eUYBsC1ykB6Ys2yK5fSteIYWvkJ4xJVLQgCvAdO8C4mN6MLeJpBaw==", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.2.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.179", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.179.tgz", + "integrity": "sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==", + "dev": true + }, + "node_modules/element-plus": { + "version": "2.10.2", + "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.10.2.tgz", + "integrity": "sha512-p2KiAa0jEGXrzdlTAfpiS7HQFAhla4gvx6H7RuDf+OO0uC3DGpolxvdHjFR8gt7+vaWyxQNcHa1sAdBkmjqlgA==", + "dev": true, + "peer": true, + "dependencies": { + "@ctrl/tinycolor": "^3.4.1", + "@element-plus/icons-vue": "^2.3.1", + "@floating-ui/dom": "^1.0.1", + "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", + "@types/lodash": "^4.14.182", + "@types/lodash-es": "^4.17.6", + "@vueuse/core": "^9.1.0", + "async-validator": "^4.2.5", + "dayjs": "^1.11.13", + "escape-html": "^1.0.3", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "lodash-unified": "^1.0.2", + "memoize-one": "^6.0.0", + "normalize-wheel-es": "^1.2.0" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/element-plus/node_modules/@vueuse/core": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz", + "integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/web-bluetooth": "^0.0.16", + "@vueuse/metadata": "9.13.0", + "@vueuse/shared": "9.13.0", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/element-plus/node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "dev": true, + "hasInstallScript": true, + "peer": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/element-plus/node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true, + "peer": true + }, + "node_modules/element-resize-detector": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/element-resize-detector/-/element-resize-detector-1.2.4.tgz", + "integrity": "sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==", + "dependencies": { + "batch-processor": "1.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.2", + "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/enhanced-resolve/node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dev": true, + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-vue": { + "version": "8.0.3", + "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-8.0.3.tgz", + "integrity": "sha512-Rlhhy5ltzde0sRwSkqHuNePTXLMMaJ5+qsQubM4RYloYsQ8cXlnJT5MDaCzSirkGADipOHtmQXIbbPFAzUrADg==", + "dev": true, + "dependencies": { + "eslint-utils": "^3.0.0", + "natural-compare": "^1.4.0", + "semver": "^7.3.5", + "vue-eslint-parser": "^8.0.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "dev": true, + "dependencies": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/eslint/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-pubsub": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/event-pubsub/-/event-pubsub-4.3.0.tgz", + "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmmirror.com/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmmirror.com/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmmirror.com/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "6.5.3", + "resolved": "https://registry.npmmirror.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", + "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=10", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dev": true, + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-value/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-sum": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", + "dev": true + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/highlight.js": { + "version": "11.6.0", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.6.0.tgz", + "integrity": "sha512-ig1eqDzJaB0pqEvlPVIpSSyMaO92bH1N2rJpLMN/nX396wTpDA4Eq0uK+7I/2XG17pFaaKE0kjV/XPeGt7Evjw==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmmirror.com/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmmirror.com/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-tags": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-2.0.0.tgz", + "integrity": "sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.6.3", + "resolved": "https://registry.npmmirror.com/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", + "integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==", + "dev": true, + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/html-webpack-plugin/node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmmirror.com/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmmirror.com/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.9", + "resolved": "https://registry.npmmirror.com/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http-proxy/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/husky/-/husky-1.3.1.tgz", + "integrity": "sha512-86U6sVVVf4b5NYSZ0yvv88dRgBSSXXmHaiq5pP4KDj5JVzdwKgBjEtUPOm8hcoytezFwbU+7gotXNhpHdystlg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "cosmiconfig": "^5.0.7", + "execa": "^1.0.0", + "find-up": "^3.0.0", + "get-stdin": "^6.0.0", + "is-ci": "^2.0.0", + "pkg-dir": "^3.0.0", + "please-upgrade-node": "^3.1.1", + "read-pkg": "^4.0.1", + "run-node": "^1.0.0", + "slash": "^2.0.0" + }, + "bin": { + "husky-upgrade": "lib/upgrader/bin.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/husky/node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/husky/node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dev": true, + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/husky/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/husky/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/husky/node_modules/read-pkg": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-4.0.1.tgz", + "integrity": "sha512-+UBirHHDm5J+3WDmLBZYSklRYg82nMlz+enn+GMZ22nSR2f4bzxmhso6rzQW/3mT2PVzpzDTiYIZahk8UmZ44w==", + "dev": true, + "dependencies": { + "normalize-package-data": "^2.3.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/husky/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/husky/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", + "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz", + "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-file-esm": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-file-esm/-/is-file-esm-1.0.0.tgz", + "integrity": "sha512-rZlaNKb4Mr8WlRu2A9XdeoKgnO5aA53XdPHgCKVyCrQ/rWi89RET1+bq37Ru46obaQXeiX4vmFIm1vks41hoSA==", + "dev": true, + "dependencies": { + "read-pkg-up": "^7.0.1" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/javascript-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz", + "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", + "dev": true + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmmirror.com/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dev": true, + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmmirror.com/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" + }, + "node_modules/jquery-mousewheel": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/jquery-mousewheel/-/jquery-mousewheel-3.2.2.tgz", + "integrity": "sha512-JP71xTAg08ZY3hcs9ZbYUZ5i+dkSsz4yRl/zpWkAmtzc+kMs5EfPkpkINSidiLYMaR0MTo3DfFGF9WIezMsFQQ==", + "dependencies": { + "jquery": ">=1.2.6" + } + }, + "node_modules/js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true + }, + "node_modules/js-message": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/js-message/-/js-message-1.0.7.tgz", + "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", + "dev": true, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsencrypt": { + "version": "3.0.0-rc.1", + "resolved": "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.0.0-rc.1.tgz", + "integrity": "sha512-gcvGaqerlUJy1Kq6tNgPYteVEoWNemu+9hBe2CdsCIz4rVcwjoTQ72iD1W76/PRMlnkzG0yVh7nwOOMOOUfKmg==" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/launch-editor": { + "version": "2.10.0", + "resolved": "https://registry.npmmirror.com/launch-editor/-/launch-editor-2.10.0.tgz", + "integrity": "sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "node_modules/launch-editor-middleware": { + "version": "2.10.0", + "resolved": "https://registry.npmmirror.com/launch-editor-middleware/-/launch-editor-middleware-2.10.0.tgz", + "integrity": "sha512-RzZu7MeVlE3p1H6Sadc2BhuDGAj7bkeDCBpNq/zSENP4ohJGhso00k5+iYaRwKshIpiOAhMmimce+5D389xmSg==", + "dev": true, + "dependencies": { + "launch-editor": "^2.10.0" + } + }, + "node_modules/less": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/less/-/less-4.0.0.tgz", + "integrity": "sha512-av1eEa2D0xZfF7fjLJS/Dld7zAYSLU7EOEJvuOELeaNI3i6L/81AdjbK5/pytaRkBwi7ZEa0433IDvMLskKCOw==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^1.10.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "native-request": "^1.0.5", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/less-loader/-/less-loader-8.0.0.tgz", + "integrity": "sha512-tnDs0ZdwPZgNOg0NGJ0sAD2KViG9TvOMDVibT33fH1bpLkT4xMo5Ue2FsbjFsVsUKtuRTlU0tYp2/lRizrycLg==", + "dev": true, + "dependencies": { + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lint-staged": { + "version": "15.5.2", + "resolved": "https://registry.npmmirror.com/lint-staged/-/lint-staged-15.5.2.tgz", + "integrity": "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==", + "dev": true, + "dependencies": { + "chalk": "^5.4.1", + "commander": "^13.1.0", + "debug": "^4.4.0", + "execa": "^8.0.1", + "lilconfig": "^3.1.3", + "listr2": "^8.2.5", + "micromatch": "^4.0.8", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.7.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lint-staged/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/yaml": { + "version": "2.8.0", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/listr2": { + "version": "8.3.3", + "resolved": "https://registry.npmmirror.com/listr2/-/listr2-8.3.3.tgz", + "integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==", + "dev": true, + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/loader-utils/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash-unified": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz", + "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "@types/lodash-es": "*", + "lodash": "*", + "lodash-es": "*" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.defaultsdeep": { + "version": "4.6.1", + "resolved": "https://registry.npmmirror.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", + "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", + "dev": true + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true + }, + "node_modules/lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==", + "dev": true + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmmirror.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmmirror.com/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "dev": true, + "peer": true + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-options": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/merge-options/-/merge-options-1.0.1.tgz", + "integrity": "sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==", + "dev": true, + "dependencies": { + "is-plain-obj": "^1.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.9.2", + "resolved": "https://registry.npmmirror.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", + "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mitt": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.0.tgz", + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==" + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module-alias": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/module-alias/-/module-alias-2.2.3.tgz", + "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==", + "dev": true + }, + "node_modules/mousetrap": { + "version": "1.6.5", + "resolved": "https://registry.npmmirror.com/mousetrap/-/mousetrap-1.6.5.tgz", + "integrity": "sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmmirror.com/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmmirror.com/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/native-request": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/native-request/-/native-request-1.1.2.tgz", + "integrity": "sha512-/etjwrK0J4Ebbcnt35VMWnfiUX/B04uwGJxyJInagxDqf2z5drSt/lsOvEMWGYunz1kaLZAFrV4NDAbOoDKvAQ==", + "dev": true, + "optional": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "optional": true + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/normalize-wheel-es": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", + "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==", + "dev": true, + "peer": true + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/number-precision": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/number-precision/-/number-precision-1.6.0.tgz", + "integrity": "sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-visit/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmmirror.com/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmmirror.com/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pinia": { + "version": "2.0.17", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.0.17.tgz", + "integrity": "sha512-AtwLwEWQgIjofjgeFT+nxbnK5lT2QwQjaHNEDqpsi2AiCwf/NY78uWTeHUyEhiiJy8+sBmw0ujgQMoQbWiZDfA==", + "dependencies": { + "@vue/devtools-api": "^6.2.1", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@vue/composition-api": "^1.4.0", + "typescript": ">=4.4.4", + "vue": "^2.6.14 || ^3.2.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "dependencies": { + "semver-compare": "^1.0.0" + } + }, + "node_modules/portfinder": { + "version": "1.0.37", + "resolved": "https://registry.npmmirror.com/portfinder/-/portfinder-1.0.37.tgz", + "integrity": "sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==", + "dev": true, + "dependencies": { + "async": "^3.2.6", + "debug": "^4.3.6" + }, + "engines": { + "node": ">= 10.12" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmmirror.com/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.1", + "resolved": "https://registry.npmmirror.com/postcss-colormin/-/postcss-colormin-5.3.1.tgz", + "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmmirror.com/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-loader/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmmirror.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.4", + "resolved": "https://registry.npmmirror.com/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", + "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dev": true, + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmmirror.com/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dev": true, + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmmirror.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "dev": true, + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-prefix-selector": { + "version": "1.16.1", + "resolved": "https://registry.npmmirror.com/postcss-prefix-selector/-/postcss-prefix-selector-1.16.1.tgz", + "integrity": "sha512-Umxu+FvKMwlY6TyDzGFoSUnzW+NOfMBLyC1tAkIjgX+Z/qGspJeRjVC903D7mx7TuBpJlwti2ibXtWuA7fKMeQ==", + "dev": true, + "peerDependencies": { + "postcss": ">4 <9" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/posthtml": { + "version": "0.9.2", + "resolved": "https://registry.npmmirror.com/posthtml/-/posthtml-0.9.2.tgz", + "integrity": "sha512-spBB5sgC4cv2YcW03f/IAUN1pgDJWNWD8FzkyY4mArLUMJW+KlQhlmUdKAHQuPfb00Jl5xIfImeOsf6YL8QK7Q==", + "dev": true, + "dependencies": { + "posthtml-parser": "^0.2.0", + "posthtml-render": "^1.0.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/posthtml-parser": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/posthtml-parser/-/posthtml-parser-0.2.1.tgz", + "integrity": "sha512-nPC53YMqJnc/+1x4fRYFfm81KV2V+G9NZY+hTohpYg64Ay7NemWWcV4UWuy/SgMupqQ3kJ88M/iRfZmSnxT+pw==", + "dev": true, + "dependencies": { + "htmlparser2": "^3.8.3", + "isobject": "^2.1.0" + } + }, + "node_modules/posthtml-parser/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/posthtml-parser/node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/posthtml-parser/node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/posthtml-parser/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "node_modules/posthtml-parser/node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/posthtml-parser/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/posthtml-parser/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "node_modules/posthtml-parser/node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/posthtml-rename-id": { + "version": "1.0.12", + "resolved": "https://registry.npmmirror.com/posthtml-rename-id/-/posthtml-rename-id-1.0.12.tgz", + "integrity": "sha512-UKXf9OF/no8WZo9edRzvuMenb6AD5hDLzIepJW+a4oJT+T/Lx7vfMYWT4aWlGNQh0WMhnUx1ipN9OkZ9q+ddEw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "1.0.5" + } + }, + "node_modules/posthtml-rename-id/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/posthtml-render": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/posthtml-render/-/posthtml-render-1.4.0.tgz", + "integrity": "sha512-W1779iVHGfq0Fvh2PROhCe2QhB8mEErgqzo1wpIt36tCgChafP+hbXIhLDOM8ePJrZcFs0vkNEtdibEWVqChqw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/posthtml-svg-mode": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/posthtml-svg-mode/-/posthtml-svg-mode-1.0.3.tgz", + "integrity": "sha512-hEqw9NHZ9YgJ2/0G7CECOeuLQKZi8HjWLkBaSVtOWjygQ9ZD8P7tqeowYs7WrFdKsWEKG7o+IlsPY8jrr0CJpQ==", + "dev": true, + "dependencies": { + "merge-options": "1.0.1", + "posthtml": "^0.9.2", + "posthtml-parser": "^0.2.1", + "posthtml-render": "^1.0.6" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/progress-webpack-plugin": { + "version": "1.0.16", + "resolved": "https://registry.npmmirror.com/progress-webpack-plugin/-/progress-webpack-plugin-1.0.16.tgz", + "integrity": "sha512-sdiHuuKOzELcBANHfrupYo+r99iPRyOnw15qX+rNlVUqXGfjXdH4IgxriKwG1kNJwVswKQHMdj1hYZMcb9jFaA==", + "dev": true, + "dependencies": { + "chalk": "^2.1.0", + "figures": "^2.0.0", + "log-update": "^2.3.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/progress-webpack-plugin/node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dev": true, + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/progress-webpack-plugin/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/progress-webpack-plugin/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/progress-webpack-plugin/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/log-update": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/log-update/-/log-update-2.3.0.tgz", + "integrity": "sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^3.0.0", + "cli-cursor": "^2.0.0", + "wrap-ansi": "^3.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/wrap-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz", + "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==", + "dev": true, + "dependencies": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/query-string/-/query-string-7.1.1.tgz", + "integrity": "sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w==", + "dependencies": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmmirror.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmmirror.com/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "dev": true, + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmmirror.com/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dev": true, + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmmirror.com/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resize-detector": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/resize-detector/-/resize-detector-0.3.0.tgz", + "integrity": "sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ==" + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmmirror.com/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-node": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/run-node/-/run-node-1.0.0.tgz", + "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==", + "dev": true, + "bin": { + "run-node": "run-node" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sass": { + "version": "1.89.2", + "resolved": "https://registry.npmmirror.com/sass/-/sass-1.89.2.tgz", + "integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==", + "dev": true, + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass-loader": { + "version": "14.2.1", + "resolved": "https://registry.npmmirror.com/sass-loader/-/sass-loader-14.2.1.tgz", + "integrity": "sha512-G0VcnMYU18a4N7VoNDegg2OuMjYtxnqzQWARVWCIVSZwJeiL9kg8QMsuIZOplsJgTzZLF6jGxI3AClj8I9nRdQ==", + "dev": true, + "dependencies": { + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/scroll-into-view-if-needed": { + "version": "2.2.31", + "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", + "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", + "dependencies": { + "compute-scroll-into-view": "^1.0.20" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmmirror.com/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmmirror.com/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmmirror.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "dev": true + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", + "dev": true + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmmirror.com/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "dev": true + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-baker": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/svg-baker/-/svg-baker-1.7.0.tgz", + "integrity": "sha512-nibslMbkXOIkqKVrfcncwha45f97fGuAOn1G99YwnwTj8kF9YiM6XexPcUso97NxOm6GsP0SIvYVIosBis1xLg==", + "dev": true, + "dependencies": { + "bluebird": "^3.5.0", + "clone": "^2.1.1", + "he": "^1.1.1", + "image-size": "^0.5.1", + "loader-utils": "^1.1.0", + "merge-options": "1.0.1", + "micromatch": "3.1.0", + "postcss": "^5.2.17", + "postcss-prefix-selector": "^1.6.0", + "posthtml-rename-id": "^1.0", + "posthtml-svg-mode": "^1.0.3", + "query-string": "^4.3.2", + "traverse": "^0.6.6" + } + }, + "node_modules/svg-baker-runtime": { + "version": "1.4.7", + "resolved": "https://registry.npmmirror.com/svg-baker-runtime/-/svg-baker-runtime-1.4.7.tgz", + "integrity": "sha512-Zorfwwj5+lWjk/oxwSMsRdS2sPQQdTmmsvaSpzU+i9ZWi3zugHLt6VckWfnswphQP0LmOel3nggpF5nETbt6xw==", + "dev": true, + "dependencies": { + "deepmerge": "1.3.2", + "mitt": "1.1.2", + "svg-baker": "^1.7.0" + } + }, + "node_modules/svg-baker-runtime/node_modules/deepmerge": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-1.3.2.tgz", + "integrity": "sha512-qjMjTrk+RKv/sp4RPDpV5CnKhxjFI9p+GkLBOls5A8EEElldYWCWA9zceAkmfd0xIo2aU1nxiaLFoiya2sb6Cg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker-runtime/node_modules/mitt": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-1.1.2.tgz", + "integrity": "sha512-3btxP0O9iGADGWAkteQ8mzDtEspZqu4I32y4GZYCV5BrwtzdcRpF4dQgNdJadCrbBx7Lu6Sq9AVrerMHR0Hkmw==", + "dev": true + }, + "node_modules/svg-baker/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/svg-baker/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/svg-baker/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/micromatch": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-3.1.0.tgz", + "integrity": "sha512-3StSelAE+hnRvMs8IdVW7Uhk8CVed5tp+kLLGlBP6WiRAXS21GPGu/Nat4WNPXj2Eoc24B02SaeoyozPMfj0/g==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.2.2", + "define-property": "^1.0.0", + "extend-shallow": "^2.0.1", + "extglob": "^2.0.2", + "fragment-cache": "^0.2.1", + "kind-of": "^5.0.2", + "nanomatch": "^1.2.1", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/svg-baker/node_modules/query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmmirror.com/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/svg-baker/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-sprite-loader": { + "version": "6.0.11", + "resolved": "https://registry.npmmirror.com/svg-sprite-loader/-/svg-sprite-loader-6.0.11.tgz", + "integrity": "sha512-TedsTf8wsHH6HgdwKjUveDZRC6q5gPloYV8A8/zZaRWP929J7x6TzQ6MvZFl+YYDJuJ0Akyuu/vNVJ+fbPuYXg==", + "dev": true, + "dependencies": { + "bluebird": "^3.5.0", + "deepmerge": "1.3.2", + "domready": "1.0.8", + "escape-string-regexp": "1.0.5", + "loader-utils": "^1.1.0", + "svg-baker": "^1.5.0", + "svg-baker-runtime": "^1.4.7", + "url-slug": "2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/svg-sprite-loader/node_modules/deepmerge": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-1.3.2.tgz", + "integrity": "sha512-qjMjTrk+RKv/sp4RPDpV5CnKhxjFI9p+GkLBOls5A8EEElldYWCWA9zceAkmfd0xIo2aU1nxiaLFoiya2sb6Cg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-sprite-loader/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmmirror.com/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmmirror.com/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/table/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.43.1", + "resolved": "https://registry.npmmirror.com/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.14.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmmirror.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/thread-loader": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/thread-loader/-/thread-loader-3.0.4.tgz", + "integrity": "sha512-ByaL2TPb+m6yArpqQUZvP+5S1mZtXsEP7nWKKlAUTm7fCml8kB5s1uI3+eHRP2bk5mVYfRSBI7FFf+tWEyLZwA==", + "dev": true, + "dependencies": { + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.1.0", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.27.0 || ^5.0.0" + } + }, + "node_modules/thread-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/thread-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/to-regex/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/traverse": { + "version": "0.6.11", + "resolved": "https://registry.npmmirror.com/traverse/-/traverse-0.6.11.tgz", + "integrity": "sha512-vxXDZg8/+p3gblxB6BhhG5yWVn1kGRlaL8O78UDXc3wRnPizB5g83dcvWV1jpDMIPnjZjOFuxlMmE82XJ4407w==", + "dev": true, + "dependencies": { + "gopd": "^1.2.0", + "typedarray.prototype.slice": "^1.0.5", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ts-loader": { + "version": "9.5.2", + "resolved": "https://registry.npmmirror.com/ts-loader/-/ts-loader-9.5.2.tgz", + "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray.prototype.slice": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.5.tgz", + "integrity": "sha512-q7QNVDGTdl702bVFiI5eY4l/HkgCM6at9KhcFbgUAzezHFbOVy4+0O/lCjsABEQwbZPravVfBIiBVGo89yzHFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "math-intrinsics": "^1.1.0", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-offset": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unidecode": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/unidecode/-/unidecode-0.1.8.tgz", + "integrity": "sha512-SdoZNxCWpN2tXTCrGkPF/0rL2HEq+i2gwRG1ReBvx8/0yTzC3enHfugOf8A9JBShVwwrRIkLX0YcDUGbzjbVCA==", + "dev": true, + "engines": { + "node": ">= 0.4.12" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/url-slug": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/url-slug/-/url-slug-2.0.0.tgz", + "integrity": "sha512-aiNmSsVgrjCiJ2+KWPferjT46YFKoE8i0YX04BlMVDue022Xwhg/zYlnZ6V9/mP3p8Wj7LEp0myiTkC/p6sxew==", + "dev": true, + "dependencies": { + "unidecode": "0.1.8" + } + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "dev": true + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmmirror.com/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vue": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.2.13.tgz", + "integrity": "sha512-raTGvLXXTdMxrhQKY1r1YFXZMmjbjTe7QHBW9EU4CgCBhq8DbgyLqgILcSUZmeFyazk5WY7a7xu0VYmHElf4lA==", + "dependencies": { + "@vue/compiler-dom": "3.2.13", + "@vue/compiler-sfc": "3.2.13", + "@vue/runtime-dom": "3.2.13", + "@vue/server-renderer": "3.2.13", + "@vue/shared": "3.2.13" + } + }, + "node_modules/vue-echarts": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/vue-echarts/-/vue-echarts-6.2.3.tgz", + "integrity": "sha512-xHzUvgsgk/asJTcNa8iVVwoovZU3iEUHvmBa3bzbiP3Y6OMxM1YXsoWOKVmVVaUusGs4ob4pSwjwNy2FemAz9w==", + "hasInstallScript": true, + "dependencies": { + "resize-detector": "^0.3.0", + "vue-demi": "^0.13.2" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.5", + "echarts": "^5.1.2", + "vue": "^2.6.12 || ^3.1.1" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-echarts/node_modules/vue-demi": { + "version": "0.13.11", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz", + "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-eslint-parser": { + "version": "8.3.0", + "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz", + "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==", + "dev": true, + "dependencies": { + "debug": "^4.3.2", + "eslint-scope": "^7.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/vue-grid-layout": { + "version": "3.0.0-beta1", + "resolved": "https://registry.npmmirror.com/vue-grid-layout/-/vue-grid-layout-3.0.0-beta1.tgz", + "integrity": "sha512-MsW0yfYNtnAO/uDhfZvkP6effxSJxvhAFbIL37x6Rn3vW9xf0WHVefKaSbQMLpSq3mXnR6ut0pg2Cd5lqIIZzg==", + "dependencies": { + "@interactjs/actions": "^1.10.2", + "@interactjs/auto-start": "^1.10.2", + "@interactjs/dev-tools": "^1.10.2", + "@interactjs/interactjs": "^1.10.2", + "@interactjs/modifiers": "^1.10.2", + "element-resize-detector": "^1.2.1", + "mitt": "^2.1.0" + } + }, + "node_modules/vue-grid-layout/node_modules/mitt": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-2.1.0.tgz", + "integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==" + }, + "node_modules/vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmmirror.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", + "dev": true + }, + "node_modules/vue-i18n": { + "version": "9.14.4", + "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.14.4.tgz", + "integrity": "sha512-B934C8yUyWLT0EMud3DySrwSUJI7ZNiWYsEEz2gknTthqKiG4dzWE/WSa8AzCuSQzwBEv4HtG1jZDhgzPfWSKQ==", + "dependencies": { + "@intlify/core-base": "9.14.4", + "@intlify/shared": "9.14.4", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-loader": { + "version": "17.4.2", + "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-17.4.2.tgz", + "integrity": "sha512-yTKOA4R/VN4jqjw4y5HrynFL8AK0Z3/Jt7eOJXEitsm0GMRHDBjCfCiuTiLP7OESvsZYo2pATCWhDqxC5ZrM6w==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "watchpack": "^2.4.0" + }, + "peerDependencies": { + "webpack": "^4.1.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/vue-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/vue-router": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.0.3.tgz", + "integrity": "sha512-AD1OjtVPyQHTSpoRsEGfPpxRQwhAhxcacOYO3zJ3KNkYP/r09mileSp6kdMQKhZWP2cFsPR3E2M3PZguSN5/ww==", + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-style-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmmirror.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz", + "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", + "dev": true, + "dependencies": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "node_modules/vue-style-loader/node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + }, + "node_modules/vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true + }, + "node_modules/vue-winbox": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/vue-winbox/-/vue-winbox-0.1.0.tgz", + "integrity": "sha512-JaUnxzBAgyX1tIvjjWkWx0t8psTZG5mAzxz4hsxCvN59grGd1d+f9694Z11wkLI/aRem/9yLQ+PL1viAW+JKVw==", + "dependencies": { + "nanoid": "^3.1.23", + "vue-demi": "^0.11.2", + "winbox": "^0.2.0" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-beta.1", + "vue": "^2.0.0 || >=3.0.0-rc.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-winbox/node_modules/vue-demi": { + "version": "0.11.4", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.11.4.tgz", + "integrity": "sha512-/3xFwzSykLW2HiiLie43a+FFgNOcokbBJ+fzvFXd0r2T8MYohqvphUyDQ8lbAwzQ3Dlcrb1c9ykifGkhSIAk6A==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue/node_modules/@vue/compiler-core": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.13.tgz", + "integrity": "sha512-H8MUuKVCfAT6C0vth/+1LAriKnM+RTFo/5MoFycwRPwworTvkpWq/EuGoIXdLBblo8Y2/bNsOmIBEEoOtrb/bQ==", + "dependencies": { + "@babel/parser": "^7.15.0", + "@vue/shared": "3.2.13", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "node_modules/vue/node_modules/@vue/compiler-dom": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.13.tgz", + "integrity": "sha512-5+2dYgQyNzM97EEgbdAusUpLjulcKkvLM26jOGpd14+qwEcW/KCnns5DGjlZD/tsdEwToOoTDCm+mjx7cO/G1Q==", + "dependencies": { + "@vue/compiler-core": "3.2.13", + "@vue/shared": "3.2.13" + } + }, + "node_modules/vue/node_modules/@vue/compiler-sfc": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.13.tgz", + "integrity": "sha512-3j970d969aOILykcTstdihP33xH1Onm0wsvcl+rGv9AGxivB9xicRxBw93HCIA4dAPivr42WjHEoci9q2/85uw==", + "dependencies": { + "@babel/parser": "^7.15.0", + "@vue/compiler-core": "3.2.13", + "@vue/compiler-dom": "3.2.13", + "@vue/compiler-ssr": "3.2.13", + "@vue/ref-transform": "3.2.13", + "@vue/shared": "3.2.13", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7", + "postcss": "^8.1.10", + "source-map": "^0.6.1" + } + }, + "node_modules/vue/node_modules/@vue/compiler-ssr": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.13.tgz", + "integrity": "sha512-ZbO6uDhUWTdKBRguYNEZXj2FU3nh1cudoHBiidbxj9q5J0tVT+j1PSVFAXPq6SquUBdJpa4HvGkQ5kQzv6upXg==", + "dependencies": { + "@vue/compiler-dom": "3.2.13", + "@vue/shared": "3.2.13" + } + }, + "node_modules/vue/node_modules/@vue/shared": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.13.tgz", + "integrity": "sha512-F/gs3kHQ8Xeo24F6EImOvBiIoYQsBjF9qoLzvk+LHxYN6ZhIDEL1NWrBFYzdFQ7NphjEYd4EvPZ+Qee+WX8P6w==" + }, + "node_modules/vue/node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmmirror.com/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/webpack": { + "version": "5.99.9", + "resolved": "https://registry.npmmirror.com/webpack/-/webpack-5.99.9.tgz", + "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-bundle-analyzer": { + "version": "4.10.2", + "resolved": "https://registry.npmmirror.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", + "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "commander": "^7.2.0", + "debounce": "^1.2.1", + "escape-string-regexp": "^4.0.0", + "gzip-size": "^6.0.0", + "html-escaper": "^2.0.2", + "opener": "^1.5.2", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-chain": { + "version": "6.5.1", + "resolved": "https://registry.npmmirror.com/webpack-chain/-/webpack-chain-6.5.1.tgz", + "integrity": "sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "dependencies": { + "deepmerge": "^1.5.2", + "javascript-stringify": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-chain/node_modules/deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.4", + "resolved": "https://registry.npmmirror.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.15.2", + "resolved": "https://registry.npmmirror.com/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", + "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.4", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmmirror.com/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmmirror.com/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.4.6", + "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.4.6.tgz", + "integrity": "sha512-5tyDlKLqPfMqjT3Q9TAqf2YqjwmnUleZwzJi1A5qXnlBCdj2AtOJ6wAWdglTIDOPgOiOrXeBeFcsQ8+aGQ6QbA==", + "dev": true + }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack/node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmmirror.com/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmmirror.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true + }, + "node_modules/winbox": { + "version": "0.2.82", + "resolved": "https://registry.npmmirror.com/winbox/-/winbox-0.2.82.tgz", + "integrity": "sha512-fMHaLnnuhyw1mLvzmCTWMpRKDfXUK/Z08VdbyBqDaITFFAd3Fx2BGDCF6rzGSkWrQgo8fHTFpFICofyM36FTnA==" + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmmirror.com/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wujie": { + "version": "1.0.28", + "resolved": "https://registry.npmmirror.com/wujie/-/wujie-1.0.28.tgz", + "integrity": "sha512-Numzm/yh50XV928sjWb8Qo1Fl3zXMhGqRPl1rZ7ZmRNC07dfaTXuFcIb17OrbYd3IokjLs17UAKgjCyKIJtfNw==", + "dependencies": { + "@babel/runtime": "^7.18.6" + } + }, + "node_modules/wujie-polyfill": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/wujie-polyfill/-/wujie-polyfill-1.1.3.tgz", + "integrity": "sha512-qYvluvjquCO/T1yIknJba6a+8dZUeRffUN/OvQQ22RHfgyCDNtxGg7Xgw1ZCZ1YB4hs/qFsVRl66GT/VvB3/0Q==" + }, + "node_modules/wujie-vue3": { + "version": "1.0.28", + "resolved": "https://registry.npmmirror.com/wujie-vue3/-/wujie-vue3-1.0.28.tgz", + "integrity": "sha512-41TxePEmldOHFoU4cKmp4PtoiXuNa6fb+ozu4cohLyGkOkw9DQDGNv+P5GQofPo6H1MhZ4B8z0wXXcIzGvR+yw==", + "dependencies": { + "wujie": "1.0.28" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xterm": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/xterm/-/xterm-5.1.0.tgz", + "integrity": "sha512-LovENH4WDzpwynj+OTkLyZgJPeDom9Gra4DMlGAgz6pZhIDCQ+YuO7yfwanY+gVbn/mmZIStNOnVRU/ikQuAEQ==", + "deprecated": "This package is now deprecated. Move to @xterm/xterm instead." + }, + "node_modules/xterm-addon-attach": { + "version": "0.7.0", + "resolved": "https://registry.npmmirror.com/xterm-addon-attach/-/xterm-addon-attach-0.7.0.tgz", + "integrity": "sha512-Yh3Kvq2e28onjnmGizQKZwlRMp9gmuZEHVX0BVZbo463YSjkqfhQS3T2wMLuO+j8AokccXsMa1z0bH1/+MMYuQ==", + "deprecated": "This package is now deprecated. Move to @xterm/addon-attach instead.", + "peerDependencies": { + "xterm": "^5.0.0" + } + }, + "node_modules/xterm-addon-fit": { + "version": "0.7.0", + "resolved": "https://registry.npmmirror.com/xterm-addon-fit/-/xterm-addon-fit-0.7.0.tgz", + "integrity": "sha512-tQgHGoHqRTgeROPnvmtEJywLKoC/V9eNs4bLLz7iyJr1aW/QFzRwfd3MGiJ6odJd9xEfxcW36/xRU47JkD5NKQ==", + "deprecated": "This package is now deprecated. Move to @xterm/addon-fit instead.", + "peerDependencies": { + "xterm": "^5.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yorkie": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/yorkie/-/yorkie-2.0.0.tgz", + "integrity": "sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "execa": "^0.8.0", + "is-ci": "^1.0.10", + "normalize-path": "^1.0.0", + "strip-indent": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yorkie/node_modules/ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, + "node_modules/yorkie/node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "dev": true, + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/yorkie/node_modules/execa": { + "version": "0.8.0", + "resolved": "https://registry.npmmirror.com/execa/-/execa-0.8.0.tgz", + "integrity": "sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==", + "dev": true, + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yorkie/node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/yorkie/node_modules/is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "dependencies": { + "ci-info": "^1.5.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/yorkie/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/yorkie/node_modules/normalize-path": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-1.0.0.tgz", + "integrity": "sha512-7WyT0w8jhpDStXRq5836AMmihQwq2nrUVQrgjvUo/p/NZf9uy/MeJ246lBJVmWuYXMlJuG9BNZHF0hWjfTbQUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yorkie/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yorkie/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yorkie/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/yorkie/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + }, + "node_modules/zrender": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.2.1.tgz", + "integrity": "sha512-M3bPGZuyLTNBC6LiNKXJwSCtglMp8XUEqEBG+2MdICDI3d1s500Y4P0CzldQGsqpRVB7fkvf3BKQQRxsEaTlsw==", + "dependencies": { + "tslib": "2.3.0" + } + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/package.json b/ER-Model/openGauss-datakit/visualtool-ui/package.json new file mode 100644 index 000000000..5a72a2251 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/package.json @@ -0,0 +1,81 @@ +{ + "name": "visualtool-ui", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "vue-cli-service serve", + "build": "vue-cli-service build", + "lint": "vue-cli-service lint" + }, + "dependencies": { + "@antv/x6": "1.32.10", + "@antv/x6-vue-shape": "1.4.2", + "@arco-design/web-vue": "2.41.1", + "@computing/opendesign-icons": "file:lib\\opendesign-icons-1.0.3.tgz", + "@computing/opendesign2": "file:lib\\opendesign2-2.0.22.tgz", + "@highlightjs/vue-plugin": "2.1.0", + "@vueuse/core": "7.3.0", + "animate.css": "4.1.1", + "axios": "0.27.2", + "bootstrap-icons": "^1.11.3", + "core-js": "3.24.1", + "dayjs": "1.11.4", + "echarts": "5.2.2", + "highlight.js": "11.6.0", + "jsencrypt": "3.0.0-rc.1", + "mitt": "3.0.0", + "nprogress": "0.2.0", + "pinia": "2.0.17", + "query-string": "7.1.1", + "vue": "3.2.13", + "vue-echarts": "6.2.3", + "vue-grid-layout": "^3.0.0-beta1", + "vue-i18n": "^9.3.0-beta.6", + "vue-router": "4.0.3", + "vue-winbox": "^0.1.0", + "wujie-polyfill": "^1.1.2", + "wujie-vue3": "^1.0.0", + "wujie-polyfill": "^1.1.2", + "xlsx": "^0.18.5", + "xterm": "5.1.0", + "xterm-addon-attach": "0.7.0", + "xterm-addon-fit": "0.7.0" + }, + "devDependencies": { + "@computing/opendesign-icons": "file:lib\\opendesign-icons-1.0.3.tgz", + "@computing/opendesign2": "file:lib\\opendesign2-2.0.22.tgz", + "@types/nprogress": "0.2.0", + "@vue/cli-plugin-babel": "~5.0.0", + "@vue/cli-plugin-eslint": "~5.0.0", + "@vue/cli-plugin-router": "~5.0.0", + "@vue/cli-service": "~5.0.0", + "eslint": "7.32.0", + "eslint-plugin-vue": "8.0.3", + "husky": "1.3.1", + "less": "4.0.0", + "less-loader": "8.0.0", + "lint-staged": "^15.2.11", + "sass": "^1.77.2", + "sass-loader": "^14.2.1", + "svg-sprite-loader": "6.0.11", + "@typescript-eslint/eslint-plugin": "5.4.0", + "@typescript-eslint/parser": "5.4.0", + "@vue/eslint-config-typescript": "9.1.0", + "@vue/cli-plugin-typescript": "~5.0.0", + "typescript": "4.9" + }, + "husky": { + "hooks": { + "pre-commit": "npm run lint" + } + }, + "lint-staged": { + "*.{ts,tsx,js,jsx,vue}": [ + "eslint --fix", + "prettier --write" + ] + }, + "overrides": { + "get-east-asian-width": "^1.3.0" + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/public/favicon.ico b/ER-Model/openGauss-datakit/visualtool-ui/public/favicon.ico new file mode 100644 index 000000000..aec5a877d Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/public/favicon.ico differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/public/import-template.xlsx b/ER-Model/openGauss-datakit/visualtool-ui/public/import-template.xlsx new file mode 100644 index 000000000..9162c486e Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/public/import-template.xlsx differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/public/index.html b/ER-Model/openGauss-datakit/visualtool-ui/public/index.html new file mode 100644 index 000000000..c61df218a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/public/index.html @@ -0,0 +1,17 @@ + + + + + + + + openGauss + + + +
+ + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/public/template.csv b/ER-Model/openGauss-datakit/visualtool-ui/public/template.csv new file mode 100644 index 000000000..123f2a07e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/public/template.csv @@ -0,0 +1 @@ +"集群名称","部署类型","节点名称","连接URL","用户名","密码" \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/App.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/App.vue new file mode 100644 index 000000000..3d7610973 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/App.vue @@ -0,0 +1,37 @@ + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/api/dashboard.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/api/dashboard.ts new file mode 100644 index 000000000..9aff885f7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/api/dashboard.ts @@ -0,0 +1,5 @@ +import axios from 'axios' + +export function instanceRoute () { + return axios.get('/getIndexInstanceRouters') +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/api/interceptor.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/api/interceptor.ts new file mode 100644 index 000000000..81fda1e73 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/api/interceptor.ts @@ -0,0 +1,95 @@ +import axios from 'axios' +import type { AxiosRequestConfig, AxiosResponse } from 'axios' +import { Message, Modal } from '@arco-design/web-vue' +import { useUserStore } from '@/store' +import { getToken } from '@/utils/auth' +import errorCode from '@/utils/errorCode' +import i18n from '@/locale/index' + +export interface HttpResponse { + status: number; + msg: string; + code: number; + data: T; +} + +axios.defaults.baseURL = process.env.VUE_APP_BASE_API || '' +axios.defaults.timeout = 600000 + +let isTimeout = false + +axios.interceptors.request.use( + (config: AxiosRequestConfig) => { + const token = getToken() + if (token && config.url !== '/login') { + if (!config.headers) { + config.headers = {} + } + config.headers.Authorization = `Bearer ${token}` + } + return config + }, + (error) => { + return Promise.reject(error) + } +) +// add response interceptors +axios.interceptors.response.use( + (response: AxiosResponse) => { + const res = response + const code = res.data.code || 200 + const msg = ((errorCode as any)[code] && i18n.global.t((errorCode as any)[code])) || res.data.msg || i18n.global.t(errorCode['default']) + + if (code === 401) { + if (!isTimeout) { + isTimeout = true + Modal.error({ + title: i18n.global.t('components.login-form.5o61dvpc2o00'), + content: i18n.global.t('components.login-form.5o61dvpc5cs0'), + okText: i18n.global.t('components.login-form.5o61dvpc5o80'), + width: 'auto', + maskClosable: false, + async onOk () { + isTimeout = false + const userStore = useUserStore() + + await userStore.logout() + window.location.reload() + } + }) + } + return Promise.reject('Please Log in') + } else if (code === 500) { + Message.error({ + content: msg, + duration: 5 * 1000 + }) + return Promise.reject(new Error(msg)) + } else if (code !== 200) { + Message.error({ + content: msg, + duration: 5 * 1000 + }) + return Promise.reject('error') + } else { + return res.data + } + }, + (error) => { + const { request, message, response } = error + if (request.responseURL.includes('plugins/base-ops')) { + if (response.status !== 404) { + Message.error({ + content: message || 'Error', + duration: 3 * 1000 + }) + } + } else { + Message.error({ + content: message || 'Error', + duration: 3 * 1000 + }) + } + return Promise.reject(error) + } +) diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/api/message.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/api/message.ts new file mode 100644 index 000000000..7e361dcc5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/api/message.ts @@ -0,0 +1,38 @@ +import axios from 'axios' + +export interface MessageRecord { + id: number; + type: string; + title: string; + subTitle: string; + avatar?: string; + content: string; + time: string; + status: 0 | 1; + messageType?: number; +} +export type MessageListType = MessageRecord[]; + +export function queryMessageList () { + return axios.post('/api/message/list') +} + +interface MessageStatus { + ids: number[]; +} + +export function setMessageStatus (data: MessageStatus) { + return axios.post('/api/message/read', data) +} + +export interface ChatRecord { + id: number; + username: string; + content: string; + time: string; + isCollect: boolean; +} + +export function queryChatList () { + return axios.post('/api/chat/list') +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/api/operLog.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/api/operLog.ts new file mode 100644 index 000000000..236135941 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/api/operLog.ts @@ -0,0 +1,10 @@ +import axios from 'axios' +import qs from 'query-string' +export function listOperLog (params: any) { + return axios.get('/system/operlog/list', { + params, + paramsSerializer: function (p) { + return qs.stringify(p) + } + }) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/api/ops/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/api/ops/index.ts new file mode 100644 index 000000000..3fa18bb35 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/api/ops/index.ts @@ -0,0 +1,362 @@ +import axios from 'axios' +import { KeyValue } from '@/types/global' +import { downloadPackage, SSHBody } from '@/types/ops/install' + +// get Key +export const getEntryKey = () => { + return axios.get('encryption/getKey') +} + +// get Loginkey +export const getLoginKey = () => { + return axios.get('pubKey') +} + +// one check +export const clusterCheck = (data: KeyValue) => { + return axios.get('opsCluster/check', { + params: data + }) +} + +export const clusterList = () => { + return axios.get('opsCluster/listCluster') +} + +export const getSummary = () => { + return axios.get('opsCluster/summary') +} + +export const openSSH = (data: SSHBody) => { + return axios.post('opsCluster/ssh', data) +} + +export const clusterMonitor = (data: KeyValue) => { + return axios.get('opsCluster/monitor', { + params: data + }) +} + +// Home Statistics Interface +// Get the number of streams +export const getDataFlowCount = () => { + return axios.get('plugins/base-ops/modeling/dataflow/count') +} + +// Get the number of business flows +export const getBusiFlowCount = () => { + return axios.get('plugins/base-ops/modeling/workflow/count') +} + +// Get the number of data models +export const getDataModelCount = () => { + return axios.get('plugins/base-ops/modeling/databaseDesign/count') +} + +// Get business flow type distribution data +export const getBusiFlowType = () => { + return axios.get('plugins/base-ops/modeling/dataflow/distribution') +} + +// Get the list of business flow running instances +export const getBusiFlowList = () => { + return axios.get('plugins/base-ops/modeling/dataflow/processInfo') +} +// Get the status of Base-ops +export const isBaseOpsStart = () => { + return axios.get('system/plugins/isBaseOpsStart') +} +// Get the number of plugins +export const getPluginCount = () => { + return axios.get('system/plugins/count') +} + +export const getHostByClusterId = (data: KeyValue) => { + return axios.get('opsCluster/listHost', { + params: data + }) +} + +// Host management related + +// page +export const hostPage = (query: any) => { + return axios.get('host/page', { + params: query + }) +} + +export const hostListAll = () => { + return axios.get('host/listAll') +} + +export const addHost = (data: KeyValue) => { + return axios.post('host', data) +} + +export const editHost = (hostId: string, data: KeyValue) => { + return axios.put(`host/${hostId}`, data) +} + +export const hostPing = (data: KeyValue) => { + return axios.post('host/ping', data) +} + +export const hostSSH = (data: KeyValue) => { + return axios.post('host/ssh', data) +} + +export const hostSSHByHostId = (hostId: string, data: KeyValue) => { + return axios.post('host/ssh/' + hostId, data) +} + +export const hostMonitor = (data: KeyValue) => { + return axios.get('host/monitor', { + params: data + }) +} + +export const pageMonitor = (hostIds: string [], businessId: string) => { + return axios.post(`host/monitor?businessId=${businessId}`, hostIds) +} + +export const hostPingById = (hostId: string, data: KeyValue) => { + return axios.get(`host/ping/${hostId}`, { + params: data + }) +} + +export const delHost = (hostId: string) => { + return axios.delete(`host/${hostId}`) +} + +export const stopHostAgent = (agentId: string) => { + return axios.post(`agent/stop?agentId=${agentId}`) +} + +export const startHostAgent = (agentId: string) => { + return axios.post(`agent/start?agentId=${agentId}`) +} + +export const uninstallAgent = (agentId: string) => { + return axios.post(`agent/uninstall?agentId=${agentId}`) +} + +export const listAgent = (data: KeyValue) => { + return axios.get(`agent/list`, { + params: data + }) +} + +// host tag +export const hostBatchAddTag = (data: KeyValue) => { + return axios.put('hostTag/addTag', data) +} + +export const hostBatchDelTag = (data: KeyValue) => { + return axios.put('hostTag/delTag', data) +} + +export const hostTagListAll = () => { + return axios.get(`hostTag/listAll`) +} + +export const hostTagPage = (data: KeyValue) => { + return axios.get('hostTag/page', { + params: data + }) +} + +export const hostTagAdd = (data: KeyValue) => { + return axios.post('hostTag/add', data) +} + +export const hostTagUpdate = (id: string, data: KeyValue) => { + return axios.put('hostTag/update/' + id, data) +} + +export const hostTagDel = (id: string) => { + return axios.delete('hostTag/del/' + id) +} + +// host user +export const hostUserPage = (hostId: string) => { + return axios.get(`hostUser/page/${hostId}`) +} + +export const hostUserListAll = (hostId: string) => { + return axios.get(`hostUser/listAll/${hostId}`) +} + +export const hostUserListWithoutRoot = (hostId: string) => { + return axios.get(`hostUser/listAllWithoutRoot/${hostId}`) +} + +export const addHostUser = (data: KeyValue) => { + return axios.post('hostUser', data) +} + +export const editHostUser = (hostUserId: string, data: KeyValue) => { + return axios.put(`hostUser/${hostUserId}`, data) +} + +export const delHostUser = (hostUserId: string) => { + return axios.delete(`hostUser/${hostUserId}`) +} + +// az management +export const azListAll = () => { + return axios.get('az/listAll') +} + +export const addAz = (data: KeyValue) => { + return axios.post('az', data) +} + +export const editAz = (azId: string, data: KeyValue) => { + return axios.put(`az/${azId}`, data) +} + +// page +export const azPage = (query: any) => { + return axios.get('az/page', { + params: query + }) +} + +export const delAz = (azId: string) => { + return axios.delete(`az/${azId}`) +} + +export const hasNameAZ = (data: KeyValue) => { + return axios.get('az/hasName', { + params: data + }) +} + +// jdbc + +export const downloadTemplate = () => { + return axios.get('jdbcDbCluster/downloadTemplate') +} + +export const jdbcPage = (query: any) => { + return axios.get('jdbcDbCluster/page', { + params: query + }) +} + +export const addJdbc = (data: KeyValue) => { + return axios.post('jdbcDbCluster/add', data) +} + +export const editJdbc = (id: string, data: KeyValue) => { + return axios.put(`jdbcDbCluster/${id}`, data) +} + +export const jdbcPing = (data: KeyValue) => { + return axios.post('jdbc/ping', data) +} + +export const delJdbc = (jdbcId: string) => { + return axios.delete(`jdbcDbCluster/${jdbcId}`) +} + +export const uploadFileJdbc = (data: KeyValue) => { + return axios.post('jdbcDbCluster/importAnalysis', data) +} + +export const uploadRealJdbc = (data: KeyValue) => { + return axios.post('jdbcDbCluster/importCluster', data) +} + +// cluster node ping +export const jdbcNodePing = (data: KeyValue) => { + return axios.post('jdbcDbClusterNode/ping', data) +} + +export const jdbcNodeMonitor = (clusterNodeId: any, data: KeyValue) => { + return axios.get(`jdbcDbClusterNode/monitor/${clusterNodeId}`, { + params: data + }) +} + +export const bulkuploadPhy = (data: KeyValue) => { + return axios.post(`/host/upload`, data) +} + +export const bulkImportany = (data: KeyValue) => { + return axios.post(`host/invokeFile`, data) +} + +export const bulkImporErrPlan = (data: KeyValue) => { + return axios.get(`/host/get_import_plan`, data) +} + +export const delPackage = (packageId: string) => { + return axios.delete(`/plugins/base-ops/installPackageManager/${packageId}`) +} + +export const delPackageV2 = (data: KeyValue) => { + return axios.post(`/plugins/base-ops/installPackageManager/v2/delete/package`, data) +} + +export const checkPackage = (data: KeyValue) => { + return axios.post(`/plugins/base-ops/installPackageManager/v2/check/package`, data) +} + +export const getVersionNum = () => { + return axios.get(`/plugins/base-ops/installPackageManager/v2/list/version/number`) +} + +export const getPackageList = (data: FormData) => { + return axios.get(`/plugins/base-ops/installPackageManager/v2/list/package`, { data }) +} + +export const getPackagePage = (pageSize: number, pageNum:number, data: KeyValue) => { + return axios.post(`/plugins/base-ops/installPackageManager/v2/page/package?pageSize=${pageSize}&pageNum=${pageNum}`, data) +} + +export const batchPackageOnline = (data: FormData) => { + return axios.post(`/plugins/base-ops/installPackageManager/v2/save/online`, data) +} + +export const batchPackageUpload = (uploadFileParam:any) => { + return axios.post(`/plugins/base-ops/installPackageManager/v2/save/upload`, uploadFileParam) +} + +export const packageOnlineUpdate = (params: URLSearchParams, config: KeyValue) => { + return axios.post(`/plugins/base-ops/installPackageManager/v2/update/online`, params, config) +} + +export const packageUploadUpdate = (params: KeyValue, data: KeyValue) => { + return axios.post(`/plugins/base-ops/installPackageManager/v2/update/upload${params}`, data) +} + +export const checkNetStatus = () => { + return axios.get(`/plugins/base-ops/installPackageManager/v2/check/online`) +} + +export const checkVersionNumber = (params: KeyValue) => { + return axios.get(`/plugins/base-ops/installPackageManager/v2/check/version/number`, { params: params }) +} + +export const getSysUploadPath = () => { + return axios.get('/plugins/base-ops/installPackageManager/sysUploadPath') +} + +export const hasPkgName = (name: string) => { + return axios.get('/plugins/base-ops/installPackageManager/hasName', { params: { name: name }}) +} + +export const download = (data: downloadPackage) => { + return axios.post(`/plugins/base-ops/opsCluster/download`, data ? data : {}) +} + +export const delPkgTar = (path: string, id?: string) => { + return axios.delete('/plugins/base-ops/installPackageManager/delPkgTar', { params: { path: path, id: id }}) +} + +export const addAgent = (data: KeyValue) => { + return axios.post('/agent/install', data) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/api/plugin.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/api/plugin.ts new file mode 100644 index 000000000..0f26c97b5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/api/plugin.ts @@ -0,0 +1,43 @@ +import axios from 'axios' + +export function list (query?: any) { + return axios.get('/system/plugins/list', { params: query }) +} + +export function start (id: string | number) { + return axios.post(`/system/plugins/start/${id}`) +} + +export function stop (id: string | number) { + return axios.post(`/system/plugins/stop/${id}`) +} + +export function uninstall (id: string | number) { + return axios.post(`/system/plugins/uninstall/${id}`) +} + +export function pluginConfigData (payload: any) { + return axios.post('/system/plugins/pluginConfigData', payload) +} + +// 获取插件列表扩展信息 +export function extendInfoList (query?: any) { + return axios.get('/system/plugins/extensions/list', { params: query }) +} + +export function unloadPluginsInfo () { + return axios.get('/system/plugins/unloadPluginsInfo') +} + +export function getUnloadpluginUrl (pluginId: string) { + return axios.post(`/system/plugins/getUnloadPluginUrl?pluginId=${pluginId}`) +} + +export function downloadPluginWs (params: any) { + return axios.post(`/system/plugins/online_install`, params) +} + +export function uploadPluginWs (params: any) { + return axios.post(`/system/plugins/offline_install`, params) +} + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/api/role.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/api/role.ts new file mode 100644 index 000000000..68b227839 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/api/role.ts @@ -0,0 +1,33 @@ +import axios from 'axios' + +export function getRoleList (query: any) { + return axios.get('/system/role/list', { + params: query + }) +} + +export function createRole (payload: any) { + return axios.post('/system/role', payload) +} + +export function updateRole (payload: any) { + return axios.put('/system/role', payload) +} + +export function changeStatus (payload: any) { + return axios.put('/system/role/changeStatus', payload) +} + +export function menuTree (payload: any) { + return axios.get('/system/menu/treeselect', { + params: payload + }) +} + +export function roleDelete (roleId: string | number) { + return axios.delete(`/system/role/${roleId}`) +} + +export function roleMenuTree (roleId: string | number) { + return axios.get(`/system/menu/roleMenuTreeselect/${roleId}`) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/api/sysLog.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/api/sysLog.ts new file mode 100644 index 000000000..13a42c08e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/api/sysLog.ts @@ -0,0 +1,18 @@ +import axios from 'axios' + +export function listAllLogFiles () { + return axios.get('/system/log/files') +} + +export function getAllLogConfig () { + return axios.get('/system/log') +} + +export function saveLogConfig (params: any) { + return axios.post('/system/log', params) +} + +export function download (filename: string, onDownloadProgress?: (event: any)=> void) { + return axios.get(`/system/log/download?filename=${filename}`, { onDownloadProgress: onDownloadProgress }) +} + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/api/sysSetting.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/api/sysSetting.ts new file mode 100644 index 000000000..ad3dcadf5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/api/sysSetting.ts @@ -0,0 +1,18 @@ +import axios from 'axios' +import { SysSetting } from '@/types/sysSetting' + +export function updateSysSetting (params: SysSetting) { + return axios.put('/system/setting', params) +} + +export function listSysSetting () { + return axios.get('/system/setting') +} + +export const checkUploadPath = (path: string) => { + return axios.get('/system/setting/checkUploadPath', { + params: { + path: path + } + }) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/api/task.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/api/task.ts new file mode 100644 index 000000000..77c3f837a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/api/task.ts @@ -0,0 +1,21 @@ +import axios from 'axios' + +export function list (query?: any) { + return axios.get('/sys/task/list/all', { params: query }) +} + +export function start (id: string | number) { + return axios.post(`/sys/task/start/${id}`) +} + +export function stop (id: string | number) { + return axios.post(`/sys/task/stop/${id}`) +} + +export function deleteTask (ids: string) { + return axios.delete(`/sys/task/${ids}`) +} + +export function taskById (id: string | number) { + return axios.get(`/sys/task/${id}`) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/api/user.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/api/user.ts new file mode 100644 index 000000000..85c1ecbbc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/api/user.ts @@ -0,0 +1,79 @@ +import axios from 'axios' +import {encrypt, encryptWithPublicKey} from '@/utils/jsencrypt' + +export interface LoginData { + username: string; + password: string; +} + +export interface LoginRes { + token: string; +} + +export async function login(data: LoginData) { + const formatData = {...data} + formatData.password = await encrypt(formatData.password) + return axios.post('/login', formatData) +} + +export function logout () { + return axios.post('/logout') +} + +export function getUserInfo () { + return axios.get('/system/user/profile') +} + +export function updateUserInfo (payload: any) { + return axios.put('/system/user/profile', payload) +} + +export function getMenuList () { + const locale = localStorage.getItem('locale') || 'zh-CN' + return axios.get('/getRouters', { + headers: { + language: locale + } + }) +} + +export function getUserList (query: any) { + return axios.get('/system/user/list', { + params: query + }) +} + +export function createUser (payload: any) { + const formData = { ...payload } + formData.password = encryptWithPublicKey(payload.password) + return axios.post('/system/user', formData) +} + +export function userDelete (userId: string) { + return axios.delete(`/system/user/${userId}`) +} + +export function changeStatus (payload: any) { + return axios.put('/system/user/changeStatus', payload) +} + +export function updateUser (payload: any) { + return axios.put('/system/user', payload) +} + +export function resetCode (payload: any) { + const formData = { ...payload } + formData.password = encryptWithPublicKey(payload.password) + return axios.put('/system/user/resetPwd', formData) +} + +export function updateCode (data: any) { + const formatData = { ...data } + formatData.newPassword = encryptWithPublicKey(formatData.newPassword) + formatData.oldPassword = encryptWithPublicKey(formatData.oldPassword) + return axios.put('/system/user/profile/updatePwd', formatData) +} + +export function userRoleList (userId?: string) { + return axios.get(`/system/user/${userId}`) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/api/whiteList.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/api/whiteList.ts new file mode 100644 index 000000000..6f7711841 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/api/whiteList.ts @@ -0,0 +1,19 @@ +import axios from 'axios' + +export function getWhiteList (query: any) { + return axios.get('/sys/whiteList/list', { + params: query + }) +} + +export function createWhiteList (payload: any) { + return axios.post('/sys/whiteList', payload) +} + +export function updateWhiteList (payload: any) { + return axios.put('/sys/whiteList', payload) +} + +export function whiteListDelete (id: string | number) { + return axios.delete(`/sys/whiteList/${id}`) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/HarmonyOS_Sans/HarmonyOS_Sans_Regular.ttf b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/HarmonyOS_Sans/HarmonyOS_Sans_Regular.ttf new file mode 100644 index 000000000..d7eccac8e Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/HarmonyOS_Sans/HarmonyOS_Sans_Regular.ttf differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/HarmonyOS_Sans/LICENSE.txt b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/HarmonyOS_Sans/LICENSE.txt new file mode 100644 index 000000000..211a701ac Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/HarmonyOS_Sans/LICENSE.txt differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.ttf b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.ttf new file mode 100644 index 000000000..aff150a13 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.ttf differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/HarmonyOS_Sans_SC/LICENSE.txt b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/HarmonyOS_Sans_SC/LICENSE.txt new file mode 100644 index 000000000..211a701ac Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/HarmonyOS_Sans_SC/LICENSE.txt differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/font.less b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/font.less new file mode 100644 index 000000000..0b88ae2a3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/font/font.less @@ -0,0 +1,9 @@ +@font-face{ + font-family: "HarmonyOS_Sans_SC"; + src: url("HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.ttf") format("truetype"); +} + +@font-face{ + font-family: "HarmonyOS_Sans"; + src: url("HarmonyOS_Sans/HarmonyOS_Sans_Regular.ttf") format("truetype"); +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/index.ts new file mode 100644 index 000000000..5261e71fc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/index.ts @@ -0,0 +1,3 @@ +const svg = require.context('./svg', false, /\.svg$/) +const requireAll = (requireContext: any) => requireContext.keys().map(requireContext) +requireAll(svg) diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/check-one-blue.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/check-one-blue.svg new file mode 100644 index 000000000..b57938811 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/check-one-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/clouduploadsharp.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/clouduploadsharp.svg new file mode 100644 index 000000000..1f4c3958e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/clouduploadsharp.svg @@ -0,0 +1 @@ + Upload to Cloud \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/developing.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/developing.svg new file mode 100644 index 000000000..2e0153b6d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/developing.svg @@ -0,0 +1,227 @@ + + + 编组 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/download.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/download.svg new file mode 100644 index 000000000..4a5492ae8 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/download.svg @@ -0,0 +1 @@ + Download diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/file-search-fail.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/file-search-fail.svg new file mode 100644 index 000000000..c5eee901d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/file-search-fail.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/file-search-suc.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/file-search-suc.svg new file mode 100644 index 000000000..e57b64087 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/file-search-suc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/file-zip.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/file-zip.svg new file mode 100644 index 000000000..8dc08bbdf --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/file-zip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/hexagon-one-blue.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/hexagon-one-blue.svg new file mode 100644 index 000000000..b4f93cfed --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/hexagon-one-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/hexagon-one-gray.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/hexagon-one-gray.svg new file mode 100644 index 000000000..dc76abd03 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/hexagon-one-gray.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/hexagon-one-green.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/hexagon-one-green.svg new file mode 100644 index 000000000..a45d2933e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/hexagon-one-green.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/hexagon-one-red.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/hexagon-one-red.svg new file mode 100644 index 000000000..5f152250e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/hexagon-one-red.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/home.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/home.svg new file mode 100644 index 000000000..1dec32e41 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/home.svg @@ -0,0 +1,10 @@ + + + 首页-未 + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/logs.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/logs.svg new file mode 100644 index 000000000..e6139f31b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/logs.svg @@ -0,0 +1,5 @@ + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/monitor.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/monitor.svg new file mode 100644 index 000000000..1fe330279 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/monitor.svg @@ -0,0 +1,12 @@ + + + 监控-未 + + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-busi-flow.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-busi-flow.svg new file mode 100644 index 000000000..528ff51d4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-busi-flow.svg @@ -0,0 +1,15 @@ + + + 业务流 + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-cluster-ops.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-cluster-ops.svg new file mode 100644 index 000000000..32de2d1d6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-cluster-ops.svg @@ -0,0 +1,15 @@ + + + 集群运维 + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-cluster.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-cluster.svg new file mode 100644 index 000000000..954d1fde6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-cluster.svg @@ -0,0 +1,50 @@ + + + 集群 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-data-flow.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-data-flow.svg new file mode 100644 index 000000000..38b7f9993 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-data-flow.svg @@ -0,0 +1,15 @@ + + + 数据流 + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-data-model.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-data-model.svg new file mode 100644 index 000000000..ba2dc69de --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-data-model.svg @@ -0,0 +1,23 @@ + + + 数据模型 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-deploy.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-deploy.svg new file mode 100644 index 000000000..e6a3a01ff --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-deploy.svg @@ -0,0 +1,14 @@ + + + 步骤方式 + + + + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-empty.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-empty.svg new file mode 100644 index 000000000..7d0ec0299 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-empty.svg @@ -0,0 +1,158 @@ + + + 编组备份 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-error.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-error.svg new file mode 100644 index 000000000..2ef337565 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-error.svg @@ -0,0 +1,9 @@ + + + 错误 + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-has-install-plugin.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-has-install-plugin.svg new file mode 100644 index 000000000..de221f6ab --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-has-install-plugin.svg @@ -0,0 +1,14 @@ + + + 安装插件图标 + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-host.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-host.svg new file mode 100644 index 000000000..0a8ae8b31 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-host.svg @@ -0,0 +1,30 @@ + + + 主机 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-install-success.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-install-success.svg new file mode 100644 index 000000000..01ab401f3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-install-success.svg @@ -0,0 +1,112 @@ + + + 编组备份 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-instance.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-instance.svg new file mode 100644 index 000000000..c2e29fb97 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-instance.svg @@ -0,0 +1,29 @@ + + + 实例 + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-mini-version.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-mini-version.svg new file mode 100644 index 000000000..b497ad91e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-mini-version.svg @@ -0,0 +1,38 @@ + + + 极简版新 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-offline-install.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-offline-install.svg new file mode 100644 index 000000000..4f454b589 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-offline-install.svg @@ -0,0 +1,27 @@ + + + 离线-新 + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-one-check.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-one-check.svg new file mode 100644 index 000000000..18667f66d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-one-check.svg @@ -0,0 +1,10 @@ + + + 一键自检 + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-online-install.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-online-install.svg new file mode 100644 index 000000000..4b30fb2ff --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-online-install.svg @@ -0,0 +1,26 @@ + + + 在线-新 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-pass.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-pass.svg new file mode 100644 index 000000000..8f2376498 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-pass.svg @@ -0,0 +1,10 @@ + + + 通过16 + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-warning.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-warning.svg new file mode 100644 index 000000000..8e1ef6a22 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops-warning.svg @@ -0,0 +1,9 @@ + + + 警告16 + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops.svg new file mode 100644 index 000000000..e971c033c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/ops.svg @@ -0,0 +1,12 @@ + + + 运维-未 + + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/plugin.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/plugin.svg new file mode 100644 index 000000000..27f0e3dbb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/plugin.svg @@ -0,0 +1,16 @@ + + + 插件-未 + + + + + + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/rar-file.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/rar-file.svg new file mode 100644 index 000000000..38ee54ed8 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/rar-file.svg @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/resource.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/resource.svg new file mode 100644 index 000000000..8fba22504 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/resource.svg @@ -0,0 +1,24 @@ + + + 资源中心-未 + + + + + + + + + + + + + + + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/security.svg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/security.svg new file mode 100644 index 000000000..3ef3a6ac3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/icons/svg/security.svg @@ -0,0 +1,10 @@ + + + 安全-未 + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/bg.jpg b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/bg.jpg new file mode 100644 index 000000000..75f2b3fef Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/bg.jpg differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/common/avatar.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/common/avatar.png new file mode 100644 index 000000000..fbabc9ee2 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/common/avatar.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/common/down-arrow.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/common/down-arrow.png new file mode 100644 index 000000000..7b26ed601 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/common/down-arrow.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/common/fast.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/common/fast.png new file mode 100644 index 000000000..1b0a291d6 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/common/fast.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/common/photo.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/common/photo.png new file mode 100644 index 000000000..0cd201bed Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/common/photo.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/default-avatar.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/default-avatar.png new file mode 100644 index 000000000..0ca845626 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/default-avatar.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/1.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/1.png new file mode 100644 index 000000000..581f2847b Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/1.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/2.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/2.png new file mode 100644 index 000000000..8ba85ef8b Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/2.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/3.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/3.png new file mode 100644 index 000000000..6a3f719a3 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/3.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/4.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/4.png new file mode 100644 index 000000000..725b8f597 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/4.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/5.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/5.png new file mode 100644 index 000000000..6bfc18675 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/5.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/6.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/6.png new file mode 100644 index 000000000..8cc039687 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/fast/6.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/login-banner.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/login-banner.png new file mode 100644 index 000000000..a3b377603 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/login-banner.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/login-logo.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/login-logo.png new file mode 100644 index 000000000..f7f013eb3 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/login-logo.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-done-en.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-done-en.png new file mode 100644 index 000000000..6f8d47dd5 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-done-en.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-done.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-done.png new file mode 100644 index 000000000..45db0257d Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-done.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-fail-en.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-fail-en.png new file mode 100644 index 000000000..0ec239d4b Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-fail-en.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-fail.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-fail.png new file mode 100644 index 000000000..a692013dc Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-fail.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-import-en.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-import-en.png new file mode 100644 index 000000000..c318fef22 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-import-en.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-import.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-import.png new file mode 100644 index 000000000..79ac207f7 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-import.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-ing-en.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-ing-en.png new file mode 100644 index 000000000..5074d9686 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-ing-en.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-ing.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-ing.png new file mode 100644 index 000000000..af0d44c0e Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/bulkimport-ing.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/busi-flow.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/busi-flow.png new file mode 100644 index 000000000..6ab7000f5 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/busi-flow.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/cluster-ops.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/cluster-ops.png new file mode 100644 index 000000000..cdb14a63c Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/cluster-ops.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/cluster.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/cluster.png new file mode 100644 index 000000000..780963497 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/cluster.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/data-flow.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/data-flow.png new file mode 100644 index 000000000..9a4e522ce Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/data-flow.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/data-modeling.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/data-modeling.png new file mode 100644 index 000000000..000d4e6d8 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/data-modeling.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/deploy-way-active.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/deploy-way-active.png new file mode 100644 index 000000000..016c846de Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/deploy-way-active.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/deploy-way.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/deploy-way.png new file mode 100644 index 000000000..0eeaa021c Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/deploy-way.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/empty.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/empty.png new file mode 100644 index 000000000..7dfe04867 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/empty.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/env-monitor-active.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/env-monitor-active.png new file mode 100644 index 000000000..978c16b97 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/env-monitor-active.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/env-monitor.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/env-monitor.png new file mode 100644 index 000000000..5e409cd5a Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/env-monitor.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/exe-install-active.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/exe-install-active.png new file mode 100644 index 000000000..930bc5334 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/exe-install-active.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/exe-install.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/exe-install.png new file mode 100644 index 000000000..c98ca1e83 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/exe-install.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/host.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/host.png new file mode 100644 index 000000000..5167ff5bd Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/host.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-config-active.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-config-active.png new file mode 100644 index 000000000..c7aa8c1b3 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-config-active.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-config.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-config.png new file mode 100644 index 000000000..cc7555918 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-config.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-prompt-active.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-prompt-active.png new file mode 100644 index 000000000..8d74f70e4 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-prompt-active.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-prompt.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-prompt.png new file mode 100644 index 000000000..d4536fd04 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-prompt.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-success.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-success.png new file mode 100644 index 000000000..38eddaa4a Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/install-success.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/instance.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/instance.png new file mode 100644 index 000000000..eb6ed87fc Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/instance.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/mini-version.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/mini-version.png new file mode 100644 index 000000000..ceb9be9e6 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/mini-version.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/offline.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/offline.png new file mode 100644 index 000000000..119099556 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/offline.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/one-check.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/one-check.png new file mode 100644 index 000000000..72e91ae07 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/one-check.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/online.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/online.png new file mode 100644 index 000000000..34c3d546f Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/online.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/plugin.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/plugin.png new file mode 100644 index 000000000..2f2d04745 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/plugin.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/robot.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/robot.png new file mode 100644 index 000000000..6c7998f1d Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/images/ops/robot.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/logo.png b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/logo.png new file mode 100644 index 000000000..d6da897a8 Binary files /dev/null and b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/logo.png differ diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/breakpoint.less b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/breakpoint.less new file mode 100644 index 000000000..584f21e5a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/breakpoint.less @@ -0,0 +1,19 @@ +// ==============breakpoint============ + +// Extra small screen / phone +@screen-xs: 480px; + +// Small screen / tablet +@screen-sm: 576px; + +// Medium screen / desktop +@screen-md: 768px; + +// Large screen / wide desktop +@screen-lg: 992px; + +// Extra large screen / full hd +@screen-xl: 1200px; + +// Extra extra large screen / large desktop +@screen-xxl: 1600px; diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/global.less b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/global.less new file mode 100644 index 000000000..b96a166e8 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/global.less @@ -0,0 +1,512 @@ +@import "../font/font"; +@import "standard"; + +* { + box-sizing: border-box; +} + +html, +body { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + font-size: 14px; + font-family: "HarmonyOS_Sans_SC", "HarmonyOS_Sans", "-apple-system", BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", Arial, "Helvetica Neue", Helvetica, "Roboto", sans-serif; + background-color: var(--color-bg-1); + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; +} + +/********** css custom specification ***********/ +body { + --margin-base: 20px; // Margin + --margin-vertical: 15px; // Margins (Vertical) + + --padding-base: 20px; // Padding + --padding-vertical: 15px; // Padding (vertical direction) + + --o-popconfirm-bg: #fff; //light theme el-popConfirm background-color +} + +/********** css custom specification dark ***********/ +body.dark { + --o-popconfirm-bg: #373739; // dark theme el-popConfirm background-color +} + +// body style +.app-container { + padding: 8px; + + .main-bd { + background: var(--color-bg-2); + border-radius: 2px; + min-height: calc(100vh - 114px); + } +} + +.label-color { + color: var(--color-text-2) +} + + +// level +.z-0 { + z-index: 0; +} + +.z-1 { + z-index: 1; +} + +.z-10 { + z-index: 10; +} + +.z-20 { + z-index: 20; +} + +.z-30 { + z-index: 30; +} + +.z-40 { + z-index: 40; +} + +.z-50 { + z-index: 50; +} + +.z-auto { + z-index: auto; +} + +.mb16 { + margin-bottom: 16px; +} + +.w550 { + width: 550px !important; +} + +.w650 { + width: 650px !important; +} + +// common style +.clearfix { + + &:before, + &:after { + content: " "; + display: table; + } + + &:after { + clear: both; + } +} + +.fl { + float: left; +} + +.fr { + float: right; +} + +// General style of flex layout +.flex-col { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.flex-col-start { + display: flex; + flex-direction: column; + align-items: flex-start; +} + +.flex-col-end { + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.flex-row { + display: flex; + align-items: center; +} + +.flex-row-center { + display: flex; + align-items: center; + justify-content: center; +} + +.flex-row-start { + display: flex; + align-items: center; + justify-content: flex-start; +} + +.flex-row-end { + display: flex; + align-items: center; + justify-content: flex-end; +} + +.flex-between { + display: flex; + justify-content: space-between; +} + +.echarts-tooltip-diy { + background: linear-gradient(304.17deg, + rgba(253, 254, 255, 0.6) -6.04%, + rgba(244, 247, 252, 0.6) 85.2%) !important; + border: none !important; + backdrop-filter: blur(10px) !important; + /* Note: backdrop-filter has minimal browser support */ + + border-radius: 6px !important; + + .content-panel { + display: flex; + justify-content: space-between; + padding: 0 9px; + background: rgba(255, 255, 255, 0.8); + width: 164px; + height: 32px; + line-height: 32px; + box-shadow: 6px 0px 20px rgba(34, 87, 188, 0.1); + border-radius: 4px; + margin-bottom: 4px; + } + + .tooltip-title { + margin: 0 0 10px 0; + } + + p { + margin: 0; + } + + .tooltip-title, + .tooltip-value { + font-size: 13px; + line-height: 15px; + display: flex; + align-items: center; + text-align: right; + color: #1d2129; + font-weight: bold; + } + + .tooltip-item-icon { + display: inline-block; + margin-right: 8px; + width: 10px; + height: 10px; + border-radius: 50%; + } +} + +.general-card { + border-radius: 4px; + border: none; + + &>.arco-card-header { + height: auto; + padding: 20px; + border: none; + } + + &>.arco-card-body { + padding: 0 20px 20px 20px; + } +} + +.split-line { + border-color: rgb(var(--gray-2)); +} + +.arco-table-cell { + .circle { + display: inline-block; + margin-right: 4px; + width: 6px; + height: 6px; + border-radius: 50%; + background-color: rgb(var(--blue-6)); + + &.pass { + background-color: rgb(var(--green-6)); + } + } +} + +.custom-winbox { + padding: 35px 0px 0px 0px; + z-index: 100 !important; +} + +// empty icon style +.empty-c { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-color: white; + border-radius: 8px; + padding: 20px; + + .tip-content { + color: var(--color-neutral-4); + } +} + +// arco style +input:-webkit-autofill, +input:-webkit-autofill:focus { + transition: background-color 600000s 0s, color 600000s 0s; +} + +.arco-input-wrapper .arco-input[disabled] { + -webkit-text-fill-color: var(--color-text-3) +} + +.arco-btn-primary[type='button'] { + color: var(--color-bg-2); + + &:hover { + color: var(--color-bg-2); + } + + &.arco-btn-loading { + color: var(--color-bg-2); + } +} + +.inputMaxWarning { + display: block; + color: var(--o-color-danger); + width: 100%; + padding-top: 4px; + height: 16px; + font-size: 12px; +} + +// el-popConfirm style +.el-popper.o-popper-confirm { + padding: 12px 8px !important; + width: fit-content !important; + min-width: 150px; + + .el-popconfirm__action { + text-align: center !important; + + >button { + height: 24px; + width: 48px; + font-size: 12px; + } + + button.is-text { + border: 1px solid var(--o-button-border-color); + + span { + color: var(--o-button-color) !important; + } + + &:hover { + border-color: var(--o-button-border-color_hover); + } + + &:active { + background-color: var(--o-button-bg-color_active); + color: var(--o-button-color_active); + border-color: var(--o-button-border-color_active); + } + } + } +} + +.el-popper.operatePopper { + background-color: var(--o-bg-color-light2) !important; + padding: 12px 0 !important; + min-width: 100px !important; + height: 84px; + cursor: pointer; + border: 1px solid var(--o-color-primary-secondary) !important; + + .operateItems { + display: flex; + flex-direction: column; + justify-content: space-between; + height: 100%; + + .el-button { + margin-left: 0; + height: 22px; + width: 100%; + padding: 0 16px; + line-height: 22px; + font-size: 14px; + text-align: left; + + span { + color: var(--o-text-color-secondary) !important; + } + + &:hover { + span { + color: var(--o-color-primary-secondary) !important; + } + } + + &:active { + span { + color: var(--o-color-primary) !important; + } + } + } + } +} + +.openDesignTable { + font-size: 14px !important; + + th, + .flex-row-start button { + font-size: 14px; + height: 24px; + } +} + +.odDialogPagination { + margin-top: 24px !important; +} + +.openDesignDrawer { + --el-drawer-bg-color: var(--o-bg-color-base) !important; + + .el-drawer__header { + color: var(--o-text-color-primary) !important; + } + + .el-drawer__body { + color: var(--o-text-color-secondary) !important; + } + + .el-drawer__footer { + width: 100%; + height: 48px; + padding: 8px 20px; + box-shadow: 0 -1px 16px 0 rgba(0, 0, 0, 0.08); + } +} + +.openDesignDialog { + .el-dialog__header { + height: 54px; + } + + .el-dialog__body { + + .el-form-item__label, + .el-form-item__content { + font-size: 14px; + justify-content: flex-start; + } + + .el-form-item.is-required { + .el-input__inner { + font-size: 14px; + } + } + } +} + +.openDesignFooterButterGap { + position: fixed; + bottom: 0; + right: 0; + padding: 8px 26px; + width: 100%; + height: 48px; + box-shadow: 0 -1px 16px 0 rgba(0, 0, 0, 0.08); + background-color: var(--o-bg-color-base); + + display: flex; + justify-content: space-between; + + .footerButtonArea { + display: flex; + justify-content: flex-end; + gap: 10px; + margin-left: 220px; + } + + .el-button { + margin-right: 0; + } +} + +.openDesignForm { + .el-form-item.is-required { + .el-form-item__label { + margin-left: -8px; + margin-right: 8px; + } + } +} + +// el-header +.openDesignHeader.el-header { + background-color: var(--o-bg-color-base); + height: 50px; + padding-left: 24px; + overflow: auto; + + .el-page-header { + padding: 11px 0px; + + .el-page-header__header { + height: 28px; + } + + .el-divider--vertical { + margin: 0 6px; + } + } + + .text-color { + color: var(--o-text-color-primary); + } +} + +.statusDot { + width: 8px; + height: 8px; + border-radius: 50%; + display: inline-block; + margin-right: 8px; +} + +.runingColor { + background-color: var(--o-color-success); +} + +.uninstallColor { + background-color: var(--o-color-info); +} + +.offLineColor { + background-color: var(--o-color-danger); +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/ops/ops.less b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/ops/ops.less new file mode 100644 index 000000000..ca1f4664c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/ops/ops.less @@ -0,0 +1,54 @@ +// Installation page general style +// Toggle the style of the panel +.panel-c { + width: 100%; + height: calc(100vh - 157px - 36px - 32px); +} + +.panel-header {} + +.panel-body { + height: 70%; + overflow-y: auto; +} + +.panel-footer { + display: flex; + justify-content: center; +} + +// Install select card style +.card-item-c { + border-radius: 8px; + border: 1px solid #C9CDD4; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + cursor: pointer; + box-shadow: 0px 4px 20px 0px rgba(255, 255, 255, 0.5); + transition: all 0.3s ease-in-out; + &:hover { + box-shadow: 0px 4px 20px 0px rgba(153, 153, 153, 0.6); + cursor: pointer; + } +} + +// select page select option select style +.center-item-active { + cursor: pointer; + padding: 8px; + border-radius: 8px; + border-color: rgb(var(--primary-6)) !important; + box-shadow: 0px 4px 20px 0px rgba(var(--primary-6), 0.4); +} + +.icon-size-s { + width: 60px; + height: 60px; +} + +.icon-size { + width: 100px; + height: 100px; +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/standard.less b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/standard.less new file mode 100644 index 000000000..082a2a8e3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/standard.less @@ -0,0 +1,108 @@ +@import "variables"; + +.mr-xs { + margin-right: @gap-xs; +} + +.mr-s { + margin-right: @gap-s; +} + +.mr { + margin-right: @gap; +} + +.mr-lg { + margin-right: @gap-lg; +} + +.mr-xlg { + margin-right: @gap-xlg; +} + +.mb-xs { + margin-bottom: @gap-xs; +} + +.mb-s { + margin-bottom: @gap-s; +} + +.mb { + margin-bottom: @gap; +} + +.mb-lg { + margin-bottom: @gap-lg; +} + +.mb-xlg { + margin-bottom: @gap-xlg; +} + +.icon-s { + width: 16px !important; + height: 16px !important; +} + +.icon { + width: 22px !important; + height: 22px !important; +} + +.icon-lg { + width: 70px !important; + height: 70px !important; +} + +.icon-xlg { + width: 90px !important; + height: 90px !important; +} + +.ft-s { + font-size: @font-s; +} + +// The current default font size is 14 +.ft-m { + font-size: @font-m; +} + +.ft-lg { + font-size: @font-lg; +} + +.ft-xlg { + font-size: @font-xlg; +} + +.ft-b { + font-weight: 600; +} + +.dlg-s { + .el-dialog { + width: 600px; + } +} + +.dlg { + .el-dialog { + width: 800px; + } +} + +.dlg-lg { + .el-dialog { + width: 1100px; + } +} + +.full-w { + width: 100% !important; +} + +.full-h { + height: 100% !important; +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/variables.less b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/variables.less new file mode 100644 index 000000000..6b3b6fef1 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/assets/style/variables.less @@ -0,0 +1,12 @@ +// margin size +@gap-xlg: 90px; +@gap-lg: 30px; +@gap: 20px; +@gap-s: 8px; +@gap-xs: 4px; + +// Font size default font 14px +@font-s: 12px; +@font-m: 16px; +@font-lg: 20px; +@font-xlg: 24px; diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/breadcrumb/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/components/breadcrumb/index.vue new file mode 100644 index 000000000..457f6668d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/breadcrumb/index.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/chart/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/components/chart/index.vue new file mode 100644 index 000000000..b4f9db0b7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/chart/index.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/downloadNotification/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/components/downloadNotification/index.vue new file mode 100644 index 000000000..4c58162c5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/downloadNotification/index.vue @@ -0,0 +1,156 @@ + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/file-upload/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/components/file-upload/index.vue new file mode 100644 index 000000000..9522b2214 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/file-upload/index.vue @@ -0,0 +1,104 @@ + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/fusion-search/fusionDateRange.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/components/fusion-search/fusionDateRange.vue new file mode 100644 index 000000000..f608d25a6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/fusion-search/fusionDateRange.vue @@ -0,0 +1,51 @@ + + + + + \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/fusion-search/fusionMulti.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/components/fusion-search/fusionMulti.vue new file mode 100644 index 000000000..84dbda573 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/fusion-search/fusionMulti.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/fusion-search/fusionSelect.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/components/fusion-search/fusionSelect.vue new file mode 100644 index 000000000..e3590b974 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/fusion-search/fusionSelect.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/fusion-search/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/components/fusion-search/index.vue new file mode 100644 index 000000000..269aee249 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/fusion-search/index.vue @@ -0,0 +1,266 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/components/index.ts new file mode 100644 index 000000000..816ff0c51 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/index.ts @@ -0,0 +1,37 @@ +import { App } from 'vue' +import { use } from 'echarts/core' +import { CanvasRenderer } from 'echarts/renderers' +import { BarChart, LineChart, PieChart, RadarChart } from 'echarts/charts' +import { + GridComponent, + TooltipComponent, + LegendComponent, + DataZoomComponent, + GraphicComponent +} from 'echarts/components' +import Chart from './chart/index.vue' +import Breadcrumb from './breadcrumb/index.vue' +import SvgIcon from './svg-icon/index.vue' + +// Manually introduce ECharts modules to reduce packing size + +use([ + CanvasRenderer, + BarChart, + LineChart, + PieChart, + RadarChart, + GridComponent, + TooltipComponent, + LegendComponent, + DataZoomComponent, + GraphicComponent +]) + +export default { + install (Vue: App) { + Vue.component('Chart', Chart) + Vue.component('Breadcrumb', Breadcrumb) + Vue.component('svg-icon', SvgIcon) + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/menu/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/components/menu/index.vue new file mode 100644 index 000000000..71a8b3451 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/menu/index.vue @@ -0,0 +1,238 @@ + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/menu/useMenuTree.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/components/menu/useMenuTree.ts new file mode 100644 index 000000000..af188781a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/menu/useMenuTree.ts @@ -0,0 +1,68 @@ +import { computed } from 'vue' +import { RouteRecordRaw, RouteRecordNormalized } from 'vue-router' +import usePermission from '@/hooks/permission' +import { useAppStore } from '@/store' +import appClientMenus from '@/router/appMenus' + +export default function useMenuTree () { + const permission = usePermission() + const appStore = useAppStore() + const appRoute = computed(() => { + if (appStore.menuFromServer) { + return appStore.appAsyncMenus + } + return appClientMenus + }) + const menuTree = computed(() => { + const copyRouter = JSON.parse(JSON.stringify(appRoute.value)) + copyRouter.sort((a: RouteRecordNormalized, b: RouteRecordNormalized) => { + return (a.meta.order || 0) - (b.meta.order || 0) + }) + function travel (_routes: RouteRecordRaw[], layer: number) { + if (!_routes) return null + + const collector: any = _routes.map((element) => { + // no access + if (!permission.accessRouter(element)) { + return null + } + + if (element.meta?.hideInMenu) { + return null + } + + // leaf node + if (element.meta?.hideChildrenInMenu || !element.children) { + element.children = [] + return element + } + + // route filter hideInMenu true + element.children = element.children.filter( + (x) => x.meta?.hideInMenu !== true + ) + + // Associated child node + const subItem = travel(element.children, layer + 1) + + if (subItem.length) { + element.children = subItem + return element + } + // the else logic + if (layer > 1) { + element.children = subItem + return element + } + + return null + }) + return collector.filter(Boolean) + } + return travel(copyRouter, 0) + }) + + return { + menuTree + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/navbar/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/components/navbar/index.vue new file mode 100644 index 000000000..a27372490 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/navbar/index.vue @@ -0,0 +1,348 @@ + + + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/svg-icon/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/components/svg-icon/index.vue new file mode 100644 index 000000000..1669c086e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/svg-icon/index.vue @@ -0,0 +1,17 @@ + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/tab-bar/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/components/tab-bar/index.vue new file mode 100644 index 000000000..09c04ab0b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/tab-bar/index.vue @@ -0,0 +1,138 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/components/tab-bar/tab-item.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/components/tab-bar/tab-item.vue new file mode 100644 index 000000000..f61480949 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/components/tab-bar/tab-item.vue @@ -0,0 +1,213 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/config/settings.json b/ER-Model/openGauss-datakit/visualtool-ui/src/config/settings.json new file mode 100644 index 000000000..98a20a7cc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/config/settings.json @@ -0,0 +1,15 @@ +{ + "theme": "light", + "colorWeak": false, + "navbar": true, + "menu": true, + "hideMenu": false, + "menuCollapse": false, + "themeColor": "#E41D1D", + "menuWidth": 220, + "globalSettings": false, + "device": "desktop", + "tabBar": true, + "menuFromServer": true, + "serverMenu": [] +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/chart-option.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/chart-option.ts new file mode 100644 index 000000000..537eb56a6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/chart-option.ts @@ -0,0 +1,26 @@ +import { computed } from 'vue' +import { EChartsOption } from 'echarts' +import { useAppStore } from '@/store' + +// for code hints +// import { SeriesOption } from 'echarts'; +// Because there are so many configuration items, this provides a relatively convenient code hint. +// When using vue, pay attention to the reactive issues. It is necessary to ensure that corresponding functions can be triggered, TypeScript does not report errors, and code writing is convenient. +interface optionsFn { + (isDark: boolean): EChartsOption; +} + +export default function useChartOption (sourceOption: optionsFn) { + const appStore = useAppStore() + const isDark = computed(() => { + return appStore.theme === 'dark' + }) + // echarts support https://echarts.apache.org/zh/theme-builder.html + // It's not used here + const chartOption = computed(() => { + return sourceOption(isDark.value) + }) + return { + chartOption + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/loading.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/loading.ts new file mode 100644 index 000000000..63bf50fd9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/loading.ts @@ -0,0 +1,16 @@ +import { ref } from 'vue' + +export default function useLoading (initValue = false) { + const loading = ref(initValue) + const setLoading = (value: boolean) => { + loading.value = value + } + const toggle = () => { + loading.value = !loading.value + } + return { + loading, + setLoading, + toggle + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/locale.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/locale.ts new file mode 100644 index 000000000..5577956c4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/locale.ts @@ -0,0 +1,19 @@ +import { computed } from 'vue' +import { useI18n } from 'vue-i18n' +import { Message } from '@arco-design/web-vue' + +export default function useLocale () { + const i18 = useI18n() + const currentLocale = computed(() => { + return i18.locale.value + }) + const changeLocale = (value: string) => { + i18.locale.value = value + localStorage.setItem('locale', value) + Message.success(value === 'zh-CN' ? '切换为简体中文' : 'Switch to English') + } + return { + currentLocale, + changeLocale + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/permission.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/permission.ts new file mode 100644 index 000000000..d7512405e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/permission.ts @@ -0,0 +1,33 @@ +import { RouteLocationNormalized, RouteRecordRaw } from 'vue-router' +import { useUserStore } from '@/store' + +export default function usePermission () { + const userStore = useUserStore() + return { + accessRouter (route: RouteLocationNormalized | RouteRecordRaw) { + return ( + !route.meta?.requiresAuth || + !route.meta?.roles || + route.meta?.roles?.includes('*') || + route.meta?.roles?.includes(userStore.role) + ) + }, + findFirstPermissionRoute (_routers: any, role = 'admin') { + const cloneRouters = [..._routers] + while (cloneRouters.length) { + const firstElement = cloneRouters.shift() + if ( + firstElement?.meta?.roles?.find((el: string[]) => { + return el.includes('*') || el.includes(role) + }) + ) + return { name: firstElement.name } + if (firstElement?.children) { + cloneRouters.push(...firstElement.children) + } + } + return null + } + // You can add any rules you want + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/request.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/request.ts new file mode 100644 index 000000000..2b6b35a06 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/request.ts @@ -0,0 +1,26 @@ +import { ref, UnwrapRef } from 'vue' +import { AxiosResponse } from 'axios' +import { HttpResponse } from '@/api/interceptor' +import useLoading from './loading' + +// use to fetch list +// Don't use async function. It doesn't work in async function. +// Use the bind function to add parameters +// example: useRequest(api.bind(null, {})) + +export default function useRequest ( + api: () => Promise>, + defaultValue = [] as unknown as T, + isLoading = true +) { + const { loading, setLoading } = useLoading(isLoading) + const response = ref(defaultValue) + api() + .then((res) => { + response.value = res.data as unknown as UnwrapRef + }) + .finally(() => { + setLoading(false) + }) + return { loading, response } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/responsive.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/responsive.ts new file mode 100644 index 000000000..c72e4a2fb --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/responsive.ts @@ -0,0 +1,32 @@ +import { onMounted, onBeforeMount, onBeforeUnmount } from 'vue' +import { useDebounceFn } from '@vueuse/core' +import { useAppStore } from '@/store' +import { addEventListen, removeEventListen } from '@/utils/event' + +const WIDTH = 992 // https://arco.design/vue/component/grid#responsivevalue + +function queryDevice () { + const rect = document.body.getBoundingClientRect() + return rect.width - 1 < WIDTH +} + +export default function useResponsive (immediate?: boolean) { + const appStore = useAppStore() + function resizeHandler () { + if (!document.hidden) { + const isMobile = queryDevice() + appStore.toggleDevice(isMobile ? 'mobile' : 'desktop') + appStore.toggleMenu(isMobile) + } + } + const debounceFn = useDebounceFn(resizeHandler, 100) + onMounted(() => { + if (immediate) debounceFn() + }) + onBeforeMount(() => { + addEventListen(window, 'resize', debounceFn) + }) + onBeforeUnmount(() => { + removeEventListen(window, 'resize', debounceFn) + }) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/showMessage.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/showMessage.ts new file mode 100644 index 000000000..2539167be --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/showMessage.ts @@ -0,0 +1,36 @@ +import { ElMessage } from 'element-plus' +import { + IconSuccess, + IconAlarm, + IconError, + IconRemind +} from '@computing/opendesign-icons' + +interface IconMap { + info: any; + success: any; + warning: any; + error: any; +} + +type Status = keyof IconMap; + +const icons = { + info: IconRemind, + success: IconSuccess, + warning: IconAlarm, + error: IconError +} + +const showMessage = (type: Status, msg = '', duration?: number): void => { + ElMessage({ + type, + showClose: true, + message: msg, + icon: icons[type], + customClass: `o-message--${type}`, + duration: duration ?? 3000 + }) +} + +export default showMessage diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/themes.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/themes.ts new file mode 100644 index 000000000..adafe0a39 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/themes.ts @@ -0,0 +1,12 @@ +import { computed } from 'vue' +import { useAppStore } from '@/store' + +export default function useThemes () { + const appStore = useAppStore() + const isDark = computed(() => { + return appStore.theme === 'dark' + }) + return { + isDark + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/user.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/user.ts new file mode 100644 index 000000000..a3ae722a4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/user.ts @@ -0,0 +1,15 @@ +import { Message } from '@arco-design/web-vue' + +import { useUserStore } from '@/store' + +export default function useUser () { + const userStore = useUserStore() + const logout = async () => { + await userStore.logout() + Message.success('Logout success') + window.location.href = '/' + } + return { + logout + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/visible.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/visible.ts new file mode 100644 index 000000000..8323af8e7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/hooks/visible.ts @@ -0,0 +1,16 @@ +import { ref } from 'vue' + +export default function useVisible (initValue = false) { + const visible = ref(initValue) + const setVisible = (value: boolean) => { + visible.value = value + } + const toggle = () => { + visible.value = !visible.value + } + return { + visible, + setVisible, + toggle + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/layout/default-layout.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/layout/default-layout.vue new file mode 100644 index 000000000..c4703e478 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/layout/default-layout.vue @@ -0,0 +1,167 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/layout/page-layout.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/layout/page-layout.vue new file mode 100644 index 000000000..ee1f6e421 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/layout/page-layout.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/layout/parent-view/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/layout/parent-view/index.vue new file mode 100644 index 000000000..6c71efda3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/layout/parent-view/index.vue @@ -0,0 +1,5 @@ + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/locale/en-US.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/en-US.ts new file mode 100644 index 000000000..3050f8804 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/en-US.ts @@ -0,0 +1,6 @@ + +import en from './locale/en.json' + +export default { + ...en +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/locale/hrefLink/urlEn.json b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/hrefLink/urlEn.json new file mode 100644 index 000000000..d518981a8 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/hrefLink/urlEn.json @@ -0,0 +1,3 @@ +{ + "openGaussDownloadPage": "https://opengauss.org/en/download/archive/" +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/locale/hrefLink/urlZh.json b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/hrefLink/urlZh.json new file mode 100644 index 000000000..025276576 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/hrefLink/urlZh.json @@ -0,0 +1,3 @@ +{ + "openGaussDownloadPage": "https://opengauss.org/zh/download/archive/" +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/locale/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/index.ts new file mode 100644 index 000000000..d851673b0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/index.ts @@ -0,0 +1,24 @@ +import { createI18n } from 'vue-i18n' +import en from './en-US' +import cn from './zh-CN' +import urlEn from './hrefLink/urlEn.json' +import urlZh from './hrefLink/urlZh.json' + +export const LOCALE_OPTIONS = [ + { label: '简体中文', value: 'zh-CN' }, + { label: 'English', value: 'en-US' } +] +const defaultLocale = localStorage.getItem('locale') || 'zh-CN' + +const i18n = createI18n({ + locale: defaultLocale, + fallbackLocale: 'en-US', + globalInjection: true, + allowComposition: true, + messages: { + 'en-US': { ...en, ...urlEn }, + 'zh-CN': { ...cn, ...urlZh } + } +}) + +export default i18n diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/locale/locale/en.json b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/locale/en.json new file mode 100644 index 000000000..f69ed7bbf --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/locale/en.json @@ -0,0 +1,1089 @@ +{ + "errorCode": { + "400": "Request params is bad", + "401": "Unauthorized", + "403": "Permission denied", + "50101": "Plugin stop error", + "50102": "Plugin start error", + "50103": "Plugin uninstall error", + "50104": "Plugin install error", + "50105": "Role is exists", + "50107": "User phone is exists", + "50108": "User email is exists", + "50109": "Origin pass is error", + "50110": "New password can't be same as old password", + "50115": "User account is exists", + "50116": "This plugin menu has submenus for other plugins", + "50126": "Whitelist name already exists", + "default": "System unknown error, please feedback to administrator" + }, + "manage": { + "index": { + "5m5v55wx37o0": "Please input ID etc.", + "5m5v55wxj5s0": "Status", + "5m5v55wxjf40": "Please select", + "5m5v55wxjjs0": "Disabled", + "5m5v55wxjo00": "Enabled", + "5m5v55wxjsg0": "Search", + "5m5v55wxjwg0": "Reset", + "5m5v55wxk140": "Install Plugin", + "5m5v55wxk580": "Detail", + "5m5v55wxkrw0": "Uninstall", + "5m5v55wxkxw0": "Drag files here, or ", + "5m5v55wxl440": "click to upload", + "5m5v55wxla00": "Only .jar files are allowed to be imported", + "5m5v55wxldg0": "Cancel", + "5m5v55wxlgg0": "Confirm", + "5m5v55wxljs0": "start success", + "5m5v55wxlmg0": "stop success", + "5m5v55wxlso0": "uninstall success", + "5mdcikaqmi80": "install success", + "5n6f62aai3k0": "Enable", + "5n6f62aajx80": "Disable", + "else1": "Type: ", + "else2": "No", + "else3": "Are you sure to uninstall the plugin with id {id}?", + "OPEN_SOURCE": "Open source", + "FREE": "Free", + "TRIAL": "Trial", + "PAID": "Official", + "update_authorization": "Update authorization", + "authorization_detail": "Authorization detail", + "plugin_name": "Plugin name", + "authorization": "Authorization", + "plugin_description": "Description", + "activation_time": "Activation time", + "expires": "Expires", + "company": "Company", + "phone": "Phone", + "email": "Email", + "address": "Address", + "manual": "Manual", + "demo": "DEMO", + "websocketErrorMsg": "WebSocket unresponsive - closing download progress panel", + "onlineDownload": "Online Download", + "notificationText": "Match the following installation package based on the configuration and download it:" + }, + "PluginInstall": { + "observabilitySqlDiagnosis": "Intelligent Diagnosis Plugin", + "observabilityInstance": "Instance Monitoring Plugin", + "monitorTools": "Database monitoring plugin", + "oauthLogin": "Oauth Login Plugin", + "alertMonitor": "Alert Monitor Plugin", + "MetaTune": "Database Intelligent Parameter Tuning Tool", + "containerManagement": "Container Management Plugin", + "compatibilityAssessment": "Compatibility Evaluation Tool", + "observabilityLogSearch": "Log Retrieval Plugin", + "dataMigration": "Data Migration Plugin", + "baseOps": "Basic Operation Plugin", + "webdsPlugin": "Data Studio Plugin", + "dragMsg": "Drag files here, or", + "clickToUpload": "click to upload", + "uploadLimit": "Only .jar files are allowed to be imported", + "pluginName": "Plugin Name", + "onLineURL": "Online Download URL", + "onlineMsg": "Match plugins based on the configuration and display their download URLs for installation. If no matching plugin is found or it's not the target plugin, you may proceed with ", + "offlineUpload": "Offline Upload", + "uploadErrAlert": "Installation failed, please upload a different installation package", + "userMsgErr": "Incomplete form submission or user data retrieval failed", + "fileTypeRestriction": "Please select a file of the specified type", + "fileSizeLimit": "File size exceeds the limit" + } + }, + "components": { + "FusionSearch": { + "addFilters": "Please add filters", + "confirm": "Confirm", + "cancel": "Cancel", + "inputMaxWarnText": "Maximum length has been reached" + }, + "UpdateDefaultCode": { + "5nus586u4jg0": "Rest Default Password", + "5n7vbkp1s580": "Change Password", + "5n7vbkp1t2s0": "Old Password", + "5n7vbkp1tb80": "Old password cannot be empty", + "5n7vbkp1tiw0": "Please enter old password", + "5n7vbkp1two0": "New Password", + "5n7vbkp1u340": "New password cannot be empty", + "5n7vbkp1u8o0": "The length is between 6 and 20 numbers, letters, and special characters, at least two combinations", + "5n7vbkp1uyw0": "Please enter a new password", + "5n7vbkp1v8o0": "Confirm Password", + "5n7vbkp1vg00": "Confirm password can not be blank", + "5n7vbkp1vm40": "Please enter new password again", + "5n7vbkp1vus0": "Cancel", + "5n7vbkp1vy00": "Confirm", + "5n7vbkp1w7c0": "The two entered passwords do not match" + }, + "EditCode": { + "5m6nfosuqic0": "Reset Password", + "5m6nfosus3k0": "New Password", + "5m6nfosusbo0": "password can not be blank", + "5m6nfosusgc0": "Please enter a new password", + "5m6nfosuskg0": "Cancel", + "5m6nfosuspc0": "Confirm", + "5m6nfosustg0": "reset success", + "5nsczb3r79w0": "The length is between 6 and 20 numbers, letters, and special characters, at least two combinations" + }, + "EditUser": { + "5m6nh3mgjns0": "Revise", + "5m6nh3mgkn80": "Username", + "5m6nh3mgl2k0": "Username can not be empty", + "5m6nh3mglh40": "please enter username", + "5m6nh3mglmw0": "Password", + "5m6nh3mglt40": "password can not be empty", + "5m6nh3mglzg0": "Please enter password", + "5m6nh3mgm380": "Nickname", + "5m6nh3mgm6o0": "nickname can not be empty", + "5m6nh3mgm9s0": "Please enter nickname", + "5m6nh3mgmds0": "Phone", + "5m6nh3mgmko0": "Phone number can not be empty", + "5m6nh3mgmp40": "Please enter phone number", + "5m6nh3mgmss0": "User Role", + "5m6nh3mgmvs0": "Please select a user role", + "5m6nh3mgmz40": "Status", + "5m6nh3mgn1w0": "Enable", + "5m6nh3mgn4k0": "Disable", + "5m6nh3mgn9c0": "Remark", + "5m6nh3mgncw0": "Please enter a note", + "5m6nh3mgnhs0": "Cancel", + "5m6nh3mgnkg0": "Confirm", + "5m6nh3mgnn40": "Successfully modified", + "5m6nh3mgnpo0": "Added successfully", + "5n7v8xco9u80": "The length is between 6 and 20 numbers, letters, and special characters, at least two combinations", + "5nsg5pkyao80": "Mobile phone number format is incorrect", + "else1": "Add", + "else2": " User" + }, + "UpdateCode": { + "5n7vbkp1s580": "Change Password", + "5n7vbkp1t2s0": "Old Password", + "5n7vbkp1tb80": "Old password cannot be empty", + "5n7vbkp1tiw0": "Please enter old password", + "5n7vbkp1two0": "New Password", + "5n7vbkp1u340": "New password cannot be empty", + "5n7vbkp1u8o0": "The length is between 6 and 20 numbers, letters, and special characters, at least two combinations", + "5n7vbkp1uyw0": "Please enter a new password", + "5n7vbkp1v8o0": "Confirm Password", + "5n7vbkp1vg00": "Confirm password can not be blank", + "5n7vbkp1vm40": "Please enter new password again", + "5n7vbkp1vus0": "Cancel", + "5n7vbkp1vy00": "Confirm", + "5n7vbkp1w7c0": "The two entered passwords do not match" + }, + "PluginConfig": { + "5m6nj19s0700": "Plugin configuration", + "5m6nj19s1lw0": "Cancel", + "5m6nj19s1rw0": "Confirm", + "5m6nj19s21s0": "Configured successfully", + "else1": "{attrLabel} cannot be empty", + "else2": "Please enter {attrLabel}" + }, + "EditInfo": { + "5m6njon1w080": "Modify user information", + "5m6njon1www0": "account number", + "5m6njon1x200": "Account cannot be empty", + "5m6njon1x5c0": "please enter your account", + "5m6njon1x840": "User's Nickname", + "5m6njon1xbk0": "username can not be blank", + "5m6njon1xek0": "Please enter user nickname", + "5m6njon1xhg0": "cellphone number", + "5m6njon1xkg0": "Phone number can not be blank", + "5m6njon1zco0": "Please enter the phone number", + "5m6njon1zmw0": "Mail", + "5m6njon1zrg0": "please input your email", + "5m6njon1zus0": "Cancel", + "5m6njon1zxs0": "Confirm", + "5m6njon200w0": "Modified success", + "5nslhhf6a7k0": "Mobile phone number format is incorrect", + "5nslhhf6z9o0": "E-mail format is incorrect" + }, + "EditRole": { + "5m6nl1uvgfk0": "Revise", + "5m6nl1uvjes0": "Role Name", + "5m6nl1uvjls0": "Role name cannot be empty", + "5m6nl1uvjp80": "Please enter a role name", + "5m6nl1uvk7g0": "Status", + "5m6nl1uvkmc0": "Enable", + "5m6nl1uvkq80": "Disable", + "5m6nl1uvkw40": "Menu Permissions", + "5m6nl1uvl2o0": "Select All / Select None", + "5m6nl1uvl5g0": "Remark", + "5m6nl1uvl8k0": "Please enter a note", + "5m6nl1uvlbw0": "Cancel", + "5m6nl1uvles0": "Confirm", + "5m6nl1uvlk00": "Successfully modified", + "5m6nl1uvlp00": "Added successfully", + "5nsfs3sdwwg0": "The character name is Chinese, English, numbers and their combinations, without special characters", + "else1": "Add", + "else2": " Role" + }, + "EditCondition": { + "5m6nmc7dugg0": "Revise", + "5m6nmc7dvbk0": "Whitelist Name", + "5m6nmc7dvgw0": "Whitelist name cannot be empty", + "5m6nmc7dvkg0": "Please enter a whitelist name", + "5m6nmc7dvnk0": "IP Addresses", + "5m6nmc7dvrg0": "Separate multiple IPs with commas", + "5m6nmc7dvug0": "Cancel", + "5m6nmc7dwk00": "Confirm", + "5m6nmc7dwr00": "Successfully modified", + "5m6nmc7dwuo0": "Added successfully", + "5nse3d27z0k0": "IP format is incorrect", + "else1": "Add", + "else2": " Whitelist" + }, + "login-form": { + "5m6noeaox740": "Welcome to", + "5m6noeaoxxg0": "Username can not be empty", + "5m6noeaoy240": "username", + "5m6noeaoy5c0": "password can not be blank", + "5m6noeaoy9c0": "password", + "5m6noeap7a80": "Remember password", + "5m6noeap7n40": "Login", + "5m6noeap7y80": "login successful", + "5mpi8serosw0": "openGauss DataKit", + "5o61dvpc2o00": "System Warning", + "5o61dvpc5cs0": "Your login status has expired, please log in again", + "5o61dvpc5o80": "Re-Login", + "5o62d201k380": "Home" + }, + "EditConfig": { + "5mjace1rbj80": "Configuration", + "5mjace1rc240": "Log output level", + "5mjace1rc9c0": "Log keep days", + "5mjace1rcd40": "Please enter the number of storage days", + "5mjace1rch40": "Please enter the correct number of days", + "5mjace1rckw0": "Single log file size", + "5mjace1rco40": "Please enter single log file size", + "5mjace1rcr80": "Please enter the correct size, example(20kb,20mb,20gb)", + "5mjace1rcuk0": "Total storage limit", + "5mjace1rcxg0": "Please enter the maximum storage space", + "5mjace1rd0c0": "Cancel", + "5mjace1rd3o0": "Confirm", + "5mjace1rd6o0": "Successfully saved", + "5mjace1rd9o0": "Example: 20kb,20mb,20gb" + }, + "AddHost": { + "else1": "Please enter the correct port number", + "else2": "Remember", + "currentStatus": "Current status", + "ipAddress": "Intranet IP", + "name": "name", + "namePlaceholder": "Please enter the Host Name", + "username": "username", + "usernamePlaceholder": "Please enter username", + "tags": "label", + "tagsPlaceholder": "Please enter or select a label", + "addServerSuc": "Server created successfully", + "editServerSuc": "Server edited successfully", + "addAgentSuc": "Server created successfully, starting Agent installation", + "unFill": "There are unfilled items on the current page", + "5mphy3snvg80": "available", + "5mphy3snwq40": "Not available", + "5mphy3snwxs0": "cancel", + "5mphy3snx3o0": "test", + "5mphy3snx7c0": "confirm", + "5mphy3snxdo0": "Please enter the Intranet IP", + "5mphy3snxis0": "External IP", + "5mphy3snxmw0": "Please enter the External IP", + "5mphy3snxtc0": "Port", + "5mphy3snxzk0": "Please enter the port number", + "5mphy3sny4w0": "password", + "5mphy3snyao0": "Please enter your password.", + "5mphy3snyg40": "Available area", + "5mphy3snyn80": "Select an AZ", + "5mphy3snysg0": "remark", + "5mphy3snyxc0": "Please enter a remark", + "5mphy3snz5k0": "Adding a Dedicated Server", + "5mphy3snzb80": "The current server is unavailable", + "5mphy3snzrk0": "Edit Host", + "noPasswordTip": "User password was not saved. Please add the user again." + }, + "AddAgent": { + "agentTitle": "Agent Installation", + "agentName": "Agent Name", + "installPath": "Installation Path", + "namePlaceholder": "Please enter the Agent name", + "installPathPlaceholder": "Please enter the installation path", + "userPlaceholder": "Please enter the installation user", + "formaterror": "Invalid installation path format", + "pathDepthExceeded": "The installation path depth cannot exceed five levels", + "portPlaceholder": "Please enter the port number", + "stringLengthOver": "Path length must not exceed 255 characters", + "noChinesePath": "Please do not use non-ASCII characters (such as Chinese) in the path", + "numberRangeOver": "Port number is out of range", + "insAgentSuc": "Starting Agent installation" + }, + "HostUserChoose": { + "title": "Choose user", + "tip": "There are currently multiple users, and there is no root user. Please select the user you want to log in to the terminal.", + "userChoosePlaceholder": "Please choose a user" + }, + "AddHostUser": { + "else1": "Confirm", + "else2": "The system will save the password of the user. Do you want to save the password?", + "rootPassword": "RootPassword", + "5mphzt9pc0g0": "Host", + "5mphzt9pdak0": "Please enter the root password", + "5mphzt9pdn80": "Username", + "5mphzt9pdw00": "Please enter the user name", + "5mphzt9pe3s0": "Password", + "5mphzt9pec40": "Please enter your password.", + "5mphzt9peog0": "Add User", + "updateUser": "Edit User", + "5mphzt9pewg0": "The data that came through", + "createSuccess": "Create Success", + "modifySuccess": "Modify Success" + }, + "HostPwdDlg": { + "else1": "RootPassword", + "5mpi0giveek0": "generate", + "5mpi0giveys0": "Host", + "5mpi0givf8o0": "Please enter your password.", + "5mpi0givfcw0": "Inspection of safety", + "5mpi0givfhc0": "Please enter the root password", + "5mpi0givfl40": "Request exception occurs" + }, + "HostUserMng": { + "5mpi1bru0w00": "Add User", + "5mpi1bru1n40": "edit", + "5mpi1bru2700": "Make sure to delete?", + "5mpi1bru2lo0": "confirm", + "5mpi1bru2s00": "cancel", + "oneUserTip": "At least one user should be kept.", + "5mpi1bru2yo0": "delete", + "5mpi1bru3dk0": "User Management", + "5mpi1bru3vk0": "User name", + "isSudo": "Whether you have the root permission", + "yes": "Y", + "no": "N", + "5mpi1bru4c00": "operation" + }, + "BatchLabelDlg": { + "5pbjt280spg0": "Selected host", + "5pbjt280xuo0": "Count", + "5pbjt280yik0": "Tag", + "5pbjt280z040": "Tag Batch Setting", + "5quz8t9a2xg0": "Operation Type", + "5quz8t9a5x00": "Batch Add", + "5quz8t9a6ik0": "Batch Del", + "5quz8t9a6zk0": "Common labels", + "5quz8t9a7fs0": "remove labels", + "5quz8t9a7w00": "No labels are shared and cannot be deleted in batches" + }, + "AddAz": { + "azName": "AZ Name", + "5mpib0ipo0g0": "cancel", + "5mpib0ipoo40": "confirm", + "5mpib0ipov80": "Please enter the AZ name", + "5mpib0ipozw0": "Location", + "5mpib0ipp3k0": "Please enter the actual geographic location", + "5mpib0ipp7w0": "Priority level", + "5mpib0ippbo0": "Please enter the priority", + "5mpib0ippfc0": "describe", + "5mpib0ippjg0": "Please enter a description.", + "5mpib0ippoc0": "Adding an Available Zone", + "5mpib0ipprs0": "Edit Availability zone", + "else1": "Name already exists", + "else2": "Check whether the name is incorrect" + }, + "Package": { + "5mtcyb0rty02": "Add package", + "5mtcyb0rty03": "Batch check", + "5mtcyb0rty04": "Batch delete", + "5mtcyb0rty05": "Please enter or select table", + "5mtcyb0rty06": "Package name", + "5mtcyb0rty07": "Operating system", + "5mtcyb0rty08": "CPU Arch", + "5mtcyb0rty09": "openGauss version", + "5mtcyb0rty10": "Version number", + "5mtcyb0rty11": "Installation source", + "5mtcyb0rty12": "Download link/Offline package", + "5mtcyb0rty13": "Operations", + "5mtcyb0rty14": "Check", + "5mtcyb0rty15": "Update", + "5mtcyb0rty16": "Are you sure to delete?", + "5mtcyb0rty17": "Confirm", + "5mtcyb0rty18": "Cancel", + "5mtcyb0rty19": "Delete", + "5mtcyb0rty20": "No package is found in this condition", + "5mtcyb0rty21": "You can upload the package offline or download it online", + "5mtcyb0rty22": "Offline upload", + "5mtcyb0rty23": "Online Download", + "5mtcyb0rty24": "Download Complete", + "5mtcyb0rty25": "The same parent tag can only select one child tag. There are multiple items under the {tagName} tag, and only the first one will be retained.", + "5mtcyb0rty26": "Check complete", + "5mtcyb0rty27": "Check failed", + "5mtcyb0rty28": "Please select at least one package", + "5mtcyb0rty29": "Delete complete", + "5mtcyb0rty30": "All selected packages have been deleted", + "5mtcyb0rty31": "Delete failed", + "ENTERPRISE": "Enterprise", + "MINIMAL_LIST": "Simplified", + "LITE": "Lite", + "5mtcyb0rty32": "System arch", + "5mtcyb0rty33": "Version type", + "5mtcyb0rty34": "Please enter version number", + "5mtcyb0rty35": "The current version is the new version number. You can ", + "5mtcyb0rty36": "go to the official website", + "5mtcyb0rty37": "to check whether the version number exists. If not, the download will fail.", + "5mtcyb0rty38": "Online download address", + "5mtcyb0rty39": "Match the package based on the preceding configuration information. download and install the installation package. If not matched to the appropriate package or not the target package, you can", + "5mtcyb0rty40": "Match the package based on the preceding configuration information. The information is for display only and cannot be downloaded due to no network connection.Please upload the offline installation package.", + "5mtcyb0rty41": "Offline installation package", + "5mtcyb0rty42": "You can drag the file here or select the file", + "5mtcyb0rty43": "Select file", + "5mtcyb0rty44": "Version number {versionNum} does not exist, please select again", + "5mtcyb0rty45": "Please select version number", + "5mtcyb0rty46": "Please enter package name", + "5mtcyb0rty47": "Please do not enter duplicate package name", + "5mtcyb0rty48": "Select an installation package to upload", + "5mtcyb0rty49": "Failed to get the version number", + "5mtcyb0rty50": "Add package", + "5mtcyb0rty51": "Update package", + "5mtcyb0rty52": "File download failed. Kindly verify the accuracy of the download link or ensure that network connection is functioning properly.", + "5mtcyb0rty53": "Match the following installation package based on the configuration and download it", + "5mtcyb0rty54": "Drag and drop the file here or", + "5mtcyb0rty55": "Select the file", + "5mtcyb0rty56": "Please select the .jar file", + "5mtcyb0rty57": "No response for a long time, the download progress floating box will be closed", + "5mtcyb0rty58": "Please select the file of the specified type", + "5mtcyb0rty59": "The file size exceeds the limit", + "5mtcyb0rty60": "Please select an operating system", + "5mtcyb0rty61": "Select a CPU architecture", + "5mtcyb0rty62": "Please select the openGauss version", + "5mtcyb0rty63": "Please enter a package name" + }, + "SystemAlarm": { + "cpuWarning": "CPU Alarm", + "5mpihybegg40": "System Alarm", + "5mpihybehhk0": "level", + "5mpihybeho00": "Alarm Content", + "5mpihybehvw0": "time", + "5mpihybei2o0": "Memory alarm", + "5mpihybei740": "Storage alarm", + "5mpihybeiaw0": "Business flow error" + }, + "OneCheck": { + "clusterName": "Cluster Name", + "else1": "CheckHashIndex", + "else2": "CheckNodeGroupName", + "else3": "CheckTDDate", + "else4": "Detection completion time", + "else5": "Check Item", + "else6": "rootPassword", + "else7": "Please enter the root password", + "5mpiji1q2ng0": "One Check", + "5mpiji1q3wo0": "Select a cluster name", + "5mpiji1q4a00": "retest", + "5mpiji1q4k40": "detection", + "5mpiji1q4tg0": "One Check in progress", + "5mpiji1q57w0": "Cluster", + "5mpiji1q5r40": "CheckClusterState", + "5mpiji1q5zw0": "CheckDBParams", + "5mpiji1q6b00": "CheckDebugSwitch", + "5mpiji1q8q80": "CheckDirPermissions", + "5mpiji1q96c0": "CheckReadonlyMode", + "5mpiji1q9r00": "CheckEnvProfile", + "5mpiji1qa080": "CheckDilateSysTab", + "5mpiji1qa940": "CheckProStartTime", + "5mpiji1qahc0": "CheckMpprcFile", + "5mpiji1qas40": "Database", + "5mpiji1qbv80": "CheckCurConnCount", + "5mpiji1qc780": "CheckCursorNum", + "5mpiji1qcjs0": "CheckPgxcgroup", + "5mpiji1qczo0": "CheckSysadminUser", + "5mpiji1qdeo0": "CheckTableSpace", + "5mpiji1qdlk0": "CheckPgxcRedistb", + "5mpiji1qds80": "System", + "5mpiji1qdy40": "CheckMemInfo", + "5mpiji1qe8s0": "CheckCPU", + "5mpiji1qehc0": "CheckOSVer", + "5mpiji1qeoo0": "CheckKeyProAdj", + "5mpiji1qf280": "CheckMaxProcMemory", + "5mpiji1qf9k0": "CheckEncoding", + "5mpiji1qfgo0": "CheckFirewall", + "5mpiji1qfnc0": "CheckKernelVer", + "5mpiji1qfx00": "CheckMaxHandle", + "5mpiji1qg400": "CheckNTPD", + "5mpiji1qga80": "CheckSysParams", + "5mpiji1qgl40": "CheckTHP", + "5mpiji1qgt80": "CheckTimeZone", + "5mpiji1qh2k0": "CheckSshdService", + "5mpiji1qh8s0": "CheckSshdConfig", + "5mpiji1qhfs0": "CheckCrondService", + "5mpiji1qhn00": "CheckStack", + "5mpiji1qhto0": "CheckSysPortRange", + "5mpiji1qi2s0": "CheckHyperThread", + "5mpiji1qif40": "CheckBootItems", + "5mpiji1qioc0": "CheckFilehandle", + "5mpiji1qizk0": "CheckDropCache", + "5mpiji1qjag0": "Host", + "5mpiji1qjh80": "CheckBlockdev", + "5mpiji1qkn00": "CheckDiskFormat", + "5mpiji1qkyk0": "CheckSwapMemory", + "5mpiji1ql580": "CheckLogicalBlock", + "5mpiji1qldc0": "CheckIOrequestqueue", + "5mpiji1qlmc0": "CheckMaxAsyIOrequests", + "5mpiji1qltc0": "CheckSpaceUsage", + "5mpiji1qm0s0": "CheckInodeUsage", + "5mpiji1qmbw0": "CheckIOConfigure", + "5mpiji1qmj40": "Network", + "5mpiji1qmqg0": "CheckPing", + "5mpiji1qn1c0": "CheckNetWorkDrop", + "5mpiji1qn8g0": "CheckNICModel", + "5mpiji1qnf00": "CheckMTU", + "5mpiji1qnks0": "CheckRXTX", + "5mpiji1qnr00": "CheckMultiQueue", + "5mpiji1qnxs0": "CheckRouting", + "5mpiji1qo440": "Item", + "5mpiji1qoe00": "NoPass", + "5mpiji1qom40": "Error", + "5mpiji1qosc0": "Warning", + "5mpiji1qoyw0": "Pass", + "5mpiji1qpcc0": "Check category", + "5mpiji1qpns0": "Check Items", + "5mpiji1qpvg0": "Check Result", + "5mpiji1qq180": "View Return" + }, + "ModuleSummary": { + "else1": "count", + "else2": "The baseops plugin is not installed, unable to view related data.", + "5mpim9s7gw80": "Flow of business", + "5mpim9s7hqk0": "Stream of data", + "5mpim9s7hxk0": "Data model", + "5mpim9s7i7o0": "Plug-ins have been installed." + }, + "CommonOper": { + "else1": "AZ Management", + "5mpimo1efn00": "Common Operations", + "5mpimo1egl80": "Dedicated Server Management", + "5mpimo1egv80": "Data flow design", + "5mpimo1eh8o0": "Log center", + "5mpimo1ehfw0": "Cluster Monitoring" + }, + "ClusterTop": { + "5mpinksdbe40": "Cluster Overview", + "5mpinksdby80": "One-click self-test", + "5mpinksdc4g0": "Cluster Operation and Maintenance", + "5mpinksdc8g0": "count", + "5mpinksdcc40": "Cluster", + "5mpinksdcgk0": "host", + "5mpinksdckw0": "instance" + }, + "ClusterList": { + "else1": "instance", + "else2": "session", + "else3": "connections", + "else4": "lock", + "else5": "area", + "else6": "count", + "else7": "Intranet IP", + "else8": "External IP", + "else9": "Host Name", + "else10": "Host Config", + "else11": "nuclear", + "else12": "Geographical location", + "else13": "CPU Usage", + "else14": "Successful connection", + "else15": "Connection failed, retry connection", + "5mpinxl8f0w0": "Trying to get cluster information", + "5mpinxl8frs0": "Connection timeout", + "5mpinxl8g2c0": "Memory Usage", + "5mpinxl8gaw0": "Network Status", + "5mpinxl8gi40": "Number of connections", + "5mpinxl8gpc0": "No cluster information is available", + "5mpinxl8gwg0": "Creating a Cluster", + "5mpinxl8h380": "Obtaining Cluster Information", + "5mpinxl8hbk0": "Minimalist version", + "5mpinxl8his0": "Light weight version", + "5mpinxl8hqc0": "Enterprise Edition", + "5mpinxl8i0g0": "Master", + "5mpinxl8i7k0": "Slave", + "5mpinxl8if40": "Cascade", + "5mpinxl8img0": "Available", + "5mpinxl8isw0": "Not available", + "5mpinxl8j000": "Not detected", + "nodeState1": "available", + "nodeState2": "Need repair", + "nodeState3": "Starting", + "nodeState4": "Waiting to upgrade", + "nodeState5": "upgrading", + "nodeState6": "In demotion", + "nodeState7": "Need to rebuild", + "nodeState8": "Catch-up master", + "nodeState9": "collapse", + "nodeState10": "unknown" + }, + "BusiFlowSummary": { + "5mpivn7fwjc0": "Data flow distribution", + "5mpivn7fx3k0": "Data source Type distribution", + "5mpivn7fxfc0": "Data flow example", + "5mpivn7fxjk0": "cluster", + "5mpivn7fxnc0": "type", + "5mpivn7fxrk0": "Number of queries" + }, + "EditSysSettings": { + "5mpivn7fwjc1": "Edit System Setting", + "5mpivn7fwjc2": "File Upload Path", + "5mpivn7fwjc6": "Please input file upload path", + "5mpivn7fwjc9": "File path is not correct, must end with /", + "5mpivn7fwj10": "The directory is already occupied by another user. Please enter a different path", + "5mpivn7fwj11": "MigrationKit download url", + "5mpivn7fwj12": "please input migration kit download url", + "5mpivn7fwj13": "MigrationKit Package Name", + "5mpivn7fwj14": "please input migration kit package name", + "5mpivn7fwj15": "MigrationKit Jar Name", + "5mpivn7fwj16": "please input migration kit jar name", + "5mpivn7fwj17": "datakit server host", + "5mpivn7fwj18": "please input datakit server host" + } + }, + "user": { + "index": { + "5m6ni29gbi40": "User/Nickname", + "5m6ni29gdag0": "Please enter", + "5m6ni29gdhw0": "Phone", + "5m6ni29gdog0": "Please enter", + "5m6ni29gdrk0": "Status", + "5m6ni29gdus0": "Please select", + "5m6ni29gdxs0": "Enabled", + "5m6ni29ge1s0": "Disabled", + "5m6ni29gglk0": "Search", + "5m6ni29ggtw0": "Reset", + "5m6ni29ggx40": "Add User", + "5m6ni29ghe80": "Username", + "5m6ni29ghlc0": "Nickname", + "5m6ni29ghrk0": "Creation Time", + "5m6ni29ghug0": "Operation", + "5m6ni29h4i80": "Edit", + "5m6ni29h4yc0": "Are you sure you want to delete this user ?", + "5m6ni29h7pc0": "Delete", + "5m6ni29h80k0": "Reset", + "5m6ni29h84w0": "delete success", + "5msnsk9mm9k0": "User Role", + "5obuyt8kr2o0": "Administrator" + } + }, + "ucenter": { + "index": { + "5m6nkbklgtk0": "Change avatar", + "5m6nkbklieo0": "upload success" + } + }, + "role": { + "index": { + "5m6nlovf8ik0": "Role Name", + "5m6nlovfao80": "Please enter role name", + "5m6nlovfawc0": "Status", + "5m6nlovfb040": "Please select", + "5m6nlovfb3c0": "Enabled", + "5m6nlovfb700": "Disabled", + "5m6nlovfba00": "Search", + "5m6nlovfbdc0": "Reset", + "5m6nlovfbgw0": "Add Role", + "5m6nlovfbjw0": "Disable", + "5m6nlovfbmw0": "Creation Time", + "5m6nlovfbqc0": "Operation", + "5m6nlovfbtc0": "Edit", + "5m6nlovfbwg0": "Are you sure you want to delete this role ?", + "5m6nlovfc000": "Delete", + "5m6nlovfc380": "delete success" + } + }, + "whitelist": { + "index": { + "5m6nn2djbsk0": "Whitelist Name", + "5m6nn2djccs0": "Please enter a whitelist name", + "5m6nn2djchs0": "Type", + "5m6nn2djclc0": "Please select", + "5m6nn2djcpg0": "Type 1", + "5m6nn2djd580": "Type 2", + "5m6nn2djda00": "Search", + "5m6nn2djddo0": "Reset", + "5m6nn2djdgw0": "Add Whitelist", + "5m6nn2djdjw0": "IP Addresses", + "5m6nn2djdmo0": "Creation Time", + "5m6nn2djdpc0": "Operation", + "5m6nn2djds00": "Edit", + "5m6nn2djdv00": "Are you sure you want to delete this whitelist ?", + "5m6nn2djdxo0": "Delete", + "5m6nn2dje0g0": "delete success" + } + }, + "navbar": { + "index": { + "5m6nsk34ptw0": "openGauss DataKit", + "5m6nsk34qkk0": "简体中文", + "5m6nsk34qpg0": "Shortcut", + "5m6nsk34qss0": "Operation and maintenance tool console", + "5m6nsk34qvk0": "One-click self-check", + "5m6nsk34qyk0": "Physical machine management", + "5m6nsk34r1g0": "session management", + "5m6nsk34r4g0": "parameter settings", + "5m6nsk34r7c0": "Cluster operation and maintenance", + "5m6nsk34ra40": "User Center", + "5m6nsk34rd00": "Reset Password", + "5m6nsk34rd01": "System Setting", + "5m6nsk34rg40": "Logout", + "5mpib9iig6o0": "openGauss DataKit", + "5nu3bvtnu5o0": "Click to switch to dark mode", + "5nu3bvtnuyk0": "Click to switch to bright color mode", + "5nu4bvtnuyk0": "OGPilot Gauss Xiaozhi is in the trial stage and can send company/school and public IP addresses to ", + "5nu5bvtnuyk0": " Apply for", + "5nu6bvtnuyk0": "Go apply", + "5nu7bvtnuyk0": "Applied, access oGPilot" + } + }, + "tab-bar": { + "tab-item": { + "5m6ntgxo4tg0": "close current tab", + "5m6ntgxo9vc0": "Close left tab", + "5m6ntgxoa240": "Close right tab", + "5m6ntgxoa540": "close other tabs", + "5m6ntgxoadc0": "close all tabs", + "subTaskMigraTab": "The previous migration subtask details tab is closed, and only the current migration subtask details tab page is displayed" + } + }, + "sys-log": { + "index": { + "5mja76hbg2g0": "File name", + "5mja76hbh680": "Please enter the file name", + "5mja76hbhes0": "Search", + "5mja76hbhl40": "Reset", + "5mja76hbhqc0": "Configuration", + "5mja76hbhvo0": "File size", + "5mja76hbi1c0": "Creation time", + "5mja76hbi6c0": "Operation", + "5mja76hbib80": "Download", + "5mja76hbim00": "Downloading", + "5mja76hbiy80": "Download failed, please try again" + } + }, + "oper-log": { + "index": { + "5mjaax4r6ko0": "System module", + "5mjaax4r7fs0": "Please enter module name", + "5mjaax4r7mc0": "Username", + "5mjaax4r7qc0": "Please enter operator name", + "5mjaax4r7to0": "Operation type", + "5mjaax4r7x80": "HTTP status", + "5mjaax4r80g0": "Success", + "5mjaax4r83o0": "Fail", + "5mjaax4r89o0": "Search", + "5mjaax4r8d80": "Reset", + "5mjaax4r8jo0": "Request method", + "5mjaax4r8mo0": "Date", + "5mjaax4r8pw0": "Operation", + "5mjaax4r8sw0": "Details", + "5mjaax4r8vw0": "System module:", + "5mjaax4r8z40": "Login information:", + "5mjaax4r92g0": "Request address:", + "5mjaax4r9co0": "Request method:", + "5mjaax4r9hk0": "Method:", + "5mjaax4r9m00": "Request parameter:", + "5mjaax4r9ow0": "Response parameter:", + "5mjaax4r9sw0": "HTTP status:", + "5mjaax4r9vo0": "Date:", + "5mjaax4r9y00": "Error:", + "5mjaax4ra0g0": "Other", + "5mjaax4ra3w0": "Insert", + "5mjaax4ra6c0": "Update", + "5mjaax4ra8w0": "Delete", + "5mjaax4rab80": "Grant", + "5mjaax4rads0": "Start", + "5mjaax4rag80": "Stop", + "5mjaax4raio0": "Install", + "5mjaax4ral00": "Uninstall" + } + }, + "login": { + "index": { + "5mpi5tn6irc0": "openGauss DataKit" + } + }, + "physical": { + "index": { + "createServer": "Create a server", + "ipAddress": "IP address", + "ipAddressPlaceholder": "Enter the IP address for query", + "batchDel": "delete", + "hostLabel": "label", + "hostLabelPlaceholder": "Please select the label for query", + "os": "system", + "osPlaceholder": "Select an operating system", + "serverName": "Server name", + "privateIp": "Intranet IP", + "publicIp": "External IP", + "privateAndPublicIP": "Internal/public IP addresses", + "cpuArch": "arch", + "hostName": "HostName", + "net": "network", + "realNetRate": "Real-time network rate", + "upLink": "UpLink", + "downlink": "Downlink", + "cpu": "CPU usage", + "memory": "Memory usage", + "disk": "Disk usage", + "agentStatus": "Agent status", + "running": "Running", + "offline": "Offline", + "uninstalled": "Not installed", + "agentManager": "Agent manage", + "agentName": "Agent name", + "operationStatus": "Operational status", + "serverIp": "Server IP", + "port": "Port", + "installPath": "Install path", + "userName": "Username", + "osMetrics": "OS metrics", + "baseInfo": "BaseInfo", + "diskInfo": "DiskInfo", + "realTime": "RealTime", + "noUserTip": "No users yet. Please add users first.", + "tipInfo": "Prerequisites: Remember the password when saving the host to obtain data in real time. Otherwise it cannot be obtained", + "detail": "Detail", + "labelBatch": "Tag Setting", + "labelManage": "Tag Manage", + "else1": "Please select at least one host", + "noData": "No data", + "operateColumn": "Operate", + "agentInstall": "Agent Install", + "installAgent": "Install Agent", + "userManage": "User Manage", + "more": "More", + "reback": "Reback", + "stopSuccess": "Stop Success", + "startSuccess": "Start Success", + "uninstallSuccess": "Uninstall Success", + "serverConfig": "Server Config", + "5mphf11rr080": "Add", + "5mphf11syw80": "test", + "5mphf11szdk0": "search", + "5mphf11szks0": "User Management", + "5mphf11szqo0": "edit", + "5mphf11szws0": "Make sure to delete?", + "5mphf11t05c0": "confirm", + "5mphf11t0bc0": "cancel", + "5mphf11t0hc0": "delete", + "5mphf11t0yc0": "External IP address", + "5mphf11te5c0": "Owning Availability zone", + "5mphf11teq40": "state", + "5mphf11tf5g0": "remark", + "5mphf11tfjw0": "operation", + "5mphf11tfr80": "Not detected", + "5mphf11tfx80": "Not available", + "5mphf11tg780": "available", + "5mphf11rr090": "Bulk Import", + "5mphf11rr590": "Bulk Delete", + "5mphf11rr890": "All selected hosts have been deleted.", + "5mphf11rr990": "Some hosts were not successfully deleted.", + "stop": "Stop", + "start": "Start", + "close": "Close", + "uninstall": "Uninstall", + "addNewUser": "Add a new user", + "unInstalledAgent": "There are agents that have not been uninstalled", + "selectUserTable": "User Manager" + } + }, + "label": { + "LabelManageDlg": { + "5pbjv7b0vrw0": "Createe", + "5pbjv7b0wu80": "Tag Name", + "5pbjv7b0x3w0": "Please enter a label name", + "5pbjv7b0xak0": "Edit", + "5pbjv7b0xgo0": "Number of associated hosts", + "5pbjv7b0y0w0": "Operation", + "5pbjv7b0yd00": "Create Label", + "5pbjv7b0yk40": "Edit Label", + "5pbjv7b0yso0": "Label Manage", + "addLabel": "Add tags", + "tagNamePlaceholder": "Please enter the tag name to search", + "selectUserTable": "Select the Users table", + "delWarning":"Please select the deleted user first" + } + }, + "bulk": { + "BulkImport": { + "5exv06n8x1kh0": "Bulk Import", + "5exv06n8x1ki0": "Choose File", + "5exv06n8x1kj0": "Drag files here or", + "5exv06n8x1kk0": "File Parsing", + "5exv06n8x1kl0": "Import to Server", + "5exv06n8x1km0": "Import Completed", + "5exv06n8x1kn0": "File is being parsed, please wait a moment.", + "5exv06n8x1ko0": "Re-upload", + "5exv06n8x1kp0": "You can still replace the file by dragging and dropping it into the area below.", + "5exv06n8x1kq0": "File uploaded successfully.", + "5exv06n8x1kr0": "Re-import", + "5exv06n8x1ks0": "Completed", + "5exv06n8x1kt0": "Download Error Report", + "5exv06n8x1ku0": "Import Template Download", + "5exv06n8x1kv0": "File is uploading", + "5exv06n8x1kx0": "File Parsing Failed", + "5exv06n8x1ky0": "Only Excel files are supported for upload, with a maximum size of 200MB.", + "5exv06n8x1kz0": "Delete", + "5exv06n8x1ka0": "Okay", + "5exv06n8x1kb0": "Cancel", + "5exv06n8x1kw0": "File upload failed, an Excel file is required. Please upload again.", + "5exv06n8x1kc0": "File upload failed, the file must be under 200MB. Please upload again.", + "5exv06n8x1kd0": "File upload failed, only one file is allowed. Please upload again.", + "5exv06n8x1ke0": ",please modify the file and", + "5exv06n8x1kf0": "File upload failed due to an unknown reason, please upload again.", + "5exv06n8x1kh1": "Please do not refresh the interface.", + "5exv06n8x1ki1": "Total imported", + "5exv06n8x1kj1": "entries of data", + "5exv06n8x1kk1": "entries", + "5exv06n8x1kl1": "Import successful,", + "5exv06n8x1km1": "Import failed", + "5exv06n8x1kn1": "Download Error Report", + "step1Content": "1. File parsing", + "step1Error": "1. File parsing (Failed)", + "step2Content": "2. Importing to server", + "step3Content": "3. Import completed" + } + }, + "az": { + "index": { + "azName": "AZ Name", + "5mpi9hkpfhk0": "Adding an Available Zone", + "5mpi9hkpgdw0": "search...", + "5mpi9hkpgns0": "edit", + "5mpi9hkpgv80": "Make sure to delete?", + "5mpi9hkph9k0": "confirm", + "5mpi9hkphi40": "cancel", + "5mpi9hkphpc0": "delete", + "5mpi9hkphvo0": "Physical geographical location", + "5mpi9hkpi2o0": "Priority level", + "5mpi9hkpi940": "remark", + "5mpi9hkpif40": "operation" + } + }, + "database": { + "AddJdbc": { + "else1": "To be detected", + "5oxhkhiks5k0": "Available", + "5oxhkhimwfg0": "Unavailable", + "5oxhkhimwy00": "Cancel", + "5oxhkhimx5c0": "Test connectivity", + "5oxhkhimxbg0": "Confirm", + "5oxhkhimxho0": "Cluster Name", + "isCustomName": "isCustom", + "customNamePlaceholder": "The cluster name is generated based on the instance information", + "5oxhkhimxno0": "Please enter an instance name", + "5oxhkhimxto0": "Database type", + "5oxhkhimxzw0": "Select a database type", + "5oxhkhimyio0": "Instance", + "5oxhkhimz480": "Please enter the cluster name", + "5oxhkhimzcg0": "The value cannot be pure space", + "5oxhkhimzmw0": "Modify data source", + "5oxhkhimzww0": "Add data source", + "checkPassword": "Please re-enter the password when editing." + }, + "HostImportDlg": { + "5oxhmjuzltc0": "Parsing result: The following error data exists. Please modify it and upload it again", + "5oxhmjuzmxw0": "Cluster Name", + "5oxhmjuzn9s0": "Connection address", + "5oxhmjuznig0": "Analytic result", + "5oxhmjuzz7w0": "Import parsing" + }, + "HostPwdDlg": { + "5oxhni610s00": "IP", + "5oxhni611qo0": "Port", + "5oxhni6126o0": "Enter the SSH port number", + "5oxhni612g00": "Username", + "5oxhni612mo0": "Please enter the user name", + "5oxhni612vk0": "Password", + "5oxhni6132k0": "rootPassword", + "5oxhni6138s0": "Please enter password", + "5oxhni613ik0": "Please enter the correct port number", + "5oxhni613ow0": "The value cannot be pure space", + "5oxhni613us0": "SSH connection information" + }, + "HostTerminal": { + "else1": "IP", + "else2": "If the font is blurred under special screen resolution, please scale the page", + "5oxhoh5r6ls0": "Terminal" + }, + "index": { + "else1": "ClusterName", + "else2": "Confirm upload", + "elseIp": "IP Address", + "elseIpPlaceholder": "Enter the IP address for query", + "else3": "Type", + "else3Placeholder": "Select a type for query", + "5oxhr0qz15w0": "Create", + "5oxhr0qz2bs0": "Download template", + "5oxhr0qz2s00": "Please enter the cluster name for query", + "5oxhr0qz30g0": "Query", + "5oxhr0qz37o0": "Update", + "5oxhr0qz3f40": "Are you sure you want to delete it?", + "5oxhr0qz3m80": "Confirm", + "5oxhr0qz3t80": "Cancel", + "5oxhr0qz40k0": "Delete", + "5oxhr0qz48w0": "clusterName", + "5oxhr0qz4fs0": "Type", + "5oxhr0qz4no0": "State", + "5oxhr0qz4zk0": "Update Time", + "5oxhr0qz58o0": "Operation", + "5oxhr0qz5g40": "Upload Confirm" + }, + "JdbcInstance": { + "5oxhtcbo8ac0": "IP", + "5oxhtcbo9fg0": "Please select or enter an IP address", + "5oxhtcbo9ps0": "Port", + "5oxhtcbo9xc0": "Please enter the port number", + "5oxhtcboa240": "Username", + "5oxhtcboa7c0": "Please enter the user name", + "5oxhtcboac00": "Password", + "5oxhtcboags0": "Please enter password", + "5oxhtcboap00": "extension", + "5oxhtcboatc0": "Add", + "5oxhtcboaxc0": "Delete", + "5oxhtcbob1s0": "Url", + "5oxhtcbob640": "Extension", + "5oxhtcbobac0": "Extended value", + "5oxhtcbobe80": "Operation", + "5oxhtcbobhs0": "Please select or enter an IP address", + "5oxhtcboblw0": "Enter the correct IP address", + "5oxhtcbobpk0": "Please enter the correct port number", + "5oxhtcbobtc0": "The value cannot be pure space", + "5oxhtcboc2c0": "Please enter the user password" + }, + "JdbcNodeTable": { + "else1": "IP", + "else2": "Port", + "else3": "Node", + "else4": "unknown", + "else5": "Oper System", + "else6": "RealTime", + "else7": "Session", + "else8": "Lock", + "5oxhv6qcm6w0": "To detect", + "5oxhv6qcnak0": "Master", + "5oxhv6qcnnk0": "Slave", + "5oxhv6qcnuk0": "Connection number", + "5oxhv6qco4c0": "TableSpace occupy", + "5oxhv6qcobk0": "Memory occupy" + } + }, + "echarts": { + "BusiFlowSummary": { + "5mpiw64lv300": "Business flow data" + }, + "DataSourceSummary": { + "5mpiwzpxnsg0": "Data Source Type Distribution" + } + }, + "protocol": { + "index": { + "5mpixm5ckcw0": "Under development, please wait" + } + }, + "footer": { + "index": { + "5msicm67hz00": "all rights reserved" + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/locale/locale/zh-cn.json b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/locale/zh-cn.json new file mode 100644 index 000000000..960362aac --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/locale/zh-cn.json @@ -0,0 +1,1089 @@ +{ + "errorCode": { + "400": "请求参数错误", + "401": "当前用户未授权", + "403": "当前用户没有操作权限", + "50101": "插件停止错误", + "50102": "插件启动错误", + "50103": "插件卸载错误", + "50104": "插件安装错误", + "50105": "角色名称已经存在", + "50107": "用户手机号码已经存在", + "50108": "用户邮箱号已经存在", + "50109": "旧密码输入错误", + "50110": "新密码不能与旧密码一致", + "50115": "用户账号已经存在", + "50116": "该插件菜单包含其他插件的子菜单", + "50126": "白名单名称已经存在", + "default": "系统未知错误,请反馈给管理员" + }, + "manage": { + "index": { + "5m5v55wx37o0": "请输入ID名/描述/开发者", + "5m5v55wxj5s0": "状态", + "5m5v55wxjf40": "请选择停用/启用", + "5m5v55wxjjs0": "停用", + "5m5v55wxjo00": "启用", + "5m5v55wxjsg0": "查询", + "5m5v55wxjwg0": "重置", + "5m5v55wxk140": "安装插件", + "5m5v55wxk580": "详情", + "5m5v55wxkrw0": "卸载", + "5m5v55wxkxw0": "将文件拖到此处,或", + "5m5v55wxl440": "点击上传", + "5m5v55wxla00": "仅允许导入jar格式文件", + "5m5v55wxldg0": "取消", + "5m5v55wxlgg0": "确定", + "5m5v55wxljs0": "启动成功", + "5m5v55wxlmg0": "停止成功", + "5m5v55wxlso0": "卸载成功", + "5mdcikaqmi80": "安装成功", + "5n6f62aai3k0": "启用", + "5n6f62aajx80": "停用", + "else1": "类别:", + "else2": "暂无", + "else3": "是否确认卸载ID为{id}的插件?", + "OPEN_SOURCE": "开源版", + "FREE": "免费版", + "TRIAL": "试用版", + "PAID": "正式版", + "update_authorization": "更新授权", + "authorization_detail": "授权详情", + "plugin_name": "插件名称", + "authorization": "授权类型", + "plugin_description": "插件介绍", + "activation_time": "激活时间", + "expires": "有效期", + "company": "开发公司", + "phone": "联系电话", + "email": "邮箱地址", + "address": "联系地址", + "manual": "用户手册", + "demo": "演示地址", + "websocketErrorMsg": "WebSocket长时间无响应,将关闭下载进度悬浮框", + "onlineDownload": "在线下载", + "notificationText": "根据配置匹配到如下安装包并进行下载:" + }, + "PluginInstall": { + "observabilitySqlDiagnosis": "智能诊断插件", + "observabilityInstance": "实例监控插件", + "monitorTools": "数据库监控插件生成工具", + "oauthLogin": "统一登录插件", + "alertMonitor": "告警监控插件", + "MetaTune": "数据库智能参数调优工具", + "containerManagement": "容器管理插件", + "compatibilityAssessment": "兼容性评估工具", + "observabilityLogSearch": "日志检索插件", + "dataMigration": "数据迁移插件", + "baseOps": "基础运维插件", + "webdsPlugin": "业务开发", + "dragMsg": "将文件拖到此处,或", + "clickToUpload": "点击上传", + "uploadLimit": "仅允许导入jar格式文件", + "pluginName": "插件名称", + "onLineURL": "在线下载地址", + "onlineMsg": "根据上述配置信息匹配插件,显示该插件的在线下载地址进行下载安装.如未匹配到插件,您可进行", + "offlineUpload": "离线上传", + "uploadErrAlert": "安装失败,请上传其他安装包", + "userMsgErr": "页面信息未完全填写,或者用户信息获取失败", + "fileTypeRestriction": "请选择指定类型的文件", + "fileSizeLimit": "文件大小超过限制" + } + }, + "components": { + "FusionSearch": { + "addFilters": "请添加筛选条件", + "confirm": "确认", + "cancel": "取消", + "inputMaxWarnText": "已达到最大长度" + }, + "UpdateDefaultCode": { + "5nus586u4jg0": "重置默认密码", + "5n7vbkp1s580": "修改密码", + "5n7vbkp1t2s0": "旧密码", + "5n7vbkp1tb80": "旧密码不能为空", + "5n7vbkp1tiw0": "请输入旧密码", + "5n7vbkp1two0": "新密码", + "5n7vbkp1u340": "新密码不能为空", + "5n7vbkp1u8o0": "长度在 6 到 20 个数字、字母、特殊字符最少两种组合", + "5n7vbkp1uyw0": "请输入新密码", + "5n7vbkp1v8o0": "确认密码", + "5n7vbkp1vg00": "确认密码不能为空", + "5n7vbkp1vm40": "请再次输入新密码", + "5n7vbkp1vus0": "取消", + "5n7vbkp1vy00": "确定", + "5n7vbkp1w7c0": "两次输入的密码不一致" + }, + "EditCode": { + "5m6nfosuqic0": "重置密码", + "5m6nfosus3k0": "新密码", + "5m6nfosusbo0": "密码不能为空", + "5m6nfosusgc0": "请输入新密码", + "5m6nfosuskg0": "取消", + "5m6nfosuspc0": "确定", + "5m6nfosustg0": "重置成功", + "5nsczb3r79w0": "长度在 6 到 20 个数字、字母、特殊字符最少两种组合" + }, + "EditUser": { + "5m6nh3mgjns0": "修改", + "5m6nh3mgkn80": "账号", + "5m6nh3mgl2k0": "账号不能为空", + "5m6nh3mglh40": "请输入账号", + "5m6nh3mglmw0": "密码", + "5m6nh3mglt40": "密码不能为空", + "5m6nh3mglzg0": "请输入密码", + "5m6nh3mgm380": "昵称", + "5m6nh3mgm6o0": "昵称不能为空", + "5m6nh3mgm9s0": "请输入昵称", + "5m6nh3mgmds0": "手机号码", + "5m6nh3mgmko0": "手机号码不能为空", + "5m6nh3mgmp40": "请输入手机号码", + "5m6nh3mgmss0": "账号角色", + "5m6nh3mgmvs0": "请选择账号角色", + "5m6nh3mgmz40": "状态", + "5m6nh3mgn1w0": "启用", + "5m6nh3mgn4k0": "停用", + "5m6nh3mgn9c0": "备注", + "5m6nh3mgncw0": "请输入备注", + "5m6nh3mgnhs0": "取消", + "5m6nh3mgnkg0": "确定", + "5m6nh3mgnn40": "修改成功", + "5m6nh3mgnpo0": "添加成功", + "5n7v8xco9u80": "长度在 6 到 20 个数字、字母、特殊字符最少两种组合", + "5nsg5pkyao80": "手机号码格式不正确", + "else1": "添加", + "else2": "账号" + }, + "UpdateCode": { + "5n7vbkp1s580": "修改密码", + "5n7vbkp1t2s0": "旧密码", + "5n7vbkp1tb80": "旧密码不能为空", + "5n7vbkp1tiw0": "请输入旧密码", + "5n7vbkp1two0": "新密码", + "5n7vbkp1u340": "新密码不能为空", + "5n7vbkp1u8o0": "长度在 6 到 20 个数字、字母、特殊字符最少两种组合", + "5n7vbkp1uyw0": "请输入新密码", + "5n7vbkp1v8o0": "确认密码", + "5n7vbkp1vg00": "确认密码不能为空", + "5n7vbkp1vm40": "请再次输入新密码", + "5n7vbkp1vus0": "取消", + "5n7vbkp1vy00": "确定", + "5n7vbkp1w7c0": "两次输入的密码不一致" + }, + "PluginConfig": { + "5m6nj19s0700": "插件配置", + "5m6nj19s1lw0": "取消", + "5m6nj19s1rw0": "确定", + "5m6nj19s21s0": "配置成功", + "else1": "{attrLabel}不能为空", + "else2": "请输入{attrLabel}" + }, + "EditInfo": { + "5m6njon1w080": "修改用户信息", + "5m6njon1www0": "帐号", + "5m6njon1x200": "帐号不能为空", + "5m6njon1x5c0": "请输入帐号", + "5m6njon1x840": "用户昵称", + "5m6njon1xbk0": "昵称不能为空", + "5m6njon1xek0": "请输入用户昵称", + "5m6njon1xhg0": "手机号码", + "5m6njon1xkg0": "手机号码不能为空", + "5m6njon1zco0": "请输入手机号码", + "5m6njon1zmw0": "邮箱", + "5m6njon1zrg0": "请输入邮箱", + "5m6njon1zus0": "取消", + "5m6njon1zxs0": "确定", + "5m6njon200w0": "修改成功", + "5nslhhf6a7k0": "手机号码格式不正确", + "5nslhhf6z9o0": "邮箱格式不正确" + }, + "EditRole": { + "5m6nl1uvgfk0": "修改", + "5m6nl1uvjes0": "角色名称", + "5m6nl1uvjls0": "角色名称不能为空", + "5m6nl1uvjp80": "请输入角色名称", + "5m6nl1uvk7g0": "状态", + "5m6nl1uvkmc0": "启用", + "5m6nl1uvkq80": "停用", + "5m6nl1uvkw40": "菜单权限", + "5m6nl1uvl2o0": "全选/全不选", + "5m6nl1uvl5g0": "备注", + "5m6nl1uvl8k0": "请输入备注", + "5m6nl1uvlbw0": "取消", + "5m6nl1uvles0": "确定", + "5m6nl1uvlk00": "修改成功", + "5m6nl1uvlp00": "添加成功", + "5nsfs3sdwwg0": "角色名称为中文、英文、数字及其组合,不包含特殊字符", + "else1": "添加", + "else2": "角色" + }, + "EditCondition": { + "5m6nmc7dugg0": "修改", + "5m6nmc7dvbk0": "白名单名称", + "5m6nmc7dvgw0": "白名单名称不能为空", + "5m6nmc7dvkg0": "请输入白名单名称", + "5m6nmc7dvnk0": "限制条件", + "5m6nmc7dvrg0": "多个IP用逗号隔开", + "5m6nmc7dvug0": "取消", + "5m6nmc7dwk00": "确定", + "5m6nmc7dwr00": "修改成功", + "5m6nmc7dwuo0": "添加成功", + "5nse3d27z0k0": "IP格式不正确", + "else1": "添加", + "else2": "访问白名单" + }, + "login-form": { + "5m6noeaox740": "欢迎使用!", + "5m6noeaoxxg0": "账号不能为空", + "5m6noeaoy240": "账号", + "5m6noeaoy5c0": "密码不能为空", + "5m6noeaoy9c0": "密码", + "5m6noeap7a80": "记住密码", + "5m6noeap7n40": "登录", + "5m6noeap7y80": "登录成功", + "5mpi8serosw0": "openGauss DataKit", + "5o61dvpc2o00": "系统提示", + "5o61dvpc5cs0": "您的登录状态已过期,请重新登录", + "5o61dvpc5o80": "重新登录", + "5o62d201k380": "首页" + }, + "EditConfig": { + "5mjace1rbj80": "日志设置", + "5mjace1rc240": "日志输出级别", + "5mjace1rc9c0": "保留天数", + "5mjace1rcd40": "请输入保留天数", + "5mjace1rch40": "请输入正确的天数", + "5mjace1rckw0": "单个日志文件大小", + "5mjace1rco40": "请输入单个日志文件大小", + "5mjace1rcr80": "请输入正确的大小,示例(20kb,20mb,20gb)", + "5mjace1rcuk0": "最大占用空间", + "5mjace1rcxg0": "请输入最大占用空间", + "5mjace1rd0c0": "取消", + "5mjace1rd3o0": "确定", + "5mjace1rd6o0": "保存成功", + "5mjace1rd9o0": "示例:20kb、20mb、20gb" + }, + "AddHost": { + "else1": "请输入正确的端口号", + "else2": "记住密码", + "currentStatus": "当前状态", + "ipAddress": "内网IP", + "name": "名称", + "namePlaceholder": "请输入物理机名称", + "username": "用户名", + "usernamePlaceholder": "请输入用户名称", + "tags": "标签", + "tagsPlaceholder": "请输入或者选择标签", + "addServerSuc": "创建服务器成功", + "editServerSuc": "编辑服务器成功", + "addAgentSuc": "创建服务器成功, 开始安装Agent", + "unFill": "当前页面有未填写项", + "5mphy3snvg80": "可用", + "5mphy3snwq40": "不可用", + "5mphy3snwxs0": "取消", + "5mphy3snx3o0": "连通性测试", + "5mphy3snx7c0": "确定", + "5mphy3snxdo0": "请输入内网IP", + "5mphy3snxis0": "外网IP", + "5mphy3snxmw0": "请输入外网IP", + "5mphy3snxtc0": "端口号", + "5mphy3snxzk0": "请输入端口号", + "5mphy3sny4w0": "密码", + "5mphy3snyao0": "请输入密码", + "5mphy3snyg40": "可用区", + "5mphy3snyn80": "请选择所属AZ", + "5mphy3snysg0": "备注", + "5mphy3snyxc0": "请输入备注", + "5mphy3snz5k0": "新增物理机", + "5mphy3snzb80": "当前服务器不可用", + "5mphy3snzrk0": "编辑物理机", + "noPasswordTip": "未保存用户密码,请重新添加用户" + }, + "AddAgent": { + "agentTitle": "Agent安装", + "agentName": "Agent名称", + "installPath": "安装路径", + "namePlaceholder": "请输入Agent名称", + "installPathPlaceholder": "请输入安装路径", + "userPlaceholder": "请输入安装用户", + "formaterror": "安装路径格式错误", + "pathDepthExceeded": "安装路径深度不能超过五层", + "portPlaceholder": "请输入端口号", + "stringLengthOver": "路径长度不得超过255个字符", + "noChinesePath": "路径中请勿使用中文等非ASCII字符", + "numberRangeOver": "端口号超出范围限制", + "insAgentSuc": "开始安装Agent" + }, + "HostUserChoose": { + "title": "选择用户", + "tip": "当前保存有多个用户,且无root用户,请选择登录终端的用户", + "userChoosePlaceholder": "请选择用户" + }, + "AddHostUser": { + "else1": "确认", + "else2": "系统将会保存该用户的密码,是否继续保存?", + "rootPassword": "root密码", + "5mphzt9pc0g0": "主机信息", + "5mphzt9pdak0": "请输入root密码", + "5mphzt9pdn80": "用户名", + "5mphzt9pdw00": "请输入用户名", + "5mphzt9pe3s0": "密码", + "5mphzt9pec40": "请输入密码", + "5mphzt9peog0": "新增用户", + "updateUser": "编辑用户", + "5mphzt9pewg0": "传过来的数据", + "createSuccess": "创建成功", + "modifySuccess": "修改成功" + }, + "HostPwdDlg": { + "else1": "root密码", + "5mpi0giveek0": "生成", + "5mpi0giveys0": "主机信息", + "5mpi0givf8o0": "请输入密码", + "5mpi0givfcw0": "安全检验", + "5mpi0givfhc0": "请输入root密码", + "5mpi0givfl40": "请求发生异常" + }, + "HostUserMng": { + "5mpi1bru0w00": "添加用户", + "5mpi1bru1n40": "编辑", + "5mpi1bru2700": "确定要删除?", + "5mpi1bru2lo0": "确定", + "5mpi1bru2s00": "取消", + "oneUserTip": "至少需要保留一个用户", + "5mpi1bru2yo0": "删除", + "5mpi1bru3dk0": "用户管理", + "5mpi1bru3vk0": "用户名", + "isSudo": "是否拥有root权限", + "yes": "是", + "no": "否", + "5mpi1bru4c00": "操作" + }, + "BatchLabelDlg": { + "5pbjt280spg0": "已选择主机", + "5pbjt280xuo0": "个", + "5pbjt280yik0": "标签", + "5pbjt280z040": "标签批量设置", + "5quz8t9a2xg0": "操作类型", + "5quz8t9a5x00": "批量新增", + "5quz8t9a6ik0": "批量删除", + "5quz8t9a6zk0": "共有标签", + "5quz8t9a7fs0": "移除标签", + "5quz8t9a7w00": "无共有标签,不能批量删除" + }, + "AddAz": { + "azName": "AZ名称", + "5mpib0ipo0g0": "取消", + "5mpib0ipoo40": "确定", + "5mpib0ipov80": "请输入AZ名称", + "5mpib0ipozw0": "实际地理位置", + "5mpib0ipp3k0": "请输入实际地理位置", + "5mpib0ipp7w0": "优先级", + "5mpib0ippbo0": "请输入优先级", + "5mpib0ippfc0": "描述", + "5mpib0ippjg0": "请输入描述", + "5mpib0ippoc0": "新增可用区", + "5mpib0ipprs0": "编辑可用区", + "else1": "名称已存在", + "else2": "判断名称是否存在出错" + }, + "Package": { + "5mtcyb0rty02": "新增安装包", + "5mtcyb0rty03": "批量检查", + "5mtcyb0rty04": "批量删除", + "5mtcyb0rty05": "请输入或者选择表", + "5mtcyb0rty06": "安装包名称", + "5mtcyb0rty07": "操作系统", + "5mtcyb0rty08": "CPU架构", + "5mtcyb0rty09": "openGauss版本", + "5mtcyb0rty10": "版本号", + "5mtcyb0rty11": "安装来源", + "5mtcyb0rty12": "下载地址/离线安装包", + "5mtcyb0rty13": "操作", + "5mtcyb0rty14": "检查", + "5mtcyb0rty15": "更新", + "5mtcyb0rty16": "是否确定删除?", + "5mtcyb0rty17": "确定", + "5mtcyb0rty18": "取消", + "5mtcyb0rty19": "删除", + "5mtcyb0rty20": "未查询到该条件下的安装包", + "5mtcyb0rty21": "您可以离线上传或者在线下载相应的安装包", + "5mtcyb0rty22": "离线上传", + "5mtcyb0rty23": "在线下载", + "5mtcyb0rty24": "下载完成", + "5mtcyb0rty25": "同一个父级标签只能选择一个子级标签,{tagName}标签下有多项,仅保留第一项", + "5mtcyb0rty26": "检查完成", + "5mtcyb0rty27": "检查失败", + "5mtcyb0rty28": "请至少选择一个安装包", + "5mtcyb0rty29": "删除成功", + "5mtcyb0rty30": "已删除全部选择安装包", + "5mtcyb0rty31": "删除失败", + "ENTERPRISE": "企业版", + "MINIMAL_LIST": "极简版", + "LITE": "轻量版", + "5mtcyb0rty32": "系统架构", + "5mtcyb0rty33": "版本类型", + "5mtcyb0rty34": "请输入版本号", + "5mtcyb0rty35": "当前版本为新输入版本号,可", + "5mtcyb0rty36": "前往官网", + "5mtcyb0rty37": "查看版本号是否存在,如不存在则会下载失败", + "5mtcyb0rty38": "在线下载地址", + "5mtcyb0rty39": "根据上述配置信息匹配安装包,显示该安装包的在线下载地址进行下载安装,如未匹配到相应的安装包或不是目标安装包,您可以进行", + "5mtcyb0rty40": " 根据上述配置信息匹配安装包。由于网络未连接,信息仅供显示无法下载。请上传离线安装包。 ", + "5mtcyb0rty41": "离线安装包", + "5mtcyb0rty42": "可将文件拖至此处 或 选择文件", + "5mtcyb0rty43": "选择文件", + "5mtcyb0rty44": "版本号{versionNum}不存在,请重新选择", + "5mtcyb0rty45": "请选择版本号", + "5mtcyb0rty46": "请输入安装包名称", + "5mtcyb0rty47": "请勿输入重复包名", + "5mtcyb0rty48": "请选择一个安装包进行上传", + "5mtcyb0rty49": "获取版本号失败", + "5mtcyb0rty50": "添加安装包", + "5mtcyb0rty51": "更新安装包", + "5mtcyb0rty52": "文件下载失败,请检查下载地址或者网络连接是否正确", + "5mtcyb0rty53": "根据配置匹配到如下安装包并进行下载", + "5mtcyb0rty54": "将文件拖拽到此处 或", + "5mtcyb0rty55": "选择文件", + "5mtcyb0rty56": "请选择.jar文件", + "5mtcyb0rty57": "长时间无响应,将关闭下载进度悬浮框", + "5mtcyb0rty58": "请选择指定类型的文件", + "5mtcyb0rty59": "文件大小超过限制", + "5mtcyb0rty60": "请选择操作系统", + "5mtcyb0rty61": "请选择cpu架构", + "5mtcyb0rty62": "请选择openGauss版本", + "5mtcyb0rty63": "请输入安装包名称" + }, + "SystemAlarm": { + "cpuWarning": "CPU告警", + "5mpihybegg40": "系统告警", + "5mpihybehhk0": "等级", + "5mpihybeho00": "告警内容", + "5mpihybehvw0": "时间", + "5mpihybei2o0": "内存告警", + "5mpihybei740": "存储告警", + "5mpihybeiaw0": "业务流出错" + }, + "OneCheck": { + "clusterName": "集群名称", + "else1": "hash index语法检查", + "else2": "Node group编码格式检查", + "else3": "TD数据库中orc表date类型列检查", + "else4": "检测完成时间", + "else5": "检查项", + "else6": "root密码", + "else7": "请输入root密码", + "5mpiji1q2ng0": "一键自检", + "5mpiji1q3wo0": "请选择集群名称", + "5mpiji1q4a00": "重新检测", + "5mpiji1q4k40": "检测", + "5mpiji1q4tg0": "正在进行一键自检", + "5mpiji1q57w0": "集群", + "5mpiji1q5r40": "检查openGauss状态", + "5mpiji1q5zw0": "检查openGauss参数", + "5mpiji1q6b00": "检查日志级别", + "5mpiji1q8q80": "检查目录权限", + "5mpiji1q96c0": "检查只读模式", + "5mpiji1q9r00": "检查环境变量", + "5mpiji1qa080": "检查系统表膨胀", + "5mpiji1qa940": "关键进程启动时间检测", + "5mpiji1qahc0": "检测环境变量分离文件改动", + "5mpiji1qas40": "数据库", + "5mpiji1qbv80": "检查当前连接数", + "5mpiji1qc780": "检查当前游标数", + "5mpiji1qcjs0": "检查pgxc_group表中需要重分布的个数", + "5mpiji1qczo0": "检查sysadmin用户", + "5mpiji1qdeo0": "检查表空间路径", + "5mpiji1qdlk0": "检查重分布残留的临时表", + "5mpiji1qds80": "系统", + "5mpiji1qdy40": "检查内存总大小", + "5mpiji1qe8s0": "检查CPU核数", + "5mpiji1qehc0": "检查操作系统版本", + "5mpiji1qeoo0": "检查关键进程omm_adj的值", + "5mpiji1qf280": "检查max_process_memory参数设置是否合理", + "5mpiji1qf9k0": "检查编码格式", + "5mpiji1qfgo0": "检查防火墙状态", + "5mpiji1qfnc0": "检查内核版本", + "5mpiji1qfx00": "检查句柄最大设置", + "5mpiji1qg400": "检查NTPD服务", + "5mpiji1qga80": "检查系统参数", + "5mpiji1qgl40": "检查THP服务", + "5mpiji1qgt80": "检查时区一致性", + "5mpiji1qh2k0": "检查sshd服务是否已启动", + "5mpiji1qh8s0": "检查sshd服务配置是否正确", + "5mpiji1qhfs0": "检查crontab服务是否已启动", + "5mpiji1qhn00": "栈深度检查", + "5mpiji1qhto0": "检查ip_local_port_range设置范围", + "5mpiji1qi2s0": "检查超线程是否打开", + "5mpiji1qif40": "检查启动项", + "5mpiji1qioc0": "检查文件句柄", + "5mpiji1qizk0": "检查DropCache进程", + "5mpiji1qjag0": "主机", + "5mpiji1qjh80": "检查磁盘预读块", + "5mpiji1qkn00": "检查磁盘格式参数", + "5mpiji1qkyk0": "检查交换内存", + "5mpiji1ql580": "检查磁盘逻辑块", + "5mpiji1qldc0": "检查IO请求", + "5mpiji1qlmc0": "检查最大异步IO请求", + "5mpiji1qltc0": "检查磁盘使用率", + "5mpiji1qm0s0": "检查磁盘inodes使用率", + "5mpiji1qmbw0": "检查IO配置", + "5mpiji1qmj40": "网络", + "5mpiji1qmqg0": "检查网络通畅", + "5mpiji1qn1c0": "检查网络掉包率", + "5mpiji1qn8g0": "网卡型号和驱动版本一致性检查", + "5mpiji1qnf00": "检查网卡MTU值", + "5mpiji1qnks0": "检查网卡RXTX值", + "5mpiji1qnr00": "检查网卡多队列", + "5mpiji1qnxs0": "本地路由表检查", + "5mpiji1qo440": "项", + "5mpiji1qoe00": "不通过", + "5mpiji1qom40": "内部错误", + "5mpiji1qosc0": "警告", + "5mpiji1qoyw0": "通过", + "5mpiji1qpcc0": "检查类别", + "5mpiji1qpns0": "检查项", + "5mpiji1qpvg0": "检测结果", + "5mpiji1qq180": "查看一键自检返回" + }, + "ModuleSummary": { + "else1": "个", + "else2": "未安装baseops插件,无法查看相关数据", + "5mpim9s7gw80": "业务流", + "5mpim9s7hqk0": "数据流", + "5mpim9s7hxk0": "数据模型", + "5mpim9s7i7o0": "已安装插件" + }, + "CommonOper": { + "else1": "AZ管理", + "5mpimo1efn00": "常用操作", + "5mpimo1egl80": "物理机管理", + "5mpimo1egv80": "数据流设计", + "5mpimo1eh8o0": "日志中心", + "5mpimo1ehfw0": "集群监控" + }, + "ClusterTop": { + "5mpinksdbe40": "集群一览", + "5mpinksdby80": "一键自检", + "5mpinksdc4g0": "集群运维", + "5mpinksdc8g0": "个", + "5mpinksdcc40": "集群", + "5mpinksdcgk0": "主机", + "5mpinksdckw0": "实例" + }, + "ClusterList": { + "else1": "实例", + "else2": "会话", + "else3": "连接数", + "else4": "锁", + "else5": "可用区", + "else6": "个", + "else7": "内网IP", + "else8": "外网IP", + "else9": "主机名称", + "else10": "主机配置", + "else11": "核", + "else12": "地理位置", + "else13": "CPU使用率", + "else14": "连接成功", + "else15": "连接失败,重试连接", + "5mpinxl8f0w0": "正在努力获取集群信息", + "5mpinxl8frs0": "连接超时", + "5mpinxl8g2c0": "内存使用率", + "5mpinxl8gaw0": "网络状态", + "5mpinxl8gi40": "连接数", + "5mpinxl8gpc0": "暂无集群信息", + "5mpinxl8gwg0": "创建集群", + "5mpinxl8h380": "获取集群信息", + "5mpinxl8hbk0": "极简版", + "5mpinxl8his0": "轻量版", + "5mpinxl8hqc0": "企业版", + "5mpinxl8i0g0": "主", + "5mpinxl8i7k0": "备", + "5mpinxl8if40": "级联", + "5mpinxl8img0": "可用", + "5mpinxl8isw0": "不可用", + "5mpinxl8j000": "未检测", + "nodeState1": "可用", + "nodeState2": "需要修复", + "nodeState3": "启动中", + "nodeState4": "等待升级中", + "nodeState5": "正在升级为主节点", + "nodeState6": "降级中", + "nodeState7": "启动失败,需要重建", + "nodeState8": "追赶主节点", + "nodeState9": "崩溃", + "nodeState10": "未知" + }, + "BusiFlowSummary": { + "5mpivn7fwjc0": "数据流分布", + "5mpivn7fx3k0": "数据源类型分布", + "5mpivn7fxfc0": "数据流实例", + "5mpivn7fxjk0": "集群", + "5mpivn7fxnc0": "类型", + "5mpivn7fxrk0": "查询数量" + }, + "EditSysSettings": { + "5mpivn7fwjc1": "修改系统设置", + "5mpivn7fwjc2": "文件上传路径", + "5mpivn7fwjc6": "请输入文件上传路径", + "5mpivn7fwjc9": "上传路径不正确,必须以/或\\结尾", + "5mpivn7fwj10": "目录已被其它用户占用,请输入别的路径", + "5mpivn7fwj11": "迁移套件在线下载地址", + "5mpivn7fwj12": "请输入迁移套件在线下载地址", + "5mpivn7fwj13": "迁移套件安装包名称", + "5mpivn7fwj14": "请输入迁移套件安装包名称", + "5mpivn7fwj15": "迁移套件jar包名称", + "5mpivn7fwj16": "请输入迁移套件jar包名称", + "5mpivn7fwj17": "DataKit服务地址", + "5mpivn7fwj18": "请输入DataKit服务器地址" + } + }, + "user": { + "index": { + "5m6ni29gbi40": "账号/用户昵称", + "5m6ni29gdag0": "请输入账号/昵称", + "5m6ni29gdhw0": "手机号", + "5m6ni29gdog0": "请输入手机号", + "5m6ni29gdrk0": "状态", + "5m6ni29gdus0": "请选择", + "5m6ni29gdxs0": "启用", + "5m6ni29ge1s0": "停用", + "5m6ni29gglk0": "查询", + "5m6ni29ggtw0": "重置", + "5m6ni29ggx40": "添加账号", + "5m6ni29ghe80": "账号", + "5m6ni29ghlc0": "昵称", + "5m6ni29ghrk0": "创建时间", + "5m6ni29ghug0": "操作", + "5m6ni29h4i80": "修改", + "5m6ni29h4yc0": "是否确认删除此用户", + "5m6ni29h7pc0": "删除", + "5m6ni29h80k0": "重置密码", + "5m6ni29h84w0": "删除成功", + "5msnsk9mm9k0": "账号角色", + "5obuyt8kr2o0": "超级管理员" + } + }, + "ucenter": { + "index": { + "5m6nkbklgtk0": "更换头像", + "5m6nkbklieo0": "上传成功" + } + }, + "role": { + "index": { + "5m6nlovf8ik0": "角色名称", + "5m6nlovfao80": "请输入角色名称", + "5m6nlovfawc0": "状态", + "5m6nlovfb040": "请选择状态", + "5m6nlovfb3c0": "启用", + "5m6nlovfb700": "停用", + "5m6nlovfba00": "查询", + "5m6nlovfbdc0": "重置", + "5m6nlovfbgw0": "添加角色", + "5m6nlovfbjw0": "停用", + "5m6nlovfbmw0": "创建时间", + "5m6nlovfbqc0": "操作", + "5m6nlovfbtc0": "修改", + "5m6nlovfbwg0": "是否确认删除此角色", + "5m6nlovfc000": "删除", + "5m6nlovfc380": "删除成功" + } + }, + "whitelist": { + "index": { + "5m6nn2djbsk0": "白名单名称", + "5m6nn2djccs0": "请输入白名单名称", + "5m6nn2djchs0": "类型", + "5m6nn2djclc0": "请选择类型", + "5m6nn2djcpg0": "类型1", + "5m6nn2djd580": "类型2", + "5m6nn2djda00": "查询", + "5m6nn2djddo0": "重置", + "5m6nn2djdgw0": "添加白名单", + "5m6nn2djdjw0": "限制IP", + "5m6nn2djdmo0": "创建时间", + "5m6nn2djdpc0": "操作", + "5m6nn2djds00": "修改", + "5m6nn2djdv00": "是否确认删除此白名单", + "5m6nn2djdxo0": "删除", + "5m6nn2dje0g0": "删除成功" + } + }, + "navbar": { + "index": { + "5m6nsk34ptw0": "openGauss DataKit", + "5m6nsk34qkk0": "简体中文", + "5m6nsk34qpg0": "快捷功能", + "5m6nsk34qss0": "运维工具控制台", + "5m6nsk34qvk0": "一键自检", + "5m6nsk34qyk0": "物理机管理", + "5m6nsk34r1g0": "会话管理", + "5m6nsk34r4g0": "参数设置", + "5m6nsk34r7c0": "集群运维", + "5m6nsk34ra40": "个人中心", + "5m6nsk34rd00": "修改密码", + "5m6nsk34rd01": "系统设置", + "5m6nsk34rg40": "退出登录", + "5mpib9iig6o0": "openGauss DataKit", + "5nu3bvtnu5o0": "点击切换为黑暗模式", + "5nu3bvtnuyk0": "点击切换为亮色模式", + "5nu4bvtnuyk0": "oGPilot高斯小智处于试用阶段,可发送公司/学校、公网IP至", + "5nu5bvtnuyk0": "进行申请", + "5nu6bvtnuyk0": "去申请", + "5nu7bvtnuyk0": "已申请,访问oGPilot" + } + }, + "tab-bar": { + "tab-item": { + "5m6ntgxo4tg0": "关闭当前标签页", + "5m6ntgxo9vc0": "关闭左侧标签页", + "5m6ntgxoa240": "关闭右侧标签页", + "5m6ntgxoa540": "关闭其它标签页", + "5m6ntgxoadc0": "关闭全部标签页", + "subTaskMigraTab": "之前的迁移子任务详情tab页签将关闭,只展示当前的迁移子任务详情tab页" + } + }, + "sys-log": { + "index": { + "5mja76hbg2g0": "文件名", + "5mja76hbh680": "输入文件名称", + "5mja76hbhes0": "查询", + "5mja76hbhl40": "重置", + "5mja76hbhqc0": "日志设置", + "5mja76hbhvo0": "文件大小", + "5mja76hbi1c0": "创建时间", + "5mja76hbi6c0": "操作", + "5mja76hbib80": "下载", + "5mja76hbim00": "下载中", + "5mja76hbiy80": "下载失败,请重试" + } + }, + "oper-log": { + "index": { + "5mjaax4r6ko0": "系统模块", + "5mjaax4r7fs0": "输入模块名称", + "5mjaax4r7mc0": "操作人员", + "5mjaax4r7qc0": "输入名称", + "5mjaax4r7to0": "操作类型", + "5mjaax4r7x80": "HTTP状态", + "5mjaax4r80g0": "成功", + "5mjaax4r83o0": "失败", + "5mjaax4r89o0": "查询", + "5mjaax4r8d80": "重置", + "5mjaax4r8jo0": "请求方式", + "5mjaax4r8mo0": "操作日期", + "5mjaax4r8pw0": "操作", + "5mjaax4r8sw0": "详情", + "5mjaax4r8vw0": "操作模块:", + "5mjaax4r8z40": "登录信息:", + "5mjaax4r92g0": "请求地址:", + "5mjaax4r9co0": "请求方式:", + "5mjaax4r9hk0": "操作方法:", + "5mjaax4r9m00": "请求参数:", + "5mjaax4r9ow0": "返回参数:", + "5mjaax4r9sw0": "HTTP状态:", + "5mjaax4r9vo0": "操作时间:", + "5mjaax4r9y00": "异常信息:", + "5mjaax4ra0g0": "其他", + "5mjaax4ra3w0": "新增", + "5mjaax4ra6c0": "修改", + "5mjaax4ra8w0": "删除", + "5mjaax4rab80": "授权", + "5mjaax4rads0": "启动", + "5mjaax4rag80": "停止", + "5mjaax4raio0": "安装", + "5mjaax4ral00": "卸载" + } + }, + "login": { + "index": { + "5mpi5tn6irc0": "openGauss DataKit" + } + }, + "physical": { + "index": { + "createServer": "创建服务器", + "ipAddress": "IP地址", + "ipAddressPlaceholder": "请输入IP地址查询", + "batchDel": "删除", + "hostLabel": "标签", + "hostLabelPlaceholder": "请输入标签进行查询", + "os": "操作系统", + "osPlaceholder": "请选择操作系统进行查询", + "serverName": "服务器名称", + "privateIp": "内网IP", + "publicIp": "外网IP", + "privateAndPublicIP": "内网/外网IP", + "cpuArch": "架构", + "hostName": "主机名称", + "net": "网络", + "realNetRate": "实时网络速率", + "upLink": "上行", + "downlink": "下行", + "cpu": "CPU使用率", + "memory": "内存使用率", + "disk": "磁盘使用率", + "agentStatus": "Agent状态", + "running": "运行中", + "offline": "离线", + "uninstalled": "未安装", + "agentManager": "Agent管理", + "agentName": "Agent名称", + "operationStatus": "运行状态", + "serverIp": "服务器IP", + "port": "端口", + "installPath": "安装路径", + "userName": "用户名", + "osMetrics": "OS指标", + "baseInfo": "基本信息", + "diskInfo": "硬件信息", + "noUserTip": "暂无用户,请先添加用户", + "tipInfo": "前提:保存主机时需记住密码,才能实时获取数据。否则不能获取", + "realTime": "实时", + "detail": "详情", + "labelBatch": "标签设置", + "labelManage": "标签管理", + "else1": "请至少选一个主机信息", + "noData": "暂无数据", + "operateColumn": "操作", + "agentInstall": "Agent安装", + "installAgent": "安装Agent", + "userManage": "用户管理", + "more": "更多", + "reback": "返回", + "stopSuccess": "停止成功", + "startSuccess": "启动成功", + "uninstallSuccess": "卸载成功", + "serverConfig": "服务器配置", + "5mphf11rr080": "创建", + "5mphf11syw80": "测试连通性", + "5mphf11szdk0": "搜索", + "5mphf11szks0": "用户管理", + "5mphf11szqo0": "编辑", + "5mphf11szws0": "确定要删除?", + "5mphf11t05c0": "确定", + "5mphf11t0bc0": "取消", + "5mphf11t0hc0": "删除", + "5mphf11t0yc0": "外网IP", + "5mphf11te5c0": "所属可用区", + "5mphf11teq40": "状态", + "5mphf11tf5g0": "备注", + "5mphf11tfjw0": "操作", + "5mphf11tfr80": "未检测", + "5mphf11tfx80": "不可用", + "5mphf11tg780": "可用", + "5mphf11rr090": "批量导入", + "5mphf11rr590": "批量删除", + "5mphf11rr890": "已删除所有选中主机", + "5mphf11rr990": "部分主机没有成功删除", + "stop": "停止", + "start": "开始", + "close": "关闭", + "uninstall": "卸载", + "addNewUser": "新增用户", + "unInstalledAgent": "存在未卸载的agent", + "selectUserTable": "用户管理" + } + }, + "label": { + "LabelManageDlg": { + "5pbjv7b0vrw0": "创建", + "5pbjv7b0wu80": "标签名称", + "5pbjv7b0x3w0": "请输入标签名称", + "5pbjv7b0xak0": "编辑", + "5pbjv7b0xgo0": "关联主机数", + "5pbjv7b0y0w0": "操作", + "5pbjv7b0yd00": "创建标签", + "5pbjv7b0yk40": "编辑标签", + "5pbjv7b0yso0": "标签管理", + "addLabel": "添加标签", + "tagNamePlaceholder": "请输入标签名称进行搜索", + "selectUserTable": "选择用户表", + "delWarning":"请先选择删除的用户" + } + }, + "bulk": { + "BulkImport": { + "5exv06n8x1kh0": "批量导入", + "5exv06n8x1ki0": "选择文件", + "5exv06n8x1kj0": "将文件拖至此处 或", + "5exv06n8x1kk0": "文件解析", + "5exv06n8x1kl0": "导入到服务器", + "5exv06n8x1km0": "导入完成", + "5exv06n8x1kn0": "文件解析中,请稍等", + "5exv06n8x1ko0": "重新上传", + "5exv06n8x1kp0": "您仍可以通过拖拽文件到下方区域进行文件替换", + "5exv06n8x1kq0": "文件上传成功", + "5exv06n8x1kr0": "重新导入", + "5exv06n8x1ks0": "完成", + "5exv06n8x1kt0": "下载错误报告", + "5exv06n8x1ku0": "导入模板下载", + "5exv06n8x1kv0": "文件上传中", + "5exv06n8x1kx0": "文件解析失败", + "5exv06n8x1ky0": "仅支持上传Excel格式文件,大小不超过200MB", + "5exv06n8x1kz0": "删除", + "5exv06n8x1ka0": "确定", + "5exv06n8x1kb0": "取消", + "5exv06n8x1kw0": "文件上传失败,需上传Excel格式的文件,请重新上传", + "5exv06n8x1kc0": "文件上传失败,需上传200M以下文件,请重新上传", + "5exv06n8x1kd0": "文件上传失败,需上传单个文件,请重新上传", + "5exv06n8x1ke0": ",请修改文件并", + "5exv06n8x1kf0": "文件因为不明原因上传失败,请重新上传", + "5exv06n8x1kh1": "请不要刷新界面", + "5exv06n8x1ki1": "共导入", + "5exv06n8x1kj1": "条数据", + "5exv06n8x1kk1": "条", + "5exv06n8x1kl1": "导入成功,", + "5exv06n8x1km1": "导入失败", + "5exv06n8x1kn1": "下载错误报告", + "step1Content": "1.文件解析", + "step1Error": "1.文件解析(失败)", + "step2Content": "2.导入到服务器", + "step3Content": "3.导入完成" + } + }, + "az": { + "index": { + "azName": "AZ名称", + "5mpi9hkpfhk0": "新增可用区", + "5mpi9hkpgdw0": "搜索...", + "5mpi9hkpgns0": "编辑", + "5mpi9hkpgv80": "确定要删除?", + "5mpi9hkph9k0": "确定", + "5mpi9hkphi40": "取消", + "5mpi9hkphpc0": "删除", + "5mpi9hkphvo0": "实际地理位置", + "5mpi9hkpi2o0": "优先级", + "5mpi9hkpi940": "备注", + "5mpi9hkpif40": "操作" + } + }, + "database": { + "AddJdbc": { + "else1": "待检测", + "5oxhkhiks5k0": "可用", + "5oxhkhimwfg0": "不可用", + "5oxhkhimwy00": "取消", + "5oxhkhimx5c0": "测试连通性", + "5oxhkhimxbg0": "确定", + "5oxhkhimxho0": "集群名称", + "isCustomName": "自定义名称", + "customNamePlaceholder": "集群名称将会根据实例信息生成", + "5oxhkhimxno0": "请输入实例名称", + "5oxhkhimxto0": "数据库类型", + "5oxhkhimxzw0": "请选择数据库类型", + "5oxhkhimyio0": "实例", + "5oxhkhimz480": "请输入集群名称", + "5oxhkhimzcg0": "不能为纯空格", + "5oxhkhimzmw0": "修改数据源", + "5oxhkhimzww0": "新增数据源", + "checkPassword": "编辑时请重新输入密码" + }, + "HostImportDlg": { + "5oxhmjuzltc0": "解析结果:有以下错误数据,请修改后重新上传", + "5oxhmjuzmxw0": "集群名称", + "5oxhmjuzn9s0": "连接地址", + "5oxhmjuznig0": "解析结果", + "5oxhmjuzz7w0": "导入解析" + }, + "HostPwdDlg": { + "5oxhni610s00": "IP地址", + "5oxhni611qo0": "端口号", + "5oxhni6126o0": "请输入SSH端口号", + "5oxhni612g00": "用户名", + "5oxhni612mo0": "请输入用户名", + "5oxhni612vk0": "密码", + "5oxhni6132k0": "root密码", + "5oxhni6138s0": "请输入密码", + "5oxhni613ik0": "请输入正确的端口号", + "5oxhni613ow0": "不能为纯空格", + "5oxhni613us0": "SSH连接信息" + }, + "HostTerminal": { + "else1": "IP地址", + "else2": "在特殊屏幕分辨率下如果出现字体模糊等问题,请对网页进行缩放", + "5oxhoh5r6ls0": "终端" + }, + "index": { + "else1": "集群名称", + "else2": "确认上传", + "elseIp": "IP地址", + "elseIpPlaceholder": "请输入IP地址进行查询", + "else3": "类型", + "else3Placeholder": "请选择类型进行查询", + "5oxhr0qz15w0": "创建", + "5oxhr0qz2bs0": "下载模板", + "5oxhr0qz2s00": "请输入集群名称进行查询", + "5oxhr0qz30g0": "查询", + "5oxhr0qz37o0": "修改", + "5oxhr0qz3f40": "确定要删除?", + "5oxhr0qz3m80": "确定", + "5oxhr0qz3t80": "取消", + "5oxhr0qz40k0": "删除", + "5oxhr0qz48w0": "集群名称", + "5oxhr0qz4fs0": "类型", + "5oxhr0qz4no0": "状态", + "5oxhr0qz4zk0": "更新时间", + "5oxhr0qz58o0": "操作", + "5oxhr0qz5g40": "上传确认" + }, + "JdbcInstance": { + "5oxhtcbo8ac0": "IP地址", + "5oxhtcbo9fg0": "请选择或输入IP", + "5oxhtcbo9ps0": "端口", + "5oxhtcbo9xc0": "请输入端口号", + "5oxhtcboa240": "用户名", + "5oxhtcboa7c0": "请输入用户名", + "5oxhtcboac00": "密码", + "5oxhtcboags0": "请输入密码", + "5oxhtcboap00": "连接扩展属性", + "5oxhtcboatc0": "新增", + "5oxhtcboaxc0": "删除", + "5oxhtcbob1s0": "连接地址", + "5oxhtcbob640": "扩展名", + "5oxhtcbobac0": "扩展值", + "5oxhtcbobe80": "操作", + "5oxhtcbobhs0": "请选择或输入IP地址", + "5oxhtcboblw0": "请输入正确的IP地址", + "5oxhtcbobpk0": "请输入正确的端口号", + "5oxhtcbobtc0": "不能为纯空格", + "5oxhtcboc2c0": "请输入用户密码" + }, + "JdbcNodeTable": { + "else1": "IP地址", + "else2": "端口", + "else3": "节点", + "else4": "未知", + "else5": "操作系统", + "else6": "实时", + "else7": "会话数", + "else8": "锁数量", + "5oxhv6qcm6w0": "去检测", + "5oxhv6qcnak0": "主", + "5oxhv6qcnnk0": "备", + "5oxhv6qcnuk0": "连接数", + "5oxhv6qco4c0": "表空间占用", + "5oxhv6qcobk0": "内存占用" + } + }, + "echarts": { + "BusiFlowSummary": { + "5mpiw64lv300": "业务流数据" + }, + "DataSourceSummary": { + "5mpiwzpxnsg0": "数据源类型分布" + } + }, + "protocol": { + "index": { + "5mpixm5ckcw0": "正在开发中,敬请期待" + } + }, + "footer": { + "index": { + "5msicm67hz00": "版权所有" + } + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/locale/zh-CN.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/zh-CN.ts new file mode 100644 index 000000000..a7330f849 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/locale/zh-CN.ts @@ -0,0 +1,6 @@ + +import cn from './locale/zh-cn.json' + +export default { + ...cn +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/main.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/main.ts new file mode 100644 index 000000000..d0b676941 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/main.ts @@ -0,0 +1,37 @@ +import { createApp } from 'vue' +import ArcoVue from '@arco-design/web-vue' +import ArcoVueIcon from '@arco-design/web-vue/es/icon' +import WujieVue from 'wujie-vue3' +import globalComponents from '@/components' +import App from './App.vue' +import router from './router' +import store from './store' +import '@/assets/icons' +import '@/api/interceptor' + +import '@arco-design/web-vue/dist/arco.less' +import '@/assets/style/global.less' + +import i18n from './locale/index' + +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +import opendesign2 from '@computing/opendesign2' +import '@computing/opendesign2/themes/es/css' + +const app = createApp(App) + +app.use(ArcoVue, {}) +app.use(ArcoVueIcon) + +app.use(ElementPlus) +app.use(opendesign2) + +app.use(router) +app.use(store) +app.use(globalComponents) +app.use(WujieVue) + +app.use(i18n) + +app.mount('#app') diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/appMenus/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/appMenus/index.ts new file mode 100644 index 000000000..fa3cfdc6c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/appMenus/index.ts @@ -0,0 +1,16 @@ +import { appRoutes } from '../routes' + +const mixinRoutes = [...appRoutes] + +const appClientMenus = mixinRoutes.map((el) => { + const { name, path, meta, redirect, children } = el + return { + name, + path, + meta, + redirect, + children + } +}) + +export default appClientMenus diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/constants.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/constants.ts new file mode 100644 index 000000000..2e84d3620 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/constants.ts @@ -0,0 +1,11 @@ +export const DEFAULT_LAYOUT = () => import('@/layout/default-layout.vue') +export const ParentView = () => import('@/layout/parent-view/index.vue') + +export const WHITE_LIST = [ + { name: 'notFound', children: [] }, + { name: 'login', children: [] } +] + +export const NOT_FOUND = { + name: 'notFound' +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/guard/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/guard/index.ts new file mode 100644 index 000000000..ae8602e7c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/guard/index.ts @@ -0,0 +1,17 @@ +import type { Router } from 'vue-router' +import { setRouteEmitter } from '@/utils/route-listener' +import setupUserLoginInfoGuard from './userLoginInfo' +import setupPermissionGuard from './permission' + +function setupPageGuard (router: Router) { + router.beforeEach(async (to) => { + // emit route change + setRouteEmitter(to) + }) +} + +export default function createRouteGuard (router: Router) { + setupPageGuard(router) + setupUserLoginInfoGuard(router) + setupPermissionGuard(router) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/guard/permission.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/guard/permission.ts new file mode 100644 index 000000000..e7f863d88 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/guard/permission.ts @@ -0,0 +1,49 @@ +import type { Router } from 'vue-router' +import NProgress from 'nprogress' + +import usePermission from '@/hooks/permission' +import { useUserStore, useAppStore } from '@/store' +import { appRoutes } from '../routes' +import { WHITE_LIST, NOT_FOUND } from '../constants' + +export default function setupPermissionGuard (router: Router) { + router.beforeEach(async (to, from, next) => { + const appStore = useAppStore() + const userStore = useUserStore() + const Permission = usePermission() + const permissionsAllow = Permission.accessRouter(to) + if (appStore.menuFromServer) { + // Handle routing configuration from the server + + // Refine the permission logic from the server's menu configuration as needed + if ( + !appStore.appAsyncMenus.length && + !WHITE_LIST.find((el) => el.name === to.name) + ) { + await appStore.fetchServerMenuConfig() + } + // const serverMenuConfig = [...appStore.appAsyncMenus, ...WHITE_LIST] + // The route obtained from the backend is appended to the routing system + appStore.appAsyncMenus.forEach(route => router.addRoute(route)) + let exist = false + const routes = router.getRoutes() + while (routes.length && !exist) { + const element = routes.shift() + if (element?.name === to.name) exist = true + } + if (exist && permissionsAllow) { + next() + } else next(NOT_FOUND) + } else { + // eslint-disable-next-line no-lonely-if + if (permissionsAllow) next() + else { + const destination = + Permission.findFirstPermissionRoute(appRoutes, userStore.role) || + NOT_FOUND + next(destination) + } + } + NProgress.done() + }) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/guard/userLoginInfo.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/guard/userLoginInfo.ts new file mode 100644 index 000000000..cef2f5a91 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/guard/userLoginInfo.ts @@ -0,0 +1,49 @@ +import type { Router, LocationQueryRaw } from 'vue-router' +import NProgress from 'nprogress' // progress bar + +import { useUserStore } from '@/store' +import { isLogin } from '@/utils/auth' + +const whiteList = ['login'] + +export default function setupUserLoginInfoGuard (router: Router) { + let isExpired = false + router.beforeEach(async (to, from, next) => { + NProgress.start() + const userStore = useUserStore() + if (isLogin()) { + if (userStore.userId) { + next() + } else { + if (!isExpired) { + try { + await userStore.info() + isExpired = false + next() + } catch (error) { + isExpired = true + next({ + name: 'login', + query: { + redirect: to.name, + ...to.query + } as LocationQueryRaw + }) + } + } + } + } else { + if (~whiteList.indexOf(to.name as string)) { + next() + return + } + next({ + name: 'login', + query: { + redirect: to.name, + ...to.query + } as LocationQueryRaw + }) + } + }) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/index.ts new file mode 100644 index 000000000..b6e14247e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/index.ts @@ -0,0 +1,35 @@ +import { createRouter, createWebHashHistory } from 'vue-router' +import NProgress from 'nprogress' // progress bar +import 'nprogress/nprogress.css' + +import { appRoutes } from './routes' +import createRouteGuard from './guard' + +NProgress.configure({ showSpinner: false }) // NProgress Configuration + +const router = createRouter({ + history: createWebHashHistory(), + routes: [ + { + path: '/login', + name: 'login', + component: () => import('@/views/login/index.vue'), + meta: { + requiresAuth: false + } + }, + ...appRoutes, + { + path: '/:pathMatch(.*)*', + name: 'notFound', + component: () => import('@/views/not-found/index.vue') + } + ], + scrollBehavior () { + return { top: 0 } + } +}) + +createRouteGuard(router) + +export default router diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/index.ts new file mode 100644 index 000000000..94ed4b38b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/index.ts @@ -0,0 +1,52 @@ +import type { RouteRecordNormalized } from 'vue-router' +import { filterAsyncRouter } from '@/utils/route' +import i18n from '@/locale/index' + +const context = require.context('./modules', true, /\.ts$/) +const modules = context.keys() +const localeRoutes = localStorage.getItem('opengauss-routes') + +function formatModules (_modules: any, result: RouteRecordNormalized[]) { + if (localeRoutes) { + const data = JSON.parse(localeRoutes) + const routes = filterAsyncRouter(data) + const deepRemoveChildIcon = (ary: any[]) => ary.map((val: any) => { + if (val.children) { + return { + ...val, + children: val.children.map((child: any) => { + return { + ...child, + meta: { + ...child.meta, + icon: null + } + } + }) + } + } else { + return val + } + }) + const routesRemovedChildIcon = deepRemoveChildIcon(routes || []) + return routesRemovedChildIcon + } else { + _modules.forEach((key: any) => { + result = result.concat(context(key).default) + }) + console.log(result) + return result + } +} + +export const appRoutes: RouteRecordNormalized[] = formatModules(modules, []) + +// Avoid circular references, put here + +export const DEFAULT_ROUTE_NAME = 'Dashboard' + +export const DEFAULT_ROUTE = { + title: i18n.global.t('components.login-form.5o62d201k380'), + name: DEFAULT_ROUTE_NAME, + fullPath: '/dashboard' +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/dashboard.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/dashboard.ts new file mode 100644 index 000000000..2e68a1fa2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/dashboard.ts @@ -0,0 +1,30 @@ +import { DEFAULT_LAYOUT } from '@/router/constants' +import { AppRouteRecordRaw } from '../types' + +const DASHBOARD: AppRouteRecordRaw = { + path: '/', + name: 'Home', + component: DEFAULT_LAYOUT, + redirect: '/dashboard', + meta: { + title: '首页', + requiresAuth: false, + icon: 'home', + order: 0, + hideChildrenInMenu: true + }, + children: [ + { + path: '/dashboard', + name: 'Dashboard', + component: () => import('@/views/ops/home/Index.vue'), + meta: { + title: '首页', + requiresAuth: true, + roles: ['*'] + } + } + ] +} + +export default DASHBOARD diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/logs.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/logs.ts new file mode 100644 index 000000000..87f036cc7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/logs.ts @@ -0,0 +1,40 @@ +import { DEFAULT_LAYOUT } from '@/router/constants' +import { AppRouteRecordRaw } from '../types' + +const SysLog: AppRouteRecordRaw = { + path: '/logs', + name: 'Logs', + component: DEFAULT_LAYOUT, + redirect: '/logs/sys-log', + meta: { + title: '日志管理', + requiresAuth: false, + icon: 'home', + order: 0, + hideChildrenInMenu: true + }, + children: [ + { + path: '/logs/sys-log', + name: 'SysLog', + component: () => import('@/views/logs/sys-log/index.vue'), + meta: { + title: '系统日志', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/logs/oper-log', + name: 'OperLog', + component: () => import('@/views/logs/oper-log/index.vue'), + meta: { + title: '操作日志', + requiresAuth: true, + roles: ['*'] + } + } + ] +} + +export default SysLog diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/plugin.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/plugin.ts new file mode 100644 index 000000000..d8a9f4c90 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/plugin.ts @@ -0,0 +1,31 @@ +import { DEFAULT_LAYOUT } from '@/router/constants' +import { AppRouteRecordRaw } from '../types' + +const PLUGIN: AppRouteRecordRaw = { + path: '/plugin', + name: 'plugin', + component: DEFAULT_LAYOUT, + redirect: '/plugin/manage', + meta: { + title: '插件功能', + requiresAuth: false, + icon: 'icon-apps', + order: 5, + hideChildrenInMenu: true, + activeMenu: 'plugin' + }, + children: [ + { + path: '/plugin/manage', + name: 'PluginManage', + component: () => import('@/views/plugin/manage/index.vue'), + meta: { + title: '插件管理', + requiresAuth: true, + roles: ['*'] + } + } + ] +} + +export default PLUGIN diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/resource.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/resource.ts new file mode 100644 index 000000000..bf2d68474 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/resource.ts @@ -0,0 +1,69 @@ +import { DEFAULT_LAYOUT } from '@/router/constants' +import { AppRouteRecordRaw } from '../types' + +const RESOURCE: AppRouteRecordRaw = { + path: '/resource', + name: 'resource', + component: DEFAULT_LAYOUT, + redirect: '/resource/database', + meta: { + title: '资源中心', + requiresAuth: false, + icon: 'icon-nav', + order: 4 + }, + children: [ + { + path: '/resource/database', + name: 'ResourceDatabase', + component: () => import('@/views/resource/database/index.vue'), + meta: { + title: '实例管理', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/resource/protocol', + name: 'ResourceProtocol', + component: () => import('@/views/resource/protocol/index.vue'), + meta: { + title: '协议资源', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/resource/physical', + name: 'ResourcePhysical', + component: () => import('@/views/resource/physical/index.vue'), + meta: { + title: '设备管理', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/resource/physical/createserver', + name: 'CreateServer', + component: () => import('@/views/resource/physical/components/CreateServerPage.vue'), + meta: { + title: '创建服务器', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/resource/az', + name: 'ResourceAz', + component: () => import('@/views/resource/az/index.vue'), + meta: { + title: 'AZ管理', + requiresAuth: true, + roles: ['*'] + } + } + ] +} + +export default RESOURCE diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/security.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/security.ts new file mode 100644 index 000000000..08da91b85 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/security.ts @@ -0,0 +1,60 @@ +import { DEFAULT_LAYOUT } from '@/router/constants' +import { AppRouteRecordRaw } from '../types' + +const SECURITY: AppRouteRecordRaw = { + path: '/security', + name: 'security', + component: DEFAULT_LAYOUT, + redirect: '/security/user', + meta: { + title: '安全中心', + requiresAuth: false, + icon: 'icon-settings', + order: 6 + }, + children: [ + { + path: '/security/user', + name: 'SecurityUser', + component: () => import('@/views/security/user/index.vue'), + meta: { + title: '账号与权限', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/security/role', + name: 'SecurityRole', + component: () => import('@/views/security/role/index.vue'), + meta: { + title: '角色管理', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/security/whitelist', + name: 'SecurityWhitelist', + component: () => import('@/views/security/whitelist/index.vue'), + meta: { + title: '访问白名单', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/security/usercenter', + name: 'SecurityUsercenter', + component: () => import('@/views/security/user/ucenter/index.vue'), + meta: { + title: '用户中心', + hideInMenu: true, + requiresAuth: true, + roles: ['*'] + } + } + ] +} + +export default SECURITY diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/task.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/task.ts new file mode 100644 index 000000000..227a5dc7e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/modules/task.ts @@ -0,0 +1,42 @@ +import { DEFAULT_LAYOUT } from '@/router/constants' +import { AppRouteRecordRaw } from '../types' + +const Task: AppRouteRecordRaw = { + path: '/task', + name: 'task', + component: DEFAULT_LAYOUT, + redirect: '/task/manage', + meta: { + title: '任务中心', + requiresAuth: false, + icon: 'icon-list', + order: 7, + hideChildrenInMenu: true, + activeMenu: 'task' + }, + children: [ + { + path: '/task/manage', + name: 'TaskManage', + component: () => import('@/views/task/manage/index.vue'), + meta: { + title: '任务中心', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/task/detail', + name: 'TaskDetail', + component: () => import('@/views/task/detail/index.vue'), + meta: { + title: '任务详情', + hideInMenu: true, + requiresAuth: true, + roles: ['*'] + } + } + ] +} + +export default Task diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/types.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/types.ts new file mode 100644 index 000000000..5903ce22c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/routes/types.ts @@ -0,0 +1,20 @@ +import { defineComponent } from 'vue' +import type { RouteMeta, NavigationGuard } from 'vue-router' + +export type Component = + | ReturnType + | (() => Promise) + | (() => Promise); + +export interface AppRouteRecordRaw { + path: string; + name?: string | symbol; + meta?: RouteMeta; + redirect?: string; + component: Component | string; + children?: AppRouteRecordRaw[]; + alias?: string | string[]; + props?: Record; + beforeEnter?: NavigationGuard | NavigationGuard[]; + fullPath?: string; +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/router/typings.d.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/router/typings.d.ts new file mode 100644 index 000000000..9b07a09ae --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/router/typings.d.ts @@ -0,0 +1,16 @@ +import 'vue-router' + +declare module 'vue-router' { + interface RouteMeta { + roles?: string[]; // Controls roles that have access to the page + requiresAuth: boolean; // Whether login is required to access the current page (every route must declare) + icon?: string; // The icon show in the side menu + title?: string; // The locale name show in side menu and breadcrumb + hideInMenu?: boolean; // If true, it is not displayed in the side menu + hideChildrenInMenu?: boolean; // if set true, the children are not displayed in the side menu + activeMenu?: string; // if set name, the menu will be highlighted according to the name you set + order?: number; // Sort routing menu items. If set key, the higher the value, the more forward it is + noAffix?: boolean; // if set true, the tag will not affix in the tab-bar + ignoreCache?: boolean; // if set true, the page will not be cached + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/shims-vue.d.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/shims-vue.d.ts new file mode 100644 index 000000000..75c3b046e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/shims-vue.d.ts @@ -0,0 +1,19 @@ +/* eslint-disable */ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} + +declare module 'jsencrypt/bin/jsencrypt.min' + +declare module '*.svg' +declare module '*.png' +declare module '*.jpg' +declare module '*.jpeg' +declare module '*.gif' +declare module '*.bmp' +declare module '*.tiff' +declare module '*.json' + +declare module 'vue-grid-layout' diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/store/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/store/index.ts new file mode 100644 index 000000000..41de177f8 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/store/index.ts @@ -0,0 +1,10 @@ +import { createPinia } from 'pinia' +import useAppStore from './modules/app' +import useUserStore from './modules/user' +import useTabBarStore from './modules/tab-bar' +import useOpsStore from './modules/ops' + +const pinia = createPinia() + +export { useAppStore, useUserStore, useTabBarStore, useOpsStore } +export default pinia diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/app/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/app/index.ts new file mode 100644 index 000000000..212cc39a7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/app/index.ts @@ -0,0 +1,121 @@ +import { defineStore } from 'pinia' +import type { RouteRecordNormalized } from 'vue-router' +import defaultSettings from '@/config/settings.json' +import { getMenuList } from '@/api/user' +import { filterAsyncRouter } from '@/utils/route' +import { AppState } from './types' +import i18n from '@/locale/index' + +const useAppStore = defineStore('app', { + state: (): AppState => ({ ...defaultSettings }), + + getters: { + appCurrentSetting (state: AppState): AppState { + return { ...state } + }, + appDevice (state: AppState) { + return state.device + }, + appAsyncMenus (state: AppState): RouteRecordNormalized[] { + return state.serverMenu as unknown as RouteRecordNormalized[] + } + }, + + actions: { + // Update app settings + updateSettings (partial: Partial) { + // @ts-ignore-next-line + this.$patch(partial) + }, + // Change theme color + toggleTheme (dark: boolean) { + if (dark) { + this.theme = 'dark' + document.body.setAttribute('arco-theme', 'dark') + document.body.setAttribute('theme', 'dark') + localStorage.setItem('opengauss-theme', 'dark') + } else { + this.theme = 'light' + document.body.removeAttribute('arco-theme') + document.body.removeAttribute('theme') + localStorage.removeItem('opengauss-theme') + } + }, + toggleDevice (device: string) { + this.device = device + }, + toggleMenu (value: boolean) { + this.hideMenu = value + }, + async fetchServerMenuConfig () { + try { + const { data } = await getMenuList() + if(!data.some((item: any) => item && item.name === 'Static-pluginBase-opsOps')) { + let found = false; + for (let i = 0; i < data.length && !found; i++) { + if (data[i].name === 'Resource') { + if(data[i].children[3].name === 'ResourcePackage') { + data[i].children[3].meta.hideInMenu = true + found = true + } + } + } + } + data.unshift({ + path: '/', + name: 'Home', + component: 'DEFAULT_LAYOUT', + redirect: '/dashboard', + meta: { + title: i18n.global.t('components.login-form.5o62d201k380'), + requiresAuth: false, + icon: 'home', + order: 0, + hideChildrenInMenu: true + }, + children: [ + { + path: '/dashboard', + name: 'Dashboard', + component: 'ops/home/Index', + meta: { + title: i18n.global.t('components.login-form.5o62d201k380'), + requiresAuth: true, + roles: ['*'] + } + } + ] + }) + localStorage.setItem('opengauss-routes', JSON.stringify(data)) + const routes = filterAsyncRouter(data) + const deepRemoveChildIcon = (ary: any[]) => ary.map((val: any) => { + if (val.children) { + return { + ...val, + children: val.children.map((child: any) => { + return { + ...child, + meta: { + ...child.meta, + icon: null + } + } + }) + } + } else { + return val + } + }) + const routesRemovedChildIcon = deepRemoveChildIcon(routes || []) + this.serverMenu = routesRemovedChildIcon + } catch (error) { + console.log(error) + } + }, + clearServerMenu () { + this.serverMenu = [] + } + } +}) + +export default useAppStore diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/app/types.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/app/types.ts new file mode 100644 index 000000000..0da859a78 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/app/types.ts @@ -0,0 +1,18 @@ +import type { RouteRecordNormalized } from 'vue-router' + +export interface AppState { + theme: string; + colorWeak: boolean; + navbar: boolean; + menu: boolean; + hideMenu: boolean; + menuCollapse: boolean; + themeColor: string; + menuWidth: number; + globalSettings: boolean; + device: string; + tabBar: boolean; + menuFromServer: boolean; + serverMenu: RouteRecordNormalized[]; + [key: string]: unknown; +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/ops/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/ops/index.ts new file mode 100644 index 000000000..00ef1fd64 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/ops/index.ts @@ -0,0 +1,97 @@ +import { defineStore } from 'pinia' +import { MinimalistInstallConfig, LiteInstallConfig, EnterpriseInstallConfig, OpenGaussVersionEnum, InstallModeEnum, DeployTypeEnum, WsConnectTypeEnum } from '@/types/ops/install' +import { KeyValue } from '@antv/x6/lib/types' + +const installStore = defineStore('install', { + state: () => { + return { + installContext: { + installType: 'install', + openGaussVersion: OpenGaussVersionEnum.LITE, + openGaussVersionNum: '5.0.0', + installMode: InstallModeEnum.OFF_LINE, + packagePath: '', + packageName: '', + installPackagePath: '', + deployType: DeployTypeEnum.SINGLE_NODE, + clusterId: '', + clusterName: '' + }, + minimalistInstallConfig: {} as MinimalistInstallConfig, + liteInstallConfig: {} as LiteInstallConfig, + enterpriseInstallConfig: {} as EnterpriseInstallConfig + } + }, + + getters: { + getInstallConfig: state => state.installContext, + getMiniConfig: state => state.minimalistInstallConfig, + getLiteConfig: state => state.liteInstallConfig, + getEnterpriseConfig: state => state.enterpriseInstallConfig, + getInstallParam: (state) => { + const param = { + installContext: { + minimalistInstallConfig: {}, + liteInstallConfig: {}, + enterpriseInstallConfig: {} + }, + wsConnectType: WsConnectTypeEnum.COMMAND_EXEC, + businessId: 'installLog' + } + if (state.installContext.openGaussVersion === OpenGaussVersionEnum.MINIMAL_LIST) { + param.installContext.minimalistInstallConfig = state.minimalistInstallConfig + } else if (state.installContext.openGaussVersion === OpenGaussVersionEnum.LITE) { + param.installContext.liteInstallConfig = state.liteInstallConfig + } else { + param.installContext.enterpriseInstallConfig = state.enterpriseInstallConfig + } + Object.assign(param.installContext, state.installContext) + return param + } + }, + + actions: { + // Set installation data + setInstallContext (installInfo: KeyValue) { + this.$patch(state => { + Object.assign(state.installContext, installInfo) + }) + }, + // Set Minimalist Data + setMiniConfig (config: MinimalistInstallConfig) { + this.$patch(state => { + // The minimal version can only be used on one machine + const param = { + clusterName: config.clusterName + } + this.setInstallContext(param) + state.minimalistInstallConfig.port = config.port + state.minimalistInstallConfig.databaseUsername = config.databaseUsername + state.minimalistInstallConfig.databasePassword = config.databasePassword + state.minimalistInstallConfig.nodeConfigList = [] + state.minimalistInstallConfig.nodeConfigList = config.nodeConfigList + }) + }, + setLiteConfig (config: LiteInstallConfig) { + this.$patch(state => { + // The lightweight version can be installed on multiple machines. If it is the same machine, just choose the same host + state.liteInstallConfig.port = config.port + state.liteInstallConfig.databaseUsername = config.databaseUsername + state.liteInstallConfig.databasePassword = config.databasePassword + state.liteInstallConfig.nodeConfigList = [] + state.liteInstallConfig.nodeConfigList = config.nodeConfigList + }) + }, + setEnterpriseConfig (config: EnterpriseInstallConfig) { + this.$patch(state => { + // The lightweight version can be installed on multiple machines. If it is the same machine, just choose the same host + Object.assign(state.enterpriseInstallConfig, config) + }) + }, + resetInstall () { + this.$reset() + } + } +}) + +export default installStore diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/tab-bar/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/tab-bar/index.ts new file mode 100644 index 000000000..29f794210 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/tab-bar/index.ts @@ -0,0 +1,72 @@ +import type { RouteLocationNormalized } from 'vue-router' +import { defineStore } from 'pinia' +import { DEFAULT_ROUTE, DEFAULT_ROUTE_NAME } from '@/router/routes/index' +import { TabBarState, TagProps } from './types' + +const formatTag = (route: RouteLocationNormalized): TagProps => { + const { name, meta, fullPath, query } = route + return { + title: meta.title || '', + name: String(name), + fullPath, + query, + ignoreCache: meta.ignoreCache + } +} + +const useAppStore = defineStore('tabBar', { + state: (): TabBarState => ({ + cacheTabList: new Set([DEFAULT_ROUTE_NAME]), + tagList: [DEFAULT_ROUTE] + }), + + getters: { + getTabList (): TagProps[] { + return this.tagList + }, + getCacheList (): string[] { + return Array.from(this.cacheTabList) + } + }, + + actions: { + updateTabList (route: RouteLocationNormalized) { + this.tagList.push(formatTag(route)) + if (!route.meta.ignoreCache) { + this.cacheTabList.add(route.name as string) + } + }, + deleteTag (idx: number, tag: TagProps) { + this.tagList.splice(idx, 1) + this.cacheTabList.delete(tag.name) + }, + deleteTags (tag: TagProps) { + const idx = this.tagList.findIndex(item => item.name === tag.name) + this.tagList.splice(idx, 1) + this.cacheTabList.delete(tag.name) + }, + deleteTagByFullPath (tag: TagProps) { + const idx = this.tagList.findIndex(item => item.fullPath === tag.fullPath) + if (idx !== -1) { + this.tagList.splice(idx, 1) + this.cacheTabList.delete(tag.name) + } + }, + freshTabList (tags: TagProps[]) { + this.tagList = tags + this.cacheTabList.clear() + // To judge ignoreCache first + this.tagList + .filter((el) => !el.ignoreCache) + .map((el) => el.name) + .forEach((x) => this.cacheTabList.add(x)) + }, + resetTabList () { + this.tagList = [DEFAULT_ROUTE] + this.cacheTabList.clear() + this.cacheTabList.add(DEFAULT_ROUTE_NAME) + } + } +}) + +export default useAppStore diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/tab-bar/types.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/tab-bar/types.ts new file mode 100644 index 000000000..29c1c530e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/tab-bar/types.ts @@ -0,0 +1,12 @@ +export interface TagProps { + title: string; + name: string; + fullPath: string; + query?: any; + ignoreCache?: boolean; +} + +export interface TabBarState { + tagList: TagProps[]; + cacheTabList: Set; +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/user/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/user/index.ts new file mode 100644 index 000000000..532718706 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/store/modules/user/index.ts @@ -0,0 +1,77 @@ +import { defineStore } from 'pinia' +import { + login as userLogin, + logout as userLogout, + getUserInfo, + LoginData +} from '@/api/user' +import {setToken, clearToken, clearPublckey} from '@/utils/auth' +import { removeRouteListener } from '@/utils/route-listener' +import useAppStore from '../app' + +const useUserStore = defineStore('user', { + state: (): any => ({ + userName: '', + nickName: '', + userId: undefined, + phonenumber: '', + email: undefined, + remark: undefined, + avatar: undefined + }), + + getters: { + userInfo (state: any): any { + return { ...state } + } + }, + + actions: { + // Set user's information + setInfo (partial: any) { + this.$patch(partial) + }, + + // Reset user's information + resetInfo () { + this.$reset() + }, + + // Get user's information + async info () { + const res = await getUserInfo() + + this.setInfo(res.data) + }, + + // Login + async login (loginForm: LoginData) { + try { + const res: any = await userLogin(loginForm) + setToken(res.token) + } catch (err) { + clearPublckey() + clearToken() + throw err + } + }, + logoutCallBack () { + const appStore = useAppStore() + this.resetInfo() + clearPublckey() + clearToken() + removeRouteListener() + appStore.clearServerMenu() + }, + // Logout + async logout () { + try { + await userLogout() + } finally { + this.logoutCallBack() + } + } + } +}) + +export default useUserStore diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/types/echarts.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/types/echarts.ts new file mode 100644 index 000000000..e72dbaad0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/types/echarts.ts @@ -0,0 +1,10 @@ +import { CallbackDataParams } from 'echarts/types/dist/shared' + +export interface ToolTipFormatterParams extends CallbackDataParams { + axisDim: string; + axisIndex: number; + axisType: string; + axisId: string; + axisValue: string; + axisValueLabel: string; +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/types/global.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/types/global.ts new file mode 100644 index 000000000..ff7140127 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/types/global.ts @@ -0,0 +1,46 @@ +export interface KeyValue { + [key: string]: any +} + +export interface AnyObject { + [key: string]: unknown; +} + +export interface Options { + value: unknown; + label: string; +} + +export interface NodeOptions extends Options { + children?: NodeOptions[]; +} + +export interface GetParams { + body: null; + type: string; + url: string; +} + +export interface PostData { + body: string; + type: string; + url: string; +} + +export interface Pagination { + current: number; + pageSize: number; + total?: number; +} + +export type TimeRanger = [string, string]; + +export interface GeneralChart { + xAxis: string[]; + data: Array<{ name: string; value: number[] }>; +} + +export const IpRegex = { + ipv4Reg: /^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[0-9])\.((1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.){2}(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$/, + ipv6Reg: /^([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,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}$/ +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/types/mock.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/types/mock.ts new file mode 100644 index 000000000..9219ef450 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/types/mock.ts @@ -0,0 +1,5 @@ +export interface MockParams { + url: string; + type: string; + body: string; +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/types/operlog.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/types/operlog.ts new file mode 100644 index 000000000..f5c97e2f0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/types/operlog.ts @@ -0,0 +1,16 @@ +export enum OperStatus { + SUCCESS, + ERROR +} + +export enum BusinessType { + OTHER, + INSERT, + UPDATE, + DELETE, + GRANT, + START, + STOP, + INSTALL, + UNINSTALL +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/types/ops/install.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/types/ops/install.ts new file mode 100644 index 000000000..02b34fb62 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/types/ops/install.ts @@ -0,0 +1,142 @@ +export enum OpenGaussVersionEnum { + ENTERPRISE = 'ENTERPRISE', + MINIMAL_LIST = 'MINIMAL_LIST', + LITE = 'LITE' +} + +export enum InstallModeEnum { + ON_LINE = 'ON_LINE', + OFF_LINE = 'OFF_LINE' +} + +export enum DeployTypeEnum { + CLUSTER = 'CLUSTER', + SINGLE_NODE = 'SINGLE_NODE' +} + +export enum ClusterRoleEnum { + MASTER = 'MASTER', + SLAVE = 'SLAVE', + CASCADE = 'CASCADE' +} + +export enum WsConnectTypeEnum { + DOWNLOAD_INSTALL_PACKAGE = 'DOWNLOAD_INSTALL_PACKAGE', + SSH = 'SSH', + COMMAND_EXEC = 'COMMAND_EXEC' +} + +// Install step object +export interface InstallContext { + // selected version + openGaussVersion?: OpenGaussVersionEnum; + // openGauss version number + openGaussVersionNum?: string; + // The chosen installation method + installMode?: InstallModeEnum; + // During offline installation, the selected installation package path + installPackagePath?: string; + // Deployment method + deployType: DeployTypeEnum; + + clusterId: string; + + clusterName: string; +} + +// Minimalist object +export interface MiniNodeConfig { + clusterRole: string; + hostId: string; + privateIp: string; + publicIp: string; + installUserId: string; + installPath: string; + dataPath: string; + isInstallDemoDatabase: boolean; +} + +export interface MinimalistInstallConfig { + clusterName: string; + port: number; + databaseUsername: string; + databasePassword: string; + nodeConfigList: MiniNodeConfig[] +} + +// lightweight object +export interface LiteNodeConfig { + clusterRole: string; + hostId: string; + privateIp: string; + publicIp: string; + installUserId: string; + installPath: string; + dataPath: string; + port: number; + databasePassword: string; +} + +export interface LiteInstallConfig { + clusterName: string; + port: number; + databaseUsername: string; + databasePassword: string; + nodeConfigList: LiteNodeConfig[] +} + +export interface EnterpriseInstallNodeConfig { + clusterRole: string; + hostId: string; + privateIp: string; + publicIp: string; + hostname: string; + installUserId: string; + installUsername: string; + azId: string; + azName: string; + isInstallCM: boolean; + isCMMaster: boolean; + cmDataPath: string; + cmPort: number; + dataPath: string; + xlogPath: string; +} + +// Enterprise Edition Object +export interface EnterpriseInstallConfig { // eslint-disable-line + installPath: string; + logPath: string; + tmpPath: string; + omToolsPath: string; + corePath: string; + port: number; + enableDCF: boolean; + databaseUsername: string; + databasePassword: string; + azId: string; + azName: string; + nodeConfigList: EnterpriseInstallNodeConfig[]; +} + +// Download the package object +// Installation package download +export interface downloadPackage { + resourceUrl: string, + targetPath: string, + fileName: string, + connectType: string | 'DOWNLOAD_INSTALL_PACKAGE', + businessId: string | 'downloadPackage' +} + +export enum WsConnectType { + DOWNLOAD_INSTALL_PACKAGE = 'DOWNLOAD_INSTALL_PACKAGE', + SSH = 'SSH', + COMMAND_EXEC = 'COMMAND_EXEC' +} + +export interface SSHBody { + hostId: string, + wsConnectType: WsConnectType, + businessId: string +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/types/resource/host.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/types/resource/host.ts new file mode 100644 index 000000000..9b0e54817 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/types/resource/host.ts @@ -0,0 +1,14 @@ +// host information +export interface hostData { + publicIp: string, + privateIp: string, + password: string, + azId: string, + remark: string +} +// host user information +export interface hostUserData { + hostId: string, + username: string, + password: string +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/types/searchType.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/types/searchType.ts new file mode 100644 index 000000000..ab4616541 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/types/searchType.ts @@ -0,0 +1,5 @@ +export enum searchType { + SELECT = 'SELECT', + INPUT = 'INPUT', + MULTIPLESELECT = 'MULTIPLESELECT' +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/types/sysSetting.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/types/sysSetting.ts new file mode 100644 index 000000000..c946ebc4a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/types/sysSetting.ts @@ -0,0 +1,9 @@ +export interface SysSetting { + id: string, + userId: string, + uploadPath: string, + portalPkgDownloadUrl: string, + portalPkgName: string, + portalJarName: string, + serverHost: string +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/auth.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/auth.ts new file mode 100644 index 000000000..4aff77c7d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/auth.ts @@ -0,0 +1,31 @@ +const TOKEN_KEY = 'opengauss-token' + +const isLogin = () => { + return !!localStorage.getItem(TOKEN_KEY) +} + +const getToken = () => { + return localStorage.getItem(TOKEN_KEY) +} + +const setToken = (token: string) => { + localStorage.setItem(TOKEN_KEY, token) +} + +const clearToken = () => { + localStorage.removeItem(TOKEN_KEY) +} + +const getPublckey = () => { + return localStorage.getItem('publckey') +} + +const setPublckey = (Publckey: string) => { + localStorage.setItem('publckey', Publckey) +} + +const clearPublckey = () => { + localStorage.removeItem('publckey') +} + +export { isLogin, getToken, setToken, clearToken, getPublckey, setPublckey, clearPublckey } diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/debounce.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/debounce.ts new file mode 100644 index 000000000..830bdecf5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/debounce.ts @@ -0,0 +1,11 @@ +export const debounce = (fn: Function, delay = 360) => { + let timer: number | null = null; + return (...args: any[]) => { + if (timer) { + clearTimeout(timer); + } + timer = setTimeout(() => { + fn(...args); + }, delay); + } +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/env.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/env.ts new file mode 100644 index 000000000..f371dc5c6 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/env.ts @@ -0,0 +1,3 @@ +const debug = process.env.NODE_ENV !== 'production' + +export default debug diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/errorCode.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/errorCode.ts new file mode 100644 index 000000000..8a227b8ff --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/errorCode.ts @@ -0,0 +1,18 @@ +export default { + '400': 'errorCode.400', + '401': 'errorCode.401', + '403': 'errorCode.403', + '50101': 'errorCode.50101', + '50102': 'errorCode.50102', + '50103': 'errorCode.50103', + '50104': 'errorCode.50104', + '50105': 'errorCode.50105', + '50107': 'errorCode.50107', + '50108': 'errorCode.50108', + '50109': 'errorCode.50109', + '50110': 'errorCode.50110', + '50115': 'errorCode.50115', + '50116': 'errorCode.50116', + '50126': 'errorCode.50126', + 'default': 'errorCode.default' +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/event.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/event.ts new file mode 100644 index 000000000..64badb76a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/event.ts @@ -0,0 +1,27 @@ +export function addEventListen ( + target: Window | HTMLElement, + event: string, + handler: EventListenerOrEventListenerObject, + capture = false +) { + if ( + target.addEventListener && + typeof target.addEventListener === 'function' + ) { + target.addEventListener(event, handler, capture) + } +} + +export function removeEventListen ( + target: Window | HTMLElement, + event: string, + handler: EventListenerOrEventListenerObject, + capture = false +) { + if ( + target.removeEventListener && + typeof target.removeEventListener === 'function' + ) { + target.removeEventListener(event, handler, capture) + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/fetch.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/fetch.ts new file mode 100644 index 000000000..ecec33a43 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/fetch.ts @@ -0,0 +1,4 @@ +// Credentials carrying login status must be include +export default function fetch (url: any, options: any) { + return window.fetch(url, { ...options, credentials: 'omit' }) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/index.ts new file mode 100644 index 000000000..2131346bd --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/index.ts @@ -0,0 +1,25 @@ +type TargetContext = '_self' | '_parent' | '_blank' | '_top'; + +export const openWindow = ( + url: string, + opts?: { target?: TargetContext; [key: string]: any } +) => { + const { target = '_blank', ...others } = opts || {} + window.open( + url, + target, + Object.entries(others) + .reduce((preValue: string[], curValue) => { + const [key, value] = curValue + return [...preValue, `${key}=${value}`] + }, []) + .join(',') + ) +} + +export const regexUrl = new RegExp( + '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$', + 'i' +) + +export default null diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/jsencrypt.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/jsencrypt.ts new file mode 100644 index 000000000..88088ab6b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/jsencrypt.ts @@ -0,0 +1,55 @@ +import JSEncrypt from 'jsencrypt/bin/jsencrypt.min' +import {getEntryKey, getLoginKey} from '@/api/ops' +import { KeyValue } from '@/types/global' +import {getPublckey, setPublckey} from "@/utils/auth"; + +export async function encrypt(txt: any) { + let publicKey = '' + const getPublicKey: KeyValue = await getLoginKey() + if (Number(getPublicKey.code) === 200 && getPublicKey.msg) { + const newKey = getPublicKey.msg + publicKey = newKey + setPublckey(newKey) + } + const encryptor = new JSEncrypt() + encryptor.setPublicKey(publicKey) // set public key + return encryptor.encrypt(txt) // encrypt data +} + +export function encryptWithPublicKey(txt: any) { + const publicKey = getPublckey() + const encryptor = new JSEncrypt() + encryptor.setPublicKey(publicKey) + const result = encryptor.encrypt(txt) + if (result === false) { + console.error("Encryption failed. Last error:", encryptor.lastError); + } + return result +} + +const isEncryptedData = (data: string | null) => { + if (!data) return false; + // Check Base64 format (encrypted data is usually Base64 encoded) + const base64Check = /^(?=.*[+/=])[A-Za-z0-9+/=]+$/.test(data) && + data.length % 4 === 0 && + /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(data) + return base64Check; +} + +// host password encryption +export async function encryptPassword(pwd: string) { + const isEncryptTxt = isEncryptedData(pwd); + if (isEncryptTxt) { + return pwd; + } + let publicKey = '' + const getPublicKey: KeyValue = await getEntryKey() + if (Number(getPublicKey.code) === 200 && getPublicKey.key) { + const newKey = getPublicKey.key + publicKey = newKey + } + const encryptor = new JSEncrypt() + encryptor.setPublicKey(publicKey) + return encryptor.encrypt(pwd) +} + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/monitor.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/monitor.ts new file mode 100644 index 000000000..9677d80ed --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/monitor.ts @@ -0,0 +1,20 @@ +import { App, ComponentPublicInstance } from 'vue' +import axios from 'axios' + +export default function handleError (Vue: App, baseUrl: string) { + if (!baseUrl) { + return + } + Vue.config.errorHandler = ( + err: unknown, + instance: ComponentPublicInstance | null, + info: string + ) => { + // send error info + axios.post(`${baseUrl}/report-error`, { + err, + instance, + info + }) + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/pluginApp.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/pluginApp.ts new file mode 100644 index 000000000..6cbd63a22 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/pluginApp.ts @@ -0,0 +1,24 @@ +import WujieVue from 'wujie-vue3' +import credentialsFetch from './fetch' +import { DocElementRectPlugin } from "wujie-polyfill"; + +const { setupApp, destroyApp } = WujieVue +const degrade = window.localStorage.getItem('degrade') === 'true' || !window.Proxy || !window.CustomElementRegistry + +export function createPluginApp (name: string, url = '/') { + const actualUrl = process.env.NODE_ENV === 'development' ? `//localhost:8081${url}` : url + setupApp({ + name, + url: actualUrl, + attrs: {}, + exec: true, + fetch: credentialsFetch, + alive: true, + plugins: [DocElementRectPlugin()], + degrade + }) +} + +export function destroyPluginApp (name: string) { + destroyApp(name) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/route-listener.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/route-listener.ts new file mode 100644 index 000000000..ff42ed714 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/route-listener.ts @@ -0,0 +1,30 @@ +/** + * Listening to routes alone would waste rendering performance. Use the publish-subscribe model for distribution management + */ +import mitt, { Handler } from 'mitt' +import type { RouteLocationNormalized } from 'vue-router' + +const emitter = mitt() + +const key = Symbol('ROUTE_CHANGE') + +let latestRoute: RouteLocationNormalized + +export function setRouteEmitter (to: RouteLocationNormalized) { + emitter.emit(key, to) + latestRoute = to +} + +export function listenerRouteChange ( + handler: (route: RouteLocationNormalized) => void, + immediate = true +) { + emitter.on(key, handler as Handler) + if (immediate && latestRoute) { + handler(latestRoute) + } +} + +export function removeRouteListener () { + emitter.off(key) +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/route.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/route.ts new file mode 100644 index 000000000..a3eb7eb21 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/route.ts @@ -0,0 +1,70 @@ +import { DEFAULT_LAYOUT, ParentView } from '@/router/constants' + +const loadView = (view: string) => { // Routing lazy loading + return () => import( + /* webpackExclude: /\.d.ts$/ */ + `@/views/${view}` + ) +} + +function filterChildren (childrenMap: any[], lastRouter: any = false) { + let children: any[] = [] + childrenMap.forEach((el) => { + if (el.children && el.children.length) { + if (el.component === 'ParentView') { + el.children.forEach((c: any) => { + c.path = el.path + '/' + c.path + if (c.children && c.children.length) { + children = children.concat(filterChildren(c.children, c)) + return + } + children.push(c) + }) + return + } + } + if (lastRouter) { + el.path = lastRouter.path + '/' + el.path + } + children = children.concat(el) + }) + return children +} + +// Traverse the routing string from the background and convert it to a component object +export function filterAsyncRouter (asyncRouterMap: any[], lastRouter = false, type = false) { // eslint-disable-line + return asyncRouterMap.filter(route => { + if (type && route.children) { + route.children = filterChildren(route.children) + } + if (route.component) { + // Layout Component Special Handling + if (route.component === 'DEFAULT_LAYOUT') { + route.component = DEFAULT_LAYOUT + } else if (route.component === 'ParentView') { + route.component = ParentView + } else { + route.component = loadView(route.component) + } + } + if (route.children != null && route.children && route.children.length) { + route.children = filterAsyncRouter(route.children, route, type) + } else { + delete route['children'] + delete route['redirect'] + } + return true + }) +} + +export function getQuery (url: string) { + const obj: any = {} + const i = url.indexOf('?') + 1 + const str = url.substr(i) + const arr = str.split('&') + for (let i = 0; i < arr.length; i++) { + const arr1 = arr[i].split('=') + obj[arr1[0]] = arr1[1] + } + return obj +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/showMessage.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/showMessage.ts new file mode 100644 index 000000000..2539167be --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/showMessage.ts @@ -0,0 +1,36 @@ +import { ElMessage } from 'element-plus' +import { + IconSuccess, + IconAlarm, + IconError, + IconRemind +} from '@computing/opendesign-icons' + +interface IconMap { + info: any; + success: any; + warning: any; + error: any; +} + +type Status = keyof IconMap; + +const icons = { + info: IconRemind, + success: IconSuccess, + warning: IconAlarm, + error: IconError +} + +const showMessage = (type: Status, msg = '', duration?: number): void => { + ElMessage({ + type, + showClose: true, + message: msg, + icon: icons[type], + customClass: `o-message--${type}`, + duration: duration ?? 3000 + }) +} + +export default showMessage diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/user.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/user.ts new file mode 100644 index 000000000..24b763f92 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/user.ts @@ -0,0 +1,9 @@ +const isAdmin = (user: any) => { + return user && +user.userId === 1 +} + +const isDefaultPwd = (user: any) => { + return user && user.updatePwd === '0' +} + +export { isAdmin, isDefaultPwd } diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/utils/websocket.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/websocket.ts new file mode 100644 index 000000000..90c9b07b5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/utils/websocket.ts @@ -0,0 +1,184 @@ +export type Callback = (e: Event) => void +export type MessageCallback = (e: RT) => void + +interface Ioptions { + url: string | null // The address of the linked channel + heartTime?: number // heartbeat interval + heartMsg?: string // Heartbeat information, the default is 'ping' + isReconnect?: boolean // Whether to automatically reconnect + isRestory?: boolean // whether to destroy + reconnectTime?: number // reconnection interval + reconnectCount?: number // The number of reconnections -1 is unlimited + openCb?: Callback // callback for successful connection + closeCb?: Callback // Close callback + messageCb?: MessageCallback // message callback + errorCb?: Callback // error callback +} + +/** + * heartbeat base class + */ + +export class Heart { + heartTimeOut!: number // heartbeat timer + ServerHeartTimeOut!: number // heartbeat timer + timeout = 5000 + // reset + reset (): void { + clearTimeout(this.heartTimeOut) + clearTimeout(this.ServerHeartTimeOut) + } + + /** + * start heartbeat + * @param {Function} cb + */ + start (cb: Callback): void { + this.heartTimeOut = setTimeout((e: Event) => { + cb(e) + this.ServerHeartTimeOut = setTimeout((e: Event) => { + cb(e) + // restart detection + this.reset() + this.start(cb) + }, this.timeout) + }, this.timeout) + } +} + +export default class Socket extends Heart { + ws!: WebSocket + + reconnectTimer = 0 // reconnect timer + reconnectCount = 10 // Variables are saved to prevent loss + + options: Ioptions = { + url: null, // The address of the linked channel + heartTime: 5000, // heartbeat interval + heartMsg: 'ping', // Heartbeat information, the default is 'ping' + isReconnect: true, // Whether to automatically reconnect + isRestory: false, // whether to destroy + reconnectTime: 5000, // reconnection interval + reconnectCount: 5, // The number of reconnections -1 is unlimited + openCb: (e: Event) => { + console.log('callback for successful connection::::', e) + }, // callback for successful connection + closeCb: (e: Event) => { + console.log('Close callback::::', e) + }, // Close callback + messageCb: (e: RT) => { + console.log('message callback::::', e) + }, // message callback + errorCb: (e: Event) => { + console.log('error callback::::', e) + } // error callback + } + + constructor (ops: Ioptions) { + super() + Object.assign(this.options, ops) + this.create() + } + + /** + * establish connection + */ + create (): void { + if (!('WebSocket' in window)) { + throw new Error('The current browser does not support it and cannot be used') + } + if (!this.options.url) { + throw new Error('The address does not exist, and the channel cannot be established') + } + // get location host + console.log('get locaion host: ', window.location.host) + let wsUrl + if (process.env.NODE_ENV === 'development') { + wsUrl = `wss://${process.env.VUE_APP_WS_BASE_URL}/websocket/${this.options.url}` + } else { + const wsPrefix = window.location.protocol.includes('https') ? 'wss' : 'ws' + wsUrl = `${wsPrefix}://${window.location.host}/websocket/${this.options.url}` + } + this.ws = new WebSocket(wsUrl) + this.onopen(this.options.openCb as Callback) + this.onclose(this.options.closeCb as Callback) + this.onmessage(this.options.messageCb as MessageCallback) + } + + /** + * Custom connection success event + * If the callback exists, call the callback, if there is no callback in OPTIONS + * @param {Function} callback + */ + onopen (callback: Callback): void { + this.ws.onopen = event => { + if (typeof callback === 'function') { + callback(event) + } else { + typeof this.options.openCb === 'function' && this.options.openCb(event) + } + } + } + + /** + * custom close event + * If the callback exists, call the callback, if there is no callback in OPTIONS + * @param {Function} callback + */ + onclose (callback: Callback): void { + this.ws.onclose = event => { + if (typeof callback === 'function') { + callback(event) + } else { + typeof this.options.closeCb === 'function' && this.options.closeCb(event) + } + } + } + + /** + * custom error event + * If the callback exists, call the callback, if there is no callback in OPTIONS + * @param {Function} callback + */ + onerror (callback: Callback): void { + this.ws.onerror = event => { + if (typeof callback === 'function') { + callback(event) + } else { + typeof this.options.errorCb === 'function' && this.options.errorCb(event) + } + } + } + + /** + * Custom message monitoring event + * If the callback exists, call the callback, if there is no callback in OPTIONS + * @param {Function} callback + */ + onmessage (callback: MessageCallback): void { + this.ws.onmessage = (event: MessageEvent) => { + const strMessage = event.data + if (typeof callback === 'function') { + callback(strMessage) + } + } + } + + /** + * Custom send message event + * @param {String} data text sent + */ + send (data: T | string): void { + console.log('doNothing', data) + } + + /** + * destroy + */ + destroy (): void { + super.reset() + clearTimeout(this.reconnectTimer) // clear reconnection timer + this.options.isRestory = true + this.ws.close() + } +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/iframe/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/iframe/index.vue new file mode 100644 index 000000000..00cefa4a0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/iframe/index.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/login/components/UpdateDefaultCode.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/login/components/UpdateDefaultCode.vue new file mode 100644 index 000000000..fadb3e7d3 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/login/components/UpdateDefaultCode.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/login/components/login-form.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/login/components/login-form.vue new file mode 100644 index 000000000..50e445424 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/login/components/login-form.vue @@ -0,0 +1,214 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/login/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/login/index.vue new file mode 100644 index 000000000..0cedcd459 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/login/index.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/logs/oper-log/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/logs/oper-log/index.vue new file mode 100644 index 000000000..cf4d7fc2c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/logs/oper-log/index.vue @@ -0,0 +1,231 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/logs/sys-log/components/EditConfig.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/logs/sys-log/components/EditConfig.vue new file mode 100644 index 000000000..13fe1410b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/logs/sys-log/components/EditConfig.vue @@ -0,0 +1,132 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/logs/sys-log/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/logs/sys-log/index.vue new file mode 100644 index 000000000..0dfe5615d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/logs/sys-log/index.vue @@ -0,0 +1,199 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/not-found/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/not-found/index.vue new file mode 100644 index 000000000..bb7522bdd --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/not-found/index.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/Index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/Index.vue new file mode 100644 index 000000000..9878351f1 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/Index.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/BusiFlowSummary.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/BusiFlowSummary.vue new file mode 100644 index 000000000..969ceb027 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/BusiFlowSummary.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/ClusterList.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/ClusterList.vue new file mode 100644 index 000000000..9c0365fac --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/ClusterList.vue @@ -0,0 +1,833 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/ClusterTop.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/ClusterTop.vue new file mode 100644 index 000000000..85d41b9db --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/ClusterTop.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/CommonOper.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/CommonOper.vue new file mode 100644 index 000000000..24b09a402 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/CommonOper.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/ModuleSummary.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/ModuleSummary.vue new file mode 100644 index 000000000..39731352c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/ModuleSummary.vue @@ -0,0 +1,246 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/OneCheck.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/OneCheck.vue new file mode 100644 index 000000000..82bcc1c27 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/OneCheck.vue @@ -0,0 +1,513 @@ + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/SystemAlarm.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/SystemAlarm.vue new file mode 100644 index 000000000..31488a658 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/SystemAlarm.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/echarts/BusiFlowSummary.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/echarts/BusiFlowSummary.vue new file mode 100644 index 000000000..18767d1b2 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/echarts/BusiFlowSummary.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/echarts/DataSourceSummary.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/echarts/DataSourceSummary.vue new file mode 100644 index 000000000..04c34a682 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/echarts/DataSourceSummary.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/echarts/option.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/echarts/option.ts new file mode 100644 index 000000000..75e1313ff --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/echarts/option.ts @@ -0,0 +1,214 @@ +import * as echarts from 'echarts/core' + +export const cpuOption = { + grid: { + x: 40, + y: 30, + y2: 10 + }, + xAxis: { + type: 'category', + data: [] + }, + yAxis: { + type: 'value', + name: '%' + }, + series: [ + { + data: [], + type: 'line', + smooth: true, + symbol: 'none', + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(195, 200, 251, 1)' + }, + { + offset: 1, + color: '#FFFFFF' + } + ]) + }, + itemStyle: { + normal: { + lineStyle: { + width: 1 + } + } + } + } + ] +} + +export const memoryOption = { + grid: { + x: 40, + y: 30, + y2: 10 + }, + xAxis: { + type: 'category', + data: [] + }, + yAxis: { + type: 'value', + name: '%' + }, + series: [ + { + data: [], + type: 'line', + smooth: true, + symbol: 'none', + symbolSize: 4, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(155, 221, 166, 1)' + }, + { + offset: 1, + color: '#FFFFFF' + } + ]) + }, + itemStyle: { + normal: { + lineStyle: { + width: 1 + } + } + } + } + ] +} + +export const netOption = { + grid: { + x: 40, + y: 30, + y2: 10 + }, + xAxis: { + type: 'category', + data: [] + }, + yAxis: { + type: 'value', + name: 'G' + }, + series: [ + { + data: [], + type: 'line', + smooth: true, + symbol: 'none', + symbolSize: 4, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(226, 232, 249, 1)' + }, + { + offset: 1, + color: '#FFFFFF' + } + ]) + }, + itemStyle: { + normal: { + lineStyle: { + width: 1 + } + } + } + }, + { + data: [], + type: 'line', + smooth: true, + symbol: 'none', + symbolSize: 4, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(249, 236, 231, 1)' + }, + { + offset: 1, + color: '#FFFFFF' + } + ]) + }, + itemStyle: { + normal: { + lineStyle: { + width: 1 + } + } + } + } + ] +} + +export const connectOption = { + grid: { + x: 40, + y: 30, + y2: 10 + }, + xAxis: { + type: 'category', + data: [] + }, + yAxis: { + type: 'value', + name: 'pcs' + }, + series: [ + { + data: [], + type: 'line', + smooth: true, + symbol: 'none', + symbolSize: 4, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(157, 207, 251, 1)' + }, + { + offset: 1, + color: '#FFFFFF' + } + ]) + }, + itemStyle: { + normal: { + lineStyle: { + width: 1 + } + } + } + } + ] +} + +export const clearData = () => { + cpuOption.xAxis.data = [] + cpuOption.series[0].data = [] + memoryOption.xAxis.data = [] + memoryOption.series[0].data = [] + connectOption.xAxis.data = [] + connectOption.series[0].data = [] + netOption.xAxis.data = [] + netOption.series[0].data = [] + netOption.series[1].data = [] +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/oneCheck.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/oneCheck.ts new file mode 100644 index 000000000..7379b2942 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/ops/home/components/oneCheck.ts @@ -0,0 +1,7 @@ +export const oneCheck = { + cluster: ['CheckClusterState', 'CheckDBParams', 'CheckDebugSwitch', 'CheckDirPermissions', 'CheckEnvProfile', 'CheckReadonlyMode', 'CheckDilateSysTab', 'CheckProStartTime', 'CheckMpprcFile'], + db: ['CheckCurConnCount', 'CheckCursorNum', 'CheckTableSpace', 'CheckSysadminUser', 'CheckHashIndex', 'CheckPgxcRedistb', 'CheckNodeGroupName', 'CheckTDDate', 'CheckPgxcgroup'], + os: ['CheckEncoding', 'CheckFirewall', 'CheckKernelVer', 'CheckMaxHandle', 'CheckNTPD', 'CheckOSVer', 'CheckSysParams', 'CheckTHP', 'CheckTimeZone', 'CheckCPU', 'CheckSshdService', 'CheckSshdConfig', 'CheckCrondService', 'CheckStack', 'CheckSysPortRange', 'CheckMemInfo', 'CheckHyperThread', 'CheckMaxProcMemory', 'CheckBootItems', 'CheckKeyProAdj', 'CheckFilehandle', 'CheckDropCache'], + device: ['CheckBlockdev', 'CheckDiskFormat', 'CheckSpaceUsage', 'CheckInodeUsage', 'CheckSwapMemory', 'CheckLogicalBlock', 'CheckIOrequestqueue', 'CheckMaxAsyIOrequests', 'CheckIOConfigure'], + network: ['CheckMTU', 'CheckPing', 'CheckRXTX', 'CheckNetWorkDrop', 'CheckMultiQueue', 'CheckRouting', 'CheckNICModel'] +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/plugin/manage/components/PluginConfig.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/plugin/manage/components/PluginConfig.vue new file mode 100644 index 000000000..7daa127f7 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/plugin/manage/components/PluginConfig.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/plugin/manage/components/PluginInstall.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/plugin/manage/components/PluginInstall.vue new file mode 100644 index 000000000..9a6d05d4e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/plugin/manage/components/PluginInstall.vue @@ -0,0 +1,248 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/plugin/manage/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/plugin/manage/index.vue new file mode 100644 index 000000000..0d304df6f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/plugin/manage/index.vue @@ -0,0 +1,682 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/az/components/AddAz.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/az/components/AddAz.vue new file mode 100644 index 000000000..e1562ca60 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/az/components/AddAz.vue @@ -0,0 +1,167 @@ + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/az/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/az/index.vue new file mode 100644 index 000000000..93228c0d9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/az/index.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/AddJdbc.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/AddJdbc.vue new file mode 100644 index 000000000..1719a4e89 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/AddJdbc.vue @@ -0,0 +1,483 @@ + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/HostImportDlg.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/HostImportDlg.vue new file mode 100644 index 000000000..41c31a53f --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/HostImportDlg.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/HostPwdDlg.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/HostPwdDlg.vue new file mode 100644 index 000000000..e31cdacce --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/HostPwdDlg.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/HostTerminal.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/HostTerminal.vue new file mode 100644 index 000000000..e1ba0b38b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/HostTerminal.vue @@ -0,0 +1,155 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/JdbcInstance.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/JdbcInstance.vue new file mode 100644 index 000000000..c8a72740e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/JdbcInstance.vue @@ -0,0 +1,245 @@ + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/JdbcNodeTable.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/JdbcNodeTable.vue new file mode 100644 index 000000000..d53daa872 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/JdbcNodeTable.vue @@ -0,0 +1,281 @@ + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/index.vue new file mode 100644 index 000000000..ea10f356e --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/database/index.vue @@ -0,0 +1,323 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/package/AddPack.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/package/AddPack.vue new file mode 100644 index 000000000..68f63c587 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/package/AddPack.vue @@ -0,0 +1,657 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/package/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/package/index.vue new file mode 100644 index 000000000..70a22ad74 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/package/index.vue @@ -0,0 +1,671 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/package/option.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/package/option.ts new file mode 100644 index 000000000..cd5f2cc57 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/package/option.ts @@ -0,0 +1,92 @@ +export const cpuOption = { + tooltip: { + trigger: 'item', + formatter: '{b}: {d}%' + }, + color: ['#868F9C', '#E41D1D'], + series: [ + { + type: 'pie', + radius: '50%', + avoidLabelOverlap: false, + label: { + show: false, + position: 'center' + }, + emphasis: { + label: { + show: true + } + }, + labelLine: { + show: false + }, + data: [ + { value: 0, name: 'used', unit: '%' }, + { value: 0, name: 'unused', unit: '%' } + ] + } + ] +} + +export const memoryOption = { + tooltip: { + trigger: 'item', + formatter: '{b}: {d}%' + }, + color: ['#868F9C', '#91cc75'], + series: [ + { + type: 'pie', + radius: '50%', + avoidLabelOverlap: false, + label: { + show: false, + position: 'center' + }, + emphasis: { + label: { + show: true + } + }, + labelLine: { + show: false + }, + data: [ + { value: 0, name: 'used', unit: '%' }, + { value: 0, name: 'unused', unit: '%' } + ] + } + ] +} + +export const diskOption = { + tooltip: { + trigger: 'item', + formatter: '{b}: {d}%' + }, + color: ['#868F9C', '#91cc75'], + series: [ + { + type: 'pie', + radius: '50%', + avoidLabelOverlap: false, + label: { + show: false, + position: 'center' + }, + emphasis: { + label: { + show: true + } + }, + labelLine: { + show: false + }, + data: [ + { value: 0, name: 'used', unit: '%' }, + { value: 0, name: 'unused', unit: '%' } + ] + } + ] +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/AddAgent.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/AddAgent.vue new file mode 100644 index 000000000..fe2324785 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/AddAgent.vue @@ -0,0 +1,287 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/AddHost.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/AddHost.vue new file mode 100644 index 000000000..312cbe0d0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/AddHost.vue @@ -0,0 +1,352 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/AddHostUser.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/AddHostUser.vue new file mode 100644 index 000000000..0ec53bf00 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/AddHostUser.vue @@ -0,0 +1,212 @@ + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/AgentDrawer.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/AgentDrawer.vue new file mode 100644 index 000000000..518d5b5ee --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/AgentDrawer.vue @@ -0,0 +1,187 @@ + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/BatchLabelDlg.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/BatchLabelDlg.vue new file mode 100644 index 000000000..1a7a82d18 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/BatchLabelDlg.vue @@ -0,0 +1,158 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/BulkImport.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/BulkImport.vue new file mode 100644 index 000000000..e0afa2bf5 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/BulkImport.vue @@ -0,0 +1,704 @@ + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/ChooseTerminalUser.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/ChooseTerminalUser.vue new file mode 100644 index 000000000..2225cd5f9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/ChooseTerminalUser.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/CreateServerPage.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/CreateServerPage.vue new file mode 100644 index 000000000..4a555d55b --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/CreateServerPage.vue @@ -0,0 +1,666 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/HostPwdDlg.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/HostPwdDlg.vue new file mode 100644 index 000000000..56343c36d --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/HostPwdDlg.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/HostTerminal.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/HostTerminal.vue new file mode 100644 index 000000000..be133c999 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/HostTerminal.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/HostUserMng.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/HostUserMng.vue new file mode 100644 index 000000000..9cbda15f0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/HostUserMng.vue @@ -0,0 +1,232 @@ + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/ZipIcon.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/ZipIcon.vue new file mode 100644 index 000000000..3ea084b3a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/components/ZipIcon.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/echarts/option.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/echarts/option.ts new file mode 100644 index 000000000..c3d2dd0e9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/echarts/option.ts @@ -0,0 +1,92 @@ +export const cpuOption = { + tooltip: { + trigger: 'item', + formatter: '{b}: {d}%' + }, + color: ['#868F9C', '#E41D1D'], + series: [ + { + type: 'pie', + radius: '50%', + avoidLabelOverlap: false, + label: { + show: false, + position: 'center' + }, + emphasis: { + label: { + show: true + } + }, + labelLine: { + show: false + }, + data: [ + { value: 0, name: 'used', unit: '%' }, + { value: 0, name: 'unused', unit: '%' } + ] + } + ] +} + +export const memoryOption = { + tooltip: { + trigger: 'item', + formatter: '{b}: {d}%' + }, + color: ['#868F9C', '#91cc75'], + series: [ + { + type: 'pie', + radius: '50%', + avoidLabelOverlap: false, + label: { + show: false, + position: 'center' + }, + emphasis: { + label: { + show: true + } + }, + labelLine: { + show: false + }, + data: [ + { value: 0, name: 'used', unit: '%' }, + { value: 0, name: 'unused', unit: '%' } + ] + } + ] +} + +export const diskOption = { + tooltip: { + trigger: 'item', + formatter: '{b}: {d}%' + }, + color: ['#868F9C', '#91cc75'], + series: [ + { + type: 'pie', + radius: '50%', + avoidLabelOverlap: false, + label: { + show: false, + position: 'center' + }, + emphasis: { + label: { + show: true + } + }, + labelLine: { + show: false + }, + data: [ + { value: 0, name: 'used', unit: '%' }, + { value: 0, name: 'unused', unit: '%' } + ] + } + ] +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/index.vue new file mode 100644 index 000000000..a3f1ffb29 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/index.vue @@ -0,0 +1,754 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/label/LabelManageDlg.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/label/LabelManageDlg.vue new file mode 100644 index 000000000..b64cc6fae --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/label/LabelManageDlg.vue @@ -0,0 +1,351 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/physicalType/index.ts b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/physicalType/index.ts new file mode 100644 index 000000000..ccafe7f98 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/physical/physicalType/index.ts @@ -0,0 +1,5 @@ +export enum AgentStatus { + RUNNING = "RUNNING", + UNINSTALL = "UNINSTALL", + STOP = "STOP" +} \ No newline at end of file diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/protocol/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/protocol/index.vue new file mode 100644 index 000000000..5d6f4dcb1 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/resource/protocol/index.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/role/components/EditRole.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/role/components/EditRole.vue new file mode 100644 index 000000000..3a81c9bef --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/role/components/EditRole.vue @@ -0,0 +1,191 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/role/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/role/index.vue new file mode 100644 index 000000000..75f6a3fcd --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/role/index.vue @@ -0,0 +1,200 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/systemSetting/SystemSetting.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/systemSetting/SystemSetting.vue new file mode 100644 index 000000000..42babeb5a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/systemSetting/SystemSetting.vue @@ -0,0 +1,152 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/components/EditCode.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/components/EditCode.vue new file mode 100644 index 000000000..944d5ea03 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/components/EditCode.vue @@ -0,0 +1,125 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/components/EditUser.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/components/EditUser.vue new file mode 100644 index 000000000..afc92ce59 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/components/EditUser.vue @@ -0,0 +1,192 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/components/UpdateCode.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/components/UpdateCode.vue new file mode 100644 index 000000000..4ae5906f0 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/components/UpdateCode.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/index.vue new file mode 100644 index 000000000..ee32b06bc --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/index.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/ucenter/components/EditInfo.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/ucenter/components/EditInfo.vue new file mode 100644 index 000000000..23209d554 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/ucenter/components/EditInfo.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/ucenter/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/ucenter/index.vue new file mode 100644 index 000000000..2970576a9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/user/ucenter/index.vue @@ -0,0 +1,215 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/whitelist/components/EditCondition.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/whitelist/components/EditCondition.vue new file mode 100644 index 000000000..69bee8cf4 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/whitelist/components/EditCondition.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/whitelist/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/whitelist/index.vue new file mode 100644 index 000000000..a2f39d3f9 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/security/whitelist/index.vue @@ -0,0 +1,163 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/task/detail/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/task/detail/index.vue new file mode 100644 index 000000000..560f0c0ba --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/task/detail/index.vue @@ -0,0 +1,187 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/src/views/task/manage/index.vue b/ER-Model/openGauss-datakit/visualtool-ui/src/views/task/manage/index.vue new file mode 100644 index 000000000..a0e88cf1c --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/src/views/task/manage/index.vue @@ -0,0 +1,284 @@ + + + + + diff --git a/ER-Model/openGauss-datakit/visualtool-ui/tsconfig.json b/ER-Model/openGauss-datakit/visualtool-ui/tsconfig.json new file mode 100644 index 000000000..82e357a3a --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "strict": true, + "jsx": "preserve", + "moduleResolution": "node", + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "useDefineForClassFields": true, + "sourceMap": true, + "resolveJsonModule": true, + "baseUrl": ".", + "types": [ + "webpack-env" + ], + "paths": { + "@/*": [ + "src/*" + ] + }, + "lib": [ + "esnext", + "dom", + "dom.iterable", + "scripthost" + ] + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.vue", + "tests/**/*.ts", + "tests/**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/ER-Model/openGauss-datakit/visualtool-ui/vue.config.js b/ER-Model/openGauss-datakit/visualtool-ui/vue.config.js new file mode 100644 index 000000000..e70747f53 --- /dev/null +++ b/ER-Model/openGauss-datakit/visualtool-ui/vue.config.js @@ -0,0 +1,76 @@ +const { defineConfig } = require('@vue/cli-service') +const path = require('path') + +const resolve = dir => path.join(__dirname, './', dir) +const port = process.env.port || process.env.npm_config_port || 80 + +module.exports = defineConfig({ + productionSourceMap: false, + runtimeCompiler: true, + transpileDependencies: false, + lintOnSave: false, + css: { + loaderOptions: { + less: { + lessOptions: { + modifyVars: { + 'primary-6': '#E41D1D', + 'primary-5': '#E94842', + 'primary-7': '#BE1118', + 'primary-4': '#FAC4BC', + 'primary-3': '#F49C92', + 'primary-2': '#F49C92', + 'primary-1': '#FFECE8', + 'warning-6': '#FF7D01', + 'success-6': '#00B429', + 'link-6': '#E41D1D', + 'color-text-1': '#1D212A', + 'color-text-2': '#4E5A6A', + 'color-text-3': '#868F9C', + 'color-text-4': '#f2f3f5', + 'dark-color-text-4': '#4E5A6A', + 'dark-primary-7': '#FCEF92', + 'dark-primary-6': '#FCEF92', + 'dark-primary-5': '#F8EFAE', + 'dark-warning-6': '#FF7D01', + 'dark-success-6': '#00B429', + 'dark-link-6': '#FCEF92' + }, + javascriptEnabled: true + } + } + } + }, + chainWebpack (config) { + config.module.rule('svg').exclude.add(resolve('src/assets/icons')).end() + config.module + .rule('icons') + .test(/\.svg$/) + .include.add(resolve('src/assets/icons')) + .end() + .use('svg-sprite-loader') + .loader('svg-sprite-loader') + .options({ + symbolId: 'icon-[name]' + }) + .end() + }, + // webpack-dev-server config + devServer: { + headers: { + 'Access-Control-Allow-Origin': '*' + }, + port, + proxy: { + // detail: https://cli.vuejs.org/config/#devserver-proxy + [process.env.VUE_APP_BASE_API]: { + target: process.env.API_BASE_URL, + changeOrigin: true, + pathRewrite: { + ['^' + process.env.VUE_APP_BASE_API]: '' + } + } + }, + allowedHosts: 'all' + } +}) diff --git a/ER-Model/plugins/base-ops/.gitignore b/ER-Model/plugins/base-ops/.gitignore new file mode 100644 index 000000000..2a5acb586 --- /dev/null +++ b/ER-Model/plugins/base-ops/.gitignore @@ -0,0 +1,45 @@ +###################################################################### +**/.DS_Store +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup +*.swp + +!*/build/*.java +!*/build/*.html +!*/build/*.xml diff --git a/ER-Model/plugins/base-ops/README.md b/ER-Model/plugins/base-ops/README.md new file mode 100644 index 000000000..5338a7709 --- /dev/null +++ b/ER-Model/plugins/base-ops/README.md @@ -0,0 +1,183 @@ +# Base-Ops 基础运维插件介绍 + + + +## 版本介绍 + +Base-Ops项目目前版本为1.0.0,主要目的是为openGauss用户提供简单易上手的运维、可视化、可监控能力。它作为一体化平台的可插拔插件开发,本特性依赖于openGauss一体化平台的插件特性。 + + + +## 项目背景 + +openGauss的安装、运维场景对于初级用户或单纯想要测试openGauss数据库基本特性的使用者来说技术难度较大、过程较为复杂、学习曲线较为陡峭,尤其企业版安装对一般用户来说操作难度很大。 + +使用可视化运维平台可以屏蔽openGauss的技术细节,让普通用户能够快速上手体验功能,让运维人员能够快速在企业环境中部署、卸载各类openGauss集群,减少了用户的学习成本和运维成本,实现了对openGauss各种常见操作的可视化,屏蔽了各种不同openGauss版本中的运维命令差异,可以让用户使用相同的方式操作数据库,不用知道命令细节也可以使用openGauss数据库的各项能力,让用户可以专注于自身的业务领域。 + + + +基于上述背景本插件主要提供如下能力: + +- 实现不同版本的openGauss安装、不同版本的升级;实现简单快捷的数据库日常运维,例如启停卸载等 + +- 实现在画面上查看openGauss数据库基本状态、实时数据推送等。 + +- 数据算子的可视化编辑、SQL的预览和运行、图表的生成与分享。 + + + +## 功能特性介绍 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
特性名称特性描述备注
并行安装任务支持openGauss企业版、极简版、轻量版安装支持3.0.0+
集群安装支持openGauss企业版、极简版、轻量版安装支持3.0.0+
集群导入支持openGauss企业版、极简版、轻量版导入支持3.0.0+
一键安装支持openGauss极简版3.0.0一键安装
集群升级支持openGauss企业版升级支持3.0.0+
集群运维支持openGauss企业版、极简版、轻量版的各类运维操作,例如:启动、停止、卸载、备机重建等支持3.0.0+
集群监控图表支持openGauss企业版、极简版、轻量版的各类运维监控指标,例如CPU使用率、内存占用率、会话数量等支持3.0.0+
物理机资源管理实现对物理机及用户的管理
AZ管理实现对AZ的管理
快照管理支持openGauss企业版、极简版、轻量版的快照生成与删除
WDR报告支持openGauss企业版的WDR报告生成与在线查看
慢SQL查询支持openGauss企业版、极简版、轻量版的慢SQL在线查看
集群日志支持openGauss企业版、极简版、轻量版的日志在线查看
备份恢复支持openGauss企业版、极简版、轻量版的数据备份恢复
可视化编辑提供拖拽、连线的操作方式完成数据流算子的组装、配置支持3.0.0+
实时图表生成支持读取单步算子执行结果为用户生成自定义图表,并可以记录快照支持3.0.0+
报表构建和分享可将一个或多个图表快照通过自由排布的方式构建报表,并可通过外链分享给游客用户
+ + +# + +## 版本使用注意事项 + +- base-ops项目是作为openGauss一体化平台插件进行开发,使用本项目必须依赖一体化平台。 +- base-ops为一个Java应用,在构建时需确保配置Java 11+的JDK + +## 安装说明 + +- ``` + #下载源码: + git clone git@gitee.com:opengauss/openGauss-workbench.git + #进入根目录 + cd base-ops + #编译打包 + mvn clean package -Dmaven.test.skip + ``` + +- 打包完成后在bsae-ops/target目录中找到base-ops-{version}-repackage.jar即为插件安装包。 + +- 打开并登陆openGauss一体化平台,点击插件管理-安装插件,将上诉步骤获得的安装包上传并安装。 + +- 刷新页面可在右侧菜单栏看到**安装部署、可观测运维、业务设计**三个一级菜单即为安装成功。 + +## 使用说明 + +- 具体使用方式见使用文档 + + + +# 参与贡献 + +**参与贡献** + +作为openGauss用户,你可以通过多种方式协助openGauss社区。参与社区贡献的方法请参见[社区贡献](https://opengauss.org/zh/contribution.html),这里简单列出部分方式供参考。 + +**特别兴趣小组** + +openGauss将拥有共同兴趣的人们聚在一起,组成了不同的特别兴趣小组(SIG)。当前已有的SIG请参见[SIG列表](https://opengauss.org/zh/contribution.html)。 + +我们欢迎并鼓励你加入已有的SIG或创建新的SIG,创建方法请参见[SIG管理指南](https://opengauss.org/zh/contribution.html)。 + +**邮件列表和任务** + +欢迎你积极地帮助用户解决在[邮件列表](https://opengauss.org/zh/community/mails.html)和issue任务(包括[代码仓任务](https://gitee.com/organizations/opengauss/issues)) 中提出的问题。另外,我们也欢迎你提出问题。这些都将帮助openGauss社区更好地发展。 + +**文档** + +你不仅可以通过提交代码参与社区贡献,我们也欢迎你反馈遇到的问题、困难,或者对文档易用性、完整性的改进建议等。例如获取软件或文档过程中的问题,使用系统过程中的难点。欢迎关注并改进openGauss社区的文档模块。 + +**IRC** + +openGauss也在IRC开辟了频道,作为提供社区支持和交互的额外渠道。详情请参见[openGauss IRC](https://opengauss.org/zh/community/onlineCommunication.html)。 + +# 开源的资料文档应附有对应的文档许可证 + +本文档遵循[知识共享许可协议CC 4.0](https://creativecommons.org/licenses/by/4.0/) (http://creativecommons.org/Licenses/by/4.0/)。 diff --git a/ER-Model/plugins/base-ops/doc/DataKit Product Manual - Base OPS.md b/ER-Model/plugins/base-ops/doc/DataKit Product Manual - Base OPS.md new file mode 100644 index 000000000..36220f54a --- /dev/null +++ b/ER-Model/plugins/base-ops/doc/DataKit Product Manual - Base OPS.md @@ -0,0 +1,1055 @@ +​ + +## 1. 引言 + ### 1.1 编写目的 +本文档主要描述软件操作使用方法。本文档适用于管理员、各角色数据使用人员。 + + ### 1.2 适用范围 +使用该软件的所有技术人员与客户人员。 +## 2. 客户端环境要求 + ### 2.1 硬件设备要求 + +|**用途**|**性能要求**| +| :-: | :-: | +|客户端|CPU:4核8G 2.0GHz 硬盘:128GB| + ### 2.2 支撑软件 +支持软件,包括要用到的操作系统、浏览器等。 + +|**用途**|**支撑软件**|**操作系统**| +| :-: | :-: | :-: | +|客户端|Chrome|Windows 10 | +## 3 安装部署 + +注意,如果是openEuler操作系统,需要手动先设置umask值为022,设置命令: umask 022 + + + ### 3.1 一键安装 +左侧菜单栏点击【安装部署】-【一键安装】进入该页面。 + #### 3.1.1 安装-选择主机 + +用户切换主机IP时,主机的架构信息将显示在右侧。 + +用户可以选择不同版本的极简版进行安装,版本号来自于安装包管理内与用户所选系统和CPU架构匹配的极简版版本号。 + +选择主机IP,输入有效的root密码,点击【一键安装】进入安装页面。 + +主机IP:默认显示所有物理机。 + +root密码:安全校验,需填写物理机对应密码。 + +一键安装时,默认选择目标主机上的第一个非root用户进行安装,如果目标物理机上没有非root用户,则会自动创建一个omm用户用于安装,如果omm已经存在,则会按自然顺序继续创建omm1、omm2.....直到创建成功为止。 + +![](_resources/015.png) + +当用户已经在该机器上安装了极简版时(判断方式为默认安装目录/opt/openGauss下是否有文件,如果有,认为已经安装了极简版),提示用户前往集群安装画面进行更为个性化的安装。 + +![](_resources/one-click-taken.png) + + #### 3.1.2 安装中 + +选择主机IP,输入有效的root密码,点击【一键安装】进入安装中页面。显示安装实时进程信息。 + +![](_resources/016.png) + +一键安装的过程中不能关闭tab,点击关闭时,将会提示用户不能关闭tab。 + +![](_resources/one-click-install-not-close-tab.png) + + #### 3.1.3 安装失败 + +安装失败后,左侧控制台显示安装失败信息。右侧显示自定义控制台、重试按钮。用户可以在右侧自定义控制台中执行命令修正机器上的安装问题。 + +重试:点击后重新开始安装过程。 + +![](_resources/017.png) + + #### 3.1.4 安装成功 +安装成功时,提示安装成功,点击【集群运维】可以跳转到集群运维页面进行运维操作。 + +![](_resources/018.png) + ### 3.2 集群安装 +左侧菜单栏点击【安装部署】-【集群安装】进入该页面。 + +对于openGauss的三个版本,安装的前三个步骤是一致的,下面介绍这些通用步骤。 + +注意:三个版本选择安装的主机都需要有root用户 + +依赖说明: + +touch命令:环境分离方式安装集群时,会通过touch命令创建环境变量文件 + + #### 3.2.1 通用步骤 + +##### 3.2.1.1 选择版本 + +用户首先选择一个openGauss的版本进行安装,目前提供极简版、轻量版、企业版的安装选项。 + +![](_resources/019.png) + ##### 3.2.1.2 选择安装模式 +安装模式分为安装、导入两种模式,默认的选择为“安装”选项,即通过后续步骤在物理机上安装openGauss。 + + + +![](_resources/install-type-select.png) + + + +##### 3.2.1.3 选择安装网络类型 + +安装网络类型分为离线安装和在线安装两类,离线安装需要用户自行上传安装包,在线安装会按照安装包管理页面上的在线地址设置进行下载。 + +![](_resources/020.png) + +1. 离线安装:选择后,跳转安装包选择页面。 + + 用户在可以在离线安装处上传openGauss安装包,上传安装包后将尝试自动解析所属系统、CPU架构、版本名称、版本号等,尝试失败的安装包,所属系统将被选择为通用系统all,CPU架构将被选择为通用架构noarch。此处上传的安装包将被保存到安装包管理列表内。 + + +![](_resources/021.png) + +点击上传按钮或者直接点击确定均可以完成安装包上传,点击上传按钮右侧的删除图标移除不需要的安装包。 + +![](_resources/install-upload-pkg.png) + +上传完成后,安装包名称中含有-all.tar.gz的将被过滤为企业版,含有-Lite-的被过滤为轻量版,两者都不满足的,并且以.tar.bz2结尾的,过滤为极简版。 + +2. 在线安装:选择后,跳转安装包下载页面,支持用户下载不同版本的安装包,下载后成功点击【下一步】进入安装步骤页面。 + +![](_resources/022.png) + +当用户下载了多个不同版本的安装包时,可以选择任意一个进行安装。 + +![](_resources/023.png) + + + + + +对于不同的版本,安装步骤有所不同,下面分别进行介绍。 + + #### 3.2.2 极简版安装 + +安装版本选择页面,选择【极简版】后,选择安装网络类型后跳转到极简版安装引导界面 + +![](_resources/025.png) + +##### 3.2.2.1 安装步骤-部署方式 + +部署方式页面,选择集群部署方式为极简版单节点或一主一备,点击【下一步】跳转到对应安装配置页面 + +![](_resources/026.png) + +##### 3.2.2.2 安装步骤-安装配置 + +安装配置页面,填写有效的配置信息,点击【下一步】校验通过后,将进入到环境检测页面。 + +![](_resources/027.png) + +点击【下一步】校验不通过时,会对相应项目进行提示,目前的检测项目为端口与安装目录。 + +![](_resources/install-check-mini.png) + +##### 3.2.2.3 安装步骤-环境检测 + +在这个页面上,会自动检测当前安装主机硬件环境、软件环境,检测完成后弹出对应提示信息。 + +![](_resources/028.png) + +##### 3.2.2.4 安装步骤-安装提示 + +安装提示页面,显示用户在前面步骤选择的安装主机IP、安装目录等提示信息,方便用户做最后的确认。点击【下一步】进入安装执行页面。 + +![](_resources/029.png) + +##### 3.2.2.5 安装步骤-执行安装 + +安装执行页面,显示安装进度及安装日志。 + +![](_resources/030.png) + +安装成功后,点击下方【集群运维】按钮即可跳转到集群运维页面,进行集群运维管理。 + +![](_resources/031.png) + #### 3.2.3 轻量版安装 +安装版本选择页面,选择【轻量版】后,选择安装网络类型后跳转到轻量版安装引导界面 + +![](_resources/019.png) + +##### 3.2.3.1 安装步骤-部署方式 + +部署方式页面,选择集群部署方式为轻量版单节点或一主一备,点击【下一步】跳转到对应安装配置页面 + +![](_resources/032.png) + +##### 3.2.3.2 安装步骤-安装配置 + +安装配置页面,填写有效的配置信息,点击【下一步】校验通过后,将进入到环境检测页面。 + +![](_resources/033.png) + +点击【下一步】校验不通过时,会对相应项目进行提示,目前的检测项目为端口与数据目录。 + +![](_resources/install-lite-check.png) + + + +##### 3.2.3.3 安装步骤-环境检测 + +在这个页面上,会自动检测当前安装主机硬件环境、软件环境,检测完成后弹出对应提示信息。 + +![](_resources/034.png) + +##### 3.2.3.4 安装步骤-安装提示 + +安装提示页面,显示用户在前面步骤选择的安装主机IP、安装目录等提示信息,方便用户做最后的确认。点击【下一步】进入安装执行页面。 + +![](_resources/035.png) + +##### 3.2.3.5 安装步骤-执行安装 + +安装执行页面,显示安装进度及安装日志。 + +![](_resources/036.png) + +安装成功后,点击下方【集群运维】按钮即可跳转【集群运维】页面,进行集群管理。 + +![](_resources/037.png) + +若安装失败时,可在右侧自定义控制台输入命令。如果在轻量版一主一备模式下,可以分别在两个不同的机器上执行命令。处理完成后,点击【重试】按钮再次进行安装。 + +![](_resources/038.png) + + #### 3.2.4 企业版安装 + +安装版本选择页面,选择【企业版】后,选择安装网络类型后跳转到企业版安装引导界面。 + +![](_resources/040.png) + +##### 3.2.4.1 安装步骤-集群配置 + +用户在这个页面进行集群版安装前的配置准备,一般来说用户不需要更改安装目录。用户需要输入集群标识、数据库密码。 + +内核架构默认是采用“主备架构”进行安装,如果要进行资源池化的安装,需要选择为“共享存储架构”。 + +用户需要选择是否安装CM,根据是否选择安装CM,分为以下两种情况。 + +1. 在安装CM的情况下必须至少安装一主二备。可以启用DCF自选主功能。 + +2. 在不安装CM的情况下,用户可以安装最少一个主节点,并且不能开启DCF自选主功能。 + +![](_resources/install-cluster-global-config.png) + +##### 3.2.4.2 安装步骤-节点配置 + +在节点配置页面上,用户需要填写每个节点对应的配置信息,选择要安装节点的机器,并且选择一个AZ用于企业版安装。 + +如果在一主多备并且DCF开启的模式下,用户输入更高的AZ优先级数字可以使得该节点更容易被选择为主节点。 + +确认无误后,点击【下一步】完成节点配置。 + +以下为一种单节点企业版节点配置的典型情况。 + +![](_resources/install-cluster-node-config.png) + +开启安装CM的情况下,需要配置额外的CM信息,例如CM的数据路径和CM的端口号。一般情况下用户无需改变里面的默认配置。 + +以下为一种一主两备企业版节点配置的典型情况。 + +![](_resources/install-cluster-node-config-cm.png) + +如果是“共享存储架构”下安装,则CM默认开启,同时还需要填写共享存储页的信息,其中,DSS共享卷路径、Xlog卷路径、CM共享卷路径、CM投票卷路径会根据配置的主机在下拉列表中提供,用户选择对应的卷名即可。 + +![](_resources/sharing_storage_configure.png) + +点击【下一步】后,将会对企业版安装相关端口和数据目录的占用情况进行校验,相应检查问题会表示在对应项目的下方。当检查未发生端口、路径占用后,将进入到环境检测步骤。 + +![](_resources/install-cluster-check.png) + +##### 3.2.4.3 安装步骤-环境检测 + +在这个页面上将自动检测当前企业版所有节点的安装主机硬件环境、软件环境是否满足安装要求,检测完成后弹出对应提示信息。 + +![](_resources/042.png) + +##### 3.2.4.4 安装步骤-安装提示 + +这个页面显示用户在前面步骤选择的每个节点的安装主机IP、安装目录等提示信息,方便用户做最后的确认。点击【下一步】进入安装执行页面。 + +![](_resources/041.png) + +##### 3.2.4.5 安装步骤-执行安装 + +安装执行页面,显示安装进度及安装日志。 + +![](_resources/043.png) + + + +安装成功后,点击下方【集群运维】按钮即可跳转【集群运维】页面,进行集群运维管理。 + +![](_resources/044.png) + +若安装失败时,可在右侧自定义控制台输入命令或点击【重试】按钮 + +![](_resources/038.png) + + + +#### 3.2.5 openLooKeng安装 + +用户可以通过【集群安装】-【sharding + openLooKeng】进入安装界面,或者通过【资源中心】-【OpenLooKeng管理】进入。 + +关于这部分功能安装完成后的详细使用方法,可以参考以下文档: + +[openLooKeng文档](https://docs.openlookeng.io/zh/docs/docs/start.html) + +[ShardingProxy文档](https://shardingsphere.apache.org/document/5.3.1/en/user-manual/shardingsphere-proxy/) + +![](_resources/install-olk-config.png) + +##### 3.2.5.1 配置添加 + +安装openLooKeng需要分别配置基础配置、ShardingProxy配置、openLooKeng配置画面上的表单项目,没有对应安装包时,可以在直接在画面上添加对应安装包。 + +对于distribute deploy,它的安装路径和包上传路径为同一个。所有安装、上传路径在不存在时,都会自动创建。 + +![](_resources/install-olk-config-dad.png) + +配置ShardingProxy时,需要选择至少两个数据源以达到分片的效果,用户需要自行输入表名称和分片键,并以,分割。 + +![](_resources/install-olk-config-ss.png) + +openLooKeng的配置界面上的高级配置用户在不填写时,默认按照推荐方式进行配置。 + +![](_resources/install-olk-config-olk.png) + +配置完成后,点击下一步进入配置预览画面。 + +##### 3.2.5.2 配置预览 + +在这个画面上,用户可以对分片配置文件rule.yaml进行编辑和预览,这个配置文件将在安装时被上传到ShardingProxy中。 + +点击右上角的】【重新生成】可以根据上一步的配置情况重新生成rule配置,将会覆盖用户当前的修改。 + +![](_resources/install-olk-config-view.png) + +##### 3.2.5.3 执行安装 + +点击下一步将开始安装过程,实时的进度会在画面上自动刷新,用户可以下载日志,或这点击自定义控制台按钮对主机进行操作。 + +![](_resources/install-olk-exe.png) + +安装失败时,可以根据日志提示到主机上进行操作,排除问题后可以点击重试按钮重新进行安装过程。 + +![](_resources/install-olk-error.png) + +安装完成后,可以跳转到openLooKeng管理界面对安装好的集群进行管理。 + +![](_resources/install-olk-success.png) + +##### 3.2.5.4 管理页面 + +点击【资源中心】-【OpenLooKeng管理】进入管理页面,在这个页面上,用户可以对安装好的openLooKeng集群进行查看、删除、启停、销毁等操作。删除仅指删除这条记录而不释放资源,销毁则是既释放资源也删除这一条记录。 + +每个操作进行过程中,用户都可以实时下载日志进行查看,每个新操作开始后,都会清除上一个操作的日志。 + +![](_resources/install-olk-list.png) + +点击启动、停止、销毁时,这个集群上的其它操作将暂时不能执行,用户可以下载日志查看。运行结束后,会弹出运行结果是否成功失败的提示。 + +![](_resources/install-olk-list-stop.png) + +### 3.3 集群导入 + +#### 3.3.1 极简版导入 + +##### 3.3.1.1 导入步骤-选择版本 + +选择安装模式为【导入】,并且选择极简版,点击【下一步】 + +![](_resources/import-mini-select-version.png) + +##### 3.3.1.2 导入步骤-部署方式 + +选择要导入的极简版的部署方式,可以为单节点或一主一备。选择完成后,点击【下一步】进入节点配置步骤。 + +![](_resources/import-mini-deploy-config.png) + +##### 3.3.1.2 导入步骤-节点配置 + +本步骤需要输入极简版集群节点配置信息,输入集群标识及要导入集群的安装目录与端口、数据库用户名与密码。如果此处输入的数据库用户名密码不正确,将导致后续无法正确连接数据库而导致导入失败。点击【下一步】将进行实例校验,防止用户输入错误路径或者端口导致导入失败。 + +![](_resources/import-mini-node-config.png) + + + +校验失败时,对应项目的下方会弹出提示。目前检测的项目有安装目录和端口。 + +![](_resources/import-mini-check.png) + +##### 3.3.1.3 导入步骤-导入提示 + +这个页面显示用户在前面步骤输入的每个节点的安装主机IP、安装目录等提示信息,方便用户做最后的确认。点击【下一步】进入导入执行页面。 + +![](_resources/import-mini-confirm.png) + +##### 3.3.1.4 导入步骤-执行导入 + +执行导入时,将首先尝试连接目标集群,如果连接失败,将会提示连接错误,用户可以返回【集群配置】步骤进行修改。 + +![](_resources/import-mini-connect-fail.png) + +导入成功时,将提示导入成功页面,用户可以在这里返回首页或者进入【集群运维】页面。至此,极简版集群就已经被导入了。 + +![](_resources/import-mini-ok.png) + +#### 3.3.2 轻量版导入 + +##### 3.3.2.1 导入步骤-选择版本 + +选择安装模式为【导入】,并且选择轻量版,点击【下一步】 + +![](_resources/import-lite-select-version.png) + +##### 3.3.2.2 导入步骤-部署方式 + +选择要导入的轻量版的部署方式,可以为单节点或一主一备。选择完成后,点击【下一步】进入节点配置步骤。 + +![](_resources/import-lite-deploy-config.png) + +##### 3.3.2.3 导入步骤-节点配置 + +本步骤需要输入轻量版集群节点配置信息,输入集群标识及要导入集群的安装目录与端口、数据库用户名与密码。用户还可以选择是否是环境分离的轻量版导入,如果需要导入环境分离的轻量版,需要打开是否环境分离开关,并输入环境分离文件的路径。如果此处输入的数据库用户名密码不正确,将导致后续无法正确连接数据库而导致导入失败。点击【下一步】将进行实例校验,防止用户输入错误路径或者端口导致导入失败。 + +![](_resources/import-lite-node-config.png) + +校验失败时,对应项目的下方会弹出提示。目前检测的项目有数据目录和端口。 + +![](_resources/import-lite-check.png) + +##### 3.3.2.4 导入步骤-导入提示 + +这个页面显示用户在前面步骤输入的每个节点的安装主机IP、安装目录等提示信息,方便用户做最后的确认。点击【下一步】进入导入执行页面。 + +![](_resources/import-lite-confirm.png) + +##### 3.3.2.5 导入步骤-执行导入 + +执行导入时,将首先尝试连接目标集群,如果连接失败,将会提示连接错误,用户可以返回【集群配置】步骤进行修改。 + +![](_resources/import-lite-connect-fail.png) + +导入成功时,将提示导入成功页面,用户可以在这里返回首页或者进入【集群管理】页面。至此,轻量版集群就已经被导入了。 + +![](_resources/import-lite-ok.png) + +#### 3.3.3 企业版导入 + +导入集群之前,需要对现有数据库进行设置以便于平台能够获取到数据库的监控数据信息。 + +1. 创建平台所需的连接用户 + + CREATE USER username WITH MONADMIN PASSWORD "password"; + +2. 对所有节点配置白名单并重启数据库 + + gs_guc set -N all -I all -h "host all all 平台ip地址/32 sha256" + +##### 3.3.3.1 导入步骤-选择版本 + +选择安装模式为【导入】,并且选择企业版,点击【下一步】 + +![](_resources/import-cluster-select-version.png) + +##### 3.3.3.2 导入步骤-集群配置 + +输入集群标识及要导入集群的各种路径与端口、数据库用户名与密码,及该集群是否安装了CM。如果需要导入环境分离的企业版,还需要打开是否环境分离的开关,并输入环境分离文件路径。如果此处输入的数据库用户名密码不正确,将导致后续无法正确连接数据库而导致导入失败。点击【下一步】进入节点配置。 + +![](_resources/import-cluster-config.png) + + + +##### 3.3.3.3 导入步骤-节点配置 + +本步骤需要输入完整的企业版集群节点配置信息,假设企业版有一主二备,这里也增加同样数量的节点并输入每个节点的配置。 + +节点配置时不需要输入AZ名称及每个节点的AZ优先级,相关信息将自动从企业版集群中获取,并创建到Datakit平台上。 + +点击【下一步】进行实例校验,防止用户输入错误路径或者端口导致导入失败。 + +![](_resources/import-cluster-node-config.png) + +校验失败时,对应项目的下方会弹出提示。目前检测的项目有数据路径和端口。 + +![](_resources/import-cluster-check.png) + +##### 3.3.3.4 导入步骤-导入提示 + +这个页面显示用户在前面步骤输入的每个节点的安装主机IP、安装目录等提示信息,方便用户做最后的确认。点击【下一步】进入导入执行页面。 + +![](_resources/import-cluster-confirm.png) + + + +##### 3.3.3.5 导入步骤-执行导入 + +执行导入时,将首先尝试连接目标集群,如果连接失败,将会提示连接错误,用户可以返回【集群配置】步骤进行修改。 + +![](_resources/import-cluster-connect-fail.png) + + + +导入成功时,将提示导入成功页面,用户可以在这里返回首页或者进入【集群管理】页面。至此,企业版集群就已经被导入了。 + +![](_resources/import-cluster-ok.png) + +#### 3.3.4 批量导入 + +批量导入为新增功能,且为了简化导入集群用户需要填写的信息,导入后只存储了数据库的数据路径和安装路径,用户在导入模板中只需要填写集群名、内外网IP、安装用户、数据库安装用户(非初始用户)、数据库密码、集群端口号、环境文件路径即可进行导入,相关详细步骤如下。 + +##### 3.3.4.1 批量导入步骤--下载导入模板 + +首先点击【批量导入】按钮进入批量导入界面。 + +![](_resources/bulkImport-entrance.png) + +其次,点击导入下载模板进行下载 + +![](_resources/bulkImport-downloadTemplate.png) + +##### 3.3.4.2 批量导入步骤--编辑导入模板 + +模板中存在一个示例,可以根据示例来进行填写(注意:极简版一主一备需要填写两个相同的IP,集群对应的端口只填写主或者备机的任意一个即可) + +![](_resources/bulkImport-editTemplate.png) +##### 3.3.4.3 批量导入步骤--上传导入文件 + +填写完成后,点击上传文件,并选中已经编辑好的文件。 + +![](_resources/bulkImport-upload.png) + +之后点击确定即可开始解析。 + +![](_resources/bulkImport-confirm.png) + +##### 3.3.4.4 批量导入步骤--解析导入文件 + +解析过程中不要中途关闭,不然会导致无法查看错误报告。 + +![](_resources/bulkImport-parseExcel.png) + +##### 3.3.4.5 批量导入步骤--下载错误报告 + +解析完成后,点击下载错误报告,查看错误日志 + +![](_resources/bulkImport-downErrorReport.png) + +##### 3.3.4.6 批量导入步骤--查看错误报告 + +错误报告一般会提示用户主要错误在哪里,所以需要学会看报错信息。("check publicIp, installUsername and envPath"这个报错大概率是环境变量的报错,可以重点排查里面的相关字段,企业版:GAUSSHOME(安装目录)、PGDATA(数据目录);轻量版:GAUSSHOME、GAUSSDATA(数据目录);极简版:GAUSSHOME。如果以上字段中某些不存在,可能会报错,所以需要添加或者将相关字段对应修改。分离环境变量可以如实填写路径即可,未分离环境变量时可以填写~/.bashrc) + +(此外,如果环境分离文件路径填写错误,但是该文件如果存在,也可能导致导入成功,请填写时注意!) + +![](_resources/bulkImport-errorInfo.png) + +### 3.4 集群升级 + +#### 3.4.1 升级配置 + +用户点击左侧菜单中【安装部署】-【批量升级】进入集群升级功能,功能分为三步进行,第一步进行集群配置,第二部环境检查,第三步执行升级。 + +集群配置页面分为左右两个区域,左边区域进行多个要升级的企业版集群选择,右侧选择要升级的目标版本。 + +左侧集群来源于用户在Datakit平台上所安装的所有企业版集群,只有状态可用的集群可以选择。 + +右侧目标版本列表来源于安装包管理功能,按照版本新旧顺序降序排列。 + +左右两侧选择区按照集群版本号、系统、cpu架构互相进行筛选,符合条件的可以选择,不能满足条件的将会置灰。可以一次性把多个集群升级到一个版本,目标版本必须大于左边选择集群列表的所有版本号,并且系统、cpu架构要完全一致。 + +![](_resources/upgrade-config-1.png) + +点击两侧标题右边的刷新按钮可以分别更新集群列表和目标版本列表。集群中任意节点服务器未保存root密码时,将展示输入root密码的输入框,所有节点服务器的root密码必须保持一致。 + +![](_resources/upgrade-config-2.png)当左侧两侧至少选择了一个项目后,可以进入下一步,否则会提示相应错误。 + +![](_resources/upgrade-config-3.png) + +#### 3.4.2 环境检查 + +本步骤将对上一步选择升级项目进行环境检查,使用gs_checkos检查操作系统、控制参数、磁盘配置等内容并返回报告供用户查看。 + +如果主节点的磁盘使用率大于90%,将不能进行下一步执行,用户可点击控制台图标释放部分磁盘空间后重新检测;磁盘使用率大于70%,将提示橙色警告,小于70%则展示为绿色的提示。 + +![](_resources/upgrade-check-1.png) + +![](_resources/upgrade-check-2.png) + +#### 3.4.3 执行升级 + +环境检查正常通过后,将进行集群升级,每个升级项目单独列出一个区域,上半部分展示升级的内容,即从哪些集群升级到什么版本,中部展示升级进度,右侧展示当前的升级步骤和操作按钮。 + +![](_resources/upgrade-exe-1.png) + +点击右上角全部收起或全部展开按钮,可以批量展开或收起全部升级项目,点击升级项目标题也可单独收起或展开单个升级项目。点击【下载日志】按钮,可以下载运行过程中产生的日志。 + +![](_resources/upgrade-exe-2.png) + +升级成功时,用户可以选择【提交升级】或【升级回滚】,提交升级后,集群将不能再进行其它操作,视为已经升级完成。除了点击相应升级项目的提交和回滚以外,还可以点击右上角的【批量提交】和【批量回滚】进行批量操作,系统将判断哪些升级项目可以进行对应操作,并自动进行相应操作。 + +![](_resources/upgrade-exe-3.png) + +下图为升级成功提交后的表示。 + +![](_resources/upgrade-exe-4.png) + +升级失败时,用户可以点击【重试】重新开始升级过程,或者点击【升级回滚】进行回滚。 + +![](_resources/upgrade-exe-5.png) + +### 3.5 备份恢复 + +##### 3.5.1 备份操作 + +用户在集群运维页面上进行备份操作,点击某个集群上的备份按钮进行数据库备份。弹出确认备份对话框,输入必要的备注以后点击备份按钮进行备份。 + +备份采用的是`gs_dumpall`逻辑备份,详细信息请查看openGauss官方文档:[备份与恢复](https://docs.opengauss.org/zh/docs/3.1.1/docs/Administratorguide/%E5%A4%87%E4%BB%BD%E4%B8%8E%E6%81%A2%E5%A4%8D.html) + +![](_resources/backup.png) + +备份开始后,相关执行过程中的日志将会弹出显示。 + +![](_resources/backup-running.png) + +##### 3.5.2 恢复操作 + +点击左侧菜单的【安装部署】-【备份恢复】菜单,进入备份恢复页面,这里可以看到每个集群所产生的备份文件列表。 + +通过右上角的集群标识,用户可以过滤自己想要查看的集群备份列表。 + +![](_resources/recovery-list.png) + +点击右侧的【恢复】,即可将备份文件中的数据恢复到对应的集群中。 + +![](_resources/recovery-running.png) + + + +点击【删除】按钮,将会把选中的这一条备份信息连同备份文件一起删除。 + +![](_resources/recovery-delete.png) + +### 3.6 容灾集群安装 + +#### 3.6.1 安装步骤-容灾集群配置 + +用户在此页面进行容灾集群安装的配置,选择主备集群以及对应的磁阵信息。如果没有安装对应的单集群和磁阵,可以在点击当前的集群安装和磁阵管理按钮去添加对应的集群和磁阵信息。 + +![](_resources/start_disaster_cluster.png) + +磁阵管理页面如下。需要添加磁阵的IP端口号,以及同步复制pairId等信息。 + +![](_resources/create_device_manager_interface.png) + +#### 3.6.2 安装步骤-执行安装 + +填写好具体信息之后,点击下一步执行安装,安装过程如下。 + +![](_resources/execute_disaster_cluster_installing.png) + +如果安装成功,会显示如下界面。在这个页面可以下载安装日志,或者跳转到容灾集群管理。 + +![](_resources/finish_disaster_cluster_installing.png) + +通过点击容灾集群管理可以到容灾集群管理页面。 + +![](_resources/manage_disaster_cluster.png) + +如果安装失败,可以通过上一步或者下载日志进行分析。 + +![](_resources/install_disaster_cluster_failed.png) + +### 3.6 并行任务安装 + +并行安装任务管理功能,实现集群任务创建,修改,删除,执行,复制,环境检查,任务日志下载等一系列功能 + +功能入口:在集群管理界面,并行安装任务页签中。并行安装任务包含两个下级页签:任务列表和草稿箱 + +#### 3.6.1 任务草稿创建 + +点击“创建并行安装任务”按钮,打开任务草稿创建页面,进行安装任务配 + +![1727355673047](./_resources/install_task_create_draft.png) + + + +#### 3.6.2 任务编辑 + +任务列表的操作栏,点击“更多” - "编辑" 链接,可重新编辑该任务。在草稿箱中,操作栏,可直接点击“编辑”链接对任务进行重新修改 + + + +![](_resources/installenpackage.png) + +![](_resources/installencluster.png) + + + +![](_resources/draftsubmit.png) + +#### 3.6.3 任务环境检查 + +当安装任务配置完成后,点击“下一步” 进入安装任务环境检查阶段,对该任务服务器以及任务配置进行相关环境检查 + +![](_resources/envchecksuccess.png) + +#### 3.6.4 任务确认 + +当安装任务通过环境检查后,对任务进行下一步的确认。确认后任务自动从草稿箱流转到任务列表中。进入待执行状态 + +![](./_resources/confirmationpage.png) + + + +#### 3.6.5 任务执行 + +通过任务列表中一键执行,或者批量执行,执行该任务。任务自动安装数据库 + +![](./_resources/onstepinstall.png) + +#### 3.6.6 任务执行日志下载 + +查看任务配置过程,安装信息等日志下载 + +![](./_resources/logdownload.png) + + + + + +#### 3.6.7 任务删除 + +删除任务草稿,或者已执行任务 + +![](./_resources/draftdelete.png) + + + +#### 3.6.8 任务复制 + +对当前已存在任务进行复制,并自动配置相关配置信息。 + +![](_resources/draftcopy.png) + + + +## 4 基础运维 + + ### 4.1 集群运维 + +左侧菜单栏点击【可观察运维】-【集群运维】进入该页面。 + +在这个页面上,用户可以查看所有集群信息,集群对应的机器信息,也可以对集群、实例进行运维操作。 + +![](_resources/048.png) + + #### 4.1.1 集群操作 + +集群运维的下图部分为集群操作,上面的任意操作都是针对整个集群的运维操作。 + +![](_resources/048.png) + +启动:点击后可启动该集群。 + +停止:点击后可停止该集群。 + +重启:点击后可重新启动该集群。 + +备份:点击后自动生成备份。 + +卸载:点击后可卸载该集群。 + +强制卸载:点击后可强制卸载该集群。会调用openGauss卸载命令,同时也会删除当前这条记录。 + +删除集群:只会删除当前这条记录,但是不会调用卸载命令。 + +![](_resources/049.png) + +点击任意操作按钮后弹出执行窗口执行,窗口可以进行缩放、拖动。 + +![](_resources/050.png) + + #### 4.1.2 实例操作 + +对于极简版和轻量版,实例操作有启动、停止、重启。 + +##### 4.1.2.1 主机上的实例操作 + +对于单个实例来说,选择主机点击操作,可进行【启动所有实例、停止所有实例】 + +![](_resources/051.png) + +##### 4.1.2.2 单独实例上的操作 + +对于单个实例来说,可以点击开关,开启关闭实例,也可以从右侧的操作下拉面板中选择对应的启停操作。 + +![](_resources/052.png) + +##### 4.1.2.3 企业版额外操作 + +对于企业版的节点,有额外的三个高级操作可以选择,分别是主备切换、生成配置、节点重建。其中主备切换和节点重建位于备节点上,生成配置在主备节点上都存在。 + +![](_resources/cluster-op-extra.png) + +- 主备切换:用来手动将备机切换为主机,该功能使用的命令为`gs_ctl switchover`。关于这个命令的详细文档请参照[实例主备切换 ](https://docs.opengauss.org/zh/docs/3.0.0/docs/Administratorguide/%E5%AE%9E%E4%BE%8B%E4%B8%BB%E5%A4%87%E5%88%87%E6%8D%A2.html)。**注意:一旦将备机转换为主机,可能导致集群中出现两个主节点的错误状态,此时可以参照此文档中的异常处理章节进行调整。** + +![](_resources/cluster-op-switch.png) + +- 生成配置:如果静态配置文件无意损坏后,会影响openGauss感知openGauss拓扑结构和主备关系。使用本功能,可以替换已经损坏的配置文件,保证openGauss的正常运行。使用的命令为`gs_om -t generateconf`,这个操作在主节点和备节点上均可以执行。关于这个命令的详细文档请参照[生成配置文件](https://docs.opengauss.org/zh/docs/3.0.0/docs/Administratorguide/%E7%94%9F%E6%88%90%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6.html) + +![](_resources/cluster-op-genconf.png) + +- 节点重建: 用来重建备机实例,使用的命令为`gs_ctl build`。关于这个命令的详细文档请参照[gs_ctl build](https://docs.opengauss.org/zh/docs/3.0.0/docs/Toolreference/gs_ctl.html#zh-cn_topic_0287275989_zh-cn_topic_0237152408_zh-cn_topic_0059777628_t22fb7e7152bf4c939f6316c48cb80b5b) + +![](_resources/cluster-op-build.png) + +#### 4.1.3 guc参数设置 + +对每个集群可以进行guc参数设置,点击任意实例上的guc参数设置按钮进行设置。 + +![](_resources/guc-setting-1.png) + +用户可在guc参数列表的上方进行参数名称检索,批量设置为默认值或批量还原所作的修改。对于企业版集群,还额外有【应用到所有实例】的选项,即把一个节点上的参数同步到所有数据库节点上。 + +![](_resources/guc-setting-2.png) + +对于不同的guc参数类型,有不同的界面控件与之对应,详细见下表。 + +| 参数类型 | 对应的控件类型 | +| -------------------- | ---------------------- | +| int64\|integer\|real | 数字输入并带有大小限制 | +| string | 文本输入 | +| enum | 下拉选择 | +| bool | 开关 | + +每条guc参数右侧都有【设为默认值】及【还原修改】两个操作按钮,可以针对该条guc参数进行对应设置。每个已被修改的guc参数名称旁边都会有一个已修改标记用来进行标识。 + +对于postmaster类型的guc参数,在保存时会提示集群需要重启,除此之外其它类型的guc参数则不会重启集群。 + +![](_resources/guc-setting-3.png) + + ### 4.2 集群监控 + +左侧菜单栏点击【可观测运维】-【集群监控】进入该页面。 + +页面上可以进行集群名称、主机IP的筛选,其中集群名称、主机IP在进入画面时均默认第一项。 + +![](_resources/monitor.png) + + +- #### CPU使用率 + +横轴:当前主机CPU使用率占比(%) +纵轴:当前时间格式,MM:SS + +![](_resources/053.png) +- #### 内存占用率 + +横轴:当前主机内存占用率(%) + +纵轴:当前时间格式,MM:SS + +![](_resources/054.png) +- #### 网络状态 + +横轴:当前主机网络状态(G) + +纵轴:当前时间格式,MM:SS + +![](_resources/055.png) +- #### 锁 + +横轴:当前主机锁(pcs) + +纵轴:当前时间格式,MM:SS + +![](_resources/056.png) +- #### 会话 + +横轴:当前主机会话(pcs) + +纵轴:当前时间格式,MM:SS + +![](_resources/057.png) +- #### 连接数 + +横轴:当前主机连接数(pcs) + +纵轴:当前时间格式,MM:SS + +![](_resources/058.png) +- #### 占用内存最多的会话TOP10 + +横轴为会话sessionID,纵轴为占用内存,单位为字节 + +按照占用内存大小倒序排列 + +![](_resources/059.png) + + + + ### 4.3 日志分析 +左侧菜单栏点击【可观测运维】-【日志分析】进入该页面。 + +在这个页面上用户可以查看与openGauss相关的各类日志并下载,包含系统日志、操作日志、审计日志、性能日志、黑匣子日志。 + +页面上可以进行集群名称、主机IP的筛选,其中集群名称、ip地址在进入画面时均默认为第一项。 + + #### 4.3.1 系统日志 +系统日志tab页面,默认显示当前集群对应主机的系统日志。 + +用户可以使用【..】回到上一层文件系统中,每一行文件后面的数字为文件的字节大小,用户可以点击下载按钮进行日志下载。 + +切换集群及IP地址后,页面刷新显示对应IP地址的系统日志信息。 + +![](_resources/060.png) + #### 4.3.2 操作日志 +操作日志tab页面,默认显示当前集群对应主机的操作日志,没有对应日志时显示为空白。 + +切换集群及IP地址后,页面刷新显示对应IP地址的操作日志信息。可点击下载按钮查看日志信息。 + +![](_resources/061.png) + #### 4.3.3 审计日志 +审计日志tab页面,默认显示当前集群对应主机的审计日志,没有对应日志时显示为空白。 + +支持日期时间筛选,切换集群及IP地址后,页面刷新显示对应IP地址的审计日志信息。可点击查看操作日志信息。 + +![](_resources/062.png) + + + + #### 4.3.4 性能日志 +性能日志tab页面,默认显示当前集群对应主机的性能日志,没有对应日志时显示为空白。 + +切换集群及IP地址后,页面刷新显示对应IP地址的性能日志信息。可点击查看、下载日志信息。 + +![](_resources/063.png) + + #### 4.3.5 黑匣子日志 +黑匣子日志tab页面,默认显示当前集群对应主机的黑匣子日志,没有对应日志时显示为空白。 + +切换集群及IP地址后,页面刷新显示对应IP地址的黑匣子日志信息。可点击查看、下载日志信息。 + +![](_resources/064.png) + + + + + + ### 4.4 WDR报告 +左侧菜单栏点击【可观测运维】-【WDR报告】进入该页面,列表显示集群对应的所有WDR报告信息。 + +用户可以通过集群名称、报告范围、报告类型、生产时间条件进行筛选。默认选择集群列表中的第一个。 + +![](_resources/065.png) + #### 4.4.1 查看报告 +在某条报告的后面点击【查看】按钮,浏览器将自动打开一个新的tab页面,显示该报告的内容,用户可以进行在线查看。 + +![](_resources/066.png) + #### 4.4.2 下载 +在某条报告的后面点击【下载】按钮,将下载该报告的HTML文件。 + +![](_resources/068.png) + #### 4.4.3 删除 +列表选择报告数据,操作项点击【删除】 + +删除确认框内点击【确定】,即可删除该报告。 + +![](_resources/067.png) + + #### 4.4.4 快照管理 + +WDR报告页面左上角,点击【快照管理】,弹出“快照管理”窗口。 + +在这个页面上,可以创建快照、筛选快照。 + +![](_resources/069.png) + +1. 筛选区域:集群名称、主机IP + +集群名称、主机IP:均默认选择第一项,即列表默认显示该集群主机IP下对应快照信息。 + +2. 创建快照: + +点击【创建快照】按钮,自动为当前集群生成快照信息。 + +![](_resources/070.png) + +#### 4.4.5 生成WDR报告 + +点击右上角的【生成WDR按钮】,弹出生成对话框,选择任意两个快照后点击【生成】按钮即可生成新的WDR报告。关于不同报告类型的详细区别,请查看openGauss官方文档:[例行维护-导出并查看wdr诊断报告](https://docs.opengauss.org/zh/docs/3.1.1/docs/Administratorguide/%E4%BE%8B%E8%A1%8C%E7%BB%B4%E6%8A%A4.html#%E5%AF%BC%E5%87%BA%E5%B9%B6%E6%9F%A5%E7%9C%8Bwdr%E8%AF%8A%E6%96%AD%E6%8A%A5%E5%91%8A)。 + +![](_resources/create-wdr.png) + + ### 4.5 SQL诊断 + +左侧菜单栏点击【可观测运维】-【SQL诊断】进入该页面,显示当前主机SQL诊断信息。 + +用户可以根据集群名称、主机IP、报告创建时间进行筛选。集群名称及主机IP默认选择第一项,时间控件默认选择当天。 + +![](_resources/071.png) + + ### 4.6 自定义控制台 +左侧菜单栏点击【可观测运维】-【自定义控制台】进入该页面。 + +用户可以切换主机IP进行不同机器上的操作。主机IP默认选择第一项,如果第一个主机没有选择保存密码,则立即需要输入root密码进行安全校验。 + +![](_resources/072.png) + +root密码校验成功后,自动连接该主机;连接成功后,控制台可直接输入命令执行。用户可以打开多个tab标签页,同时对多个主机进行操作。 + +![](_resources/073.png) + + ### 4.7 安装包管理 + +左侧菜单栏点击【可观测运维】-【安装包管理】进入该页面。 + +![](_resources/011.png) + + #### 4.7.1 新增安装包 + +点击【新增安装包】按钮,弹出新增安装包窗口,用户可以选择在线地址或离线安装包这两种至少一种作为安装来源,也可以同时两种都有。 + +上传安装包后将尝试自动解析所属系统、CPU架构、版本名称、版本号等,尝试失败的安装包,所属系统将被选择为通用系统all,CPU架构将被选择为通用架构noarch。 + +openGauss的在线下载地址将根据用户所选择系统、CPU架构、版本名称,输入版本号后将自动计算,默认为华为的官方下载地址。 + +目前支持的安装包类型为openGauss、Zookeeper、ShardingProxy、OpenLooKeng、DistributeDeploy五种。 + +![](_resources/012.png) + + #### 4.7.2 编辑 + +列表选择安装包,点击操作项【修改】,弹出“修改”窗口,即可编辑该安装包信息。点击删除安装包图标可以删除上传的离线安装包,一旦删除将无法恢复。 + +![](_resources/013.png) + + #### 4.7.3 删除 + +列表选择安装包,点击操作项【删除】,删除确认框内点击【确定】,即可删除该安装包记录及相关的离线安装包(如果有的话)。 + +![](_resources/014.png) + + + + + +版本修订记录 + + +| **序号** | **修改后版本号** | **生效日期** | **变更记录** | **更改人** | **审核** | **备注** | +| :------: | :--------------: | :----------: | :--------------------------: | :--------: | :------: | :------: | +| 1 | V1.0 | 2022-12-01 | | arkin | wyl | | +| 2 | V1.1 | 2023-02-20 | 增加新的截图,梳理了功能描述 | yangsen | wyl | | +| 3 | V1.2 | 2023-3-25 | 更新部分截图,增加新功能描述 | lhf | wyl | | +| | | | | | | | +| | | | | | | | diff --git a/ER-Model/plugins/base-ops/doc/DataKit Product Manual - Business Design.md b/ER-Model/plugins/base-ops/doc/DataKit Product Manual - Business Design.md new file mode 100644 index 000000000..a8795f577 --- /dev/null +++ b/ER-Model/plugins/base-ops/doc/DataKit Product Manual - Business Design.md @@ -0,0 +1,773 @@ +**数据建模产品使用手册** + + + + +# **文档历史** + +| **修订日期** | **修订内容** | **版本号** | **编写人** | +| :----------: | :------------: | :--------: | :--------: | +| 2022/12/10 | 初稿 | 1.00 | 贺帅 | +| 2023/1/13 | 修改MD版本 | 1.01 | 贺帅 | +| 2023/1/28 | 内容更新与补充 | 1.05 | 贺帅 | +| 2023/3/10 | 内容更新 | 1.06 | 贺帅 | + + +# **关于本手册** + +## **产品介绍** +基于WEB开发openGauss的数据流可视化编辑工具,实现零编程数据建模,通过拖拽、连接的方式完成简单的数据流设计。通过构建不同的数据流实例,并使用一体化平台中管理的数据库实例作为数据源,支持每一步节点的数据调试、生成图表及分享,可以快速方便的对各个业务库进行测试、数据采集、报表生成。 + +随着openGauss开源数据库的逐渐普及,有许多新用户可能由于语法差异、学习成本等原因放弃尝试。本特性希望让任何没有实际使用过openGauss的用户都可以通过平台快速完成数据操作体验,降低其上手难度,从而提升用户的兴趣度和深入探索的可能性。 + +## **主要功能介绍** +1. 可视化编辑,提供拖拽、连线的操作方式完成数据流算子的组装、配置,并根据算子组合生成对应的SQL及运行结果。 +1. 实时图表生成,支持读取单步算子执行结果为用户生成自定义图表,并可以记录快照。 +1. 可将一个或多个图表快照通过自由排布的方式构建报表,并可通过外链分享给用户。 + + +## **预期读者** +测试人员 + +开发人员 + +普通用户 + + + +## **版本及功能列表** + +| **版本号** | **新增功能** | **发布时间** | +| :--------: | :------------: | :----------: | +| V1.00 | 首次发布 | 2022/12/10 | +| V1.05 | 内容更新与补充 | 2023/1/28 | + + + +# **插件的安装** + +## **一体化平台插件管理页面** +### **概述** +可在本页面对数据建模插件进行安装、升级、卸载。 +### **角色** +拥有插件添加权限的角色可以访问。 +### **界面** +1、安装插件 + +![0](_resources/1.png) + +**说明:** + +1) 左侧下方点击插件管理菜单。 +1) 点击右上安装插件按钮。 + +2、选择安装包(通过编译打包脚本获得) + +![1](_resources/2.png) + +**说明:** + +1) 在弹出框中点击上传按钮。 +1) 选择插件包目录,选中插件包,上传完成后点击确定即可。 + +![2](_resources/3.png) + +**说明:** + +1. 等待安装完成后页面会自动刷新,可以在左侧菜单栏看到(1)处的菜单项,在(2)处看到base-ops插件卡片,即为安装成功。 + + + +## **物理机管理页面** +### **概述** +在本页面可以完成对物理机实例的新增、编辑、删除等操作,本功能属于一体化基座提供的基础功能,本插件依赖该服务所提供的实例,在使用本数据建模插件之前必须完成至少一个物理机实例的配置。 +### **角色** +拥有物理机实例管理权限的角色可以访问。 +### **界面** +1. 新增物理机 +## ![3](_resources/4.png) + +![3](_resources/5.png) + +**说明:** + +1.左侧菜单栏找到【资源中心】-【设备管理】菜单,点击打开管理页面。 + +2.点击新增物理机,并填写IP、端口等信息。 + +3.在新增好的物理机实例上点击用户管理,编辑root用户密码,并添加对openGauss数据库拥有访问权限的系统用户(如安装用户)。 + + + +## **集群安装页面** +### **概述** +在本页面可以完成对集群实例的安装、导入等操作,本功能属于一体化基座提供的基础功能,本插件依赖该服务所提供的实例,在使用本数据建模插件之前必须完成至少一个集群实例的配置。 +### **角色** +拥有集群管理权限的角色可以访问。 +### **界面** +1、导入已有集群实例 + +![4](_resources/6.png) + +**说明:** + +1.左侧菜单栏找到【安装部署】-【集群安装】菜单,点击打开安装详情页面。 + +2.本手册默认用户已有数据库实例(如需全新安装集群,请参考运维插件使用手册),此处安装模式选择【导入】,在下方选择对应的数据库版本。 + +3.点击下一步。 + + + +2、填写导入配置 + +![5](_resources/7.png) + +**说明:** + +1.在主机IP处选择之前新建的物理机实例,并输入该实例的root密码。 + +2.安装用户选择对该数据库拥有管理权限的用户,此处为之前添加的系统用户hs。 + +![5](_resources/8.png) + +3.依次点击后续下一步,直到导入完成。 + + + +## **数据流管理页面** + +### **概述** +在本页面可以完成对数据流实例的新增、编辑、删除等操作,支持名称模糊搜索,支持分页动态加载。 +### **角色** +拥有数据流设计权限的角色可以访问。 +### **界面** +1、新建数据流 + +![6](_resources/9.png) + +**说明:** + +1.左侧菜单栏找到【业务设计】-【数据流设计】菜单,点击打开管理页面。 + +2.点击上方新建数据流按钮。 + +![6](_resources/10.png) + +3.在弹出框中填写数据流名称,在默认数据库一栏中选择之前导入的数据库实例,点确认即可完成新建,页面将自动跳转到该业务流的详情页。 + + + + +## **数据流详情页面(算子菜单区)** +### **概述** +在本页面可以完成对数据流内算子的组装,运行,可视化等操作。本页面通过数据流管理页任意实例的【修改】按钮进入。 +### **角色** +拥有数据流设计权限的角色可以访问 +### **界面** +1、算子菜单 + +![9](_resources/11.png) + + + +**说明:** + +1.数据源算子(可选算子),如果一个算子序列的开头连接了该算子,则会使用数据源算子中配置的数据库连接来执行SQL,否则会使用数据流新建时配置的默认数据库连接。 + +2.基础算子,包含四大基础类型增删改查,一个有效算子序列有且只能有一个基础算子。 + +3.组合算子,在符合规则的前提下(规则详见后续算子配置篇)可以在基础算子下方连接任意数量的组合算子。 + + + +## **数据流详情页面(快捷操作区)** +### **概述** +快捷操作区主要提供对右侧绘制面板的便捷操作,如保存、放大、缩小、撤销、恢复、导入导出等功能。 +### **角色** +拥有数据流设计权限的角色可以访问 +### **界面** +1、快捷操作区 + +![12](_resources/12.png)**说明:** + + + +1.进入工作区后会定时执行自动保存,也可通过点击【保存按钮】手动保存。 + +2.选择任意算子后,点击【删除按钮】,即可删除该算子以及与之相连的箭头。 + +3.点击【撤销按钮】可撤销一步算子操作,如在新拖入一个算子后,点击撤销按钮,则该算子会被删除。 + +4.点击【恢复按钮】可将上次撤销操作恢复,如拖入一个算子后,通过撤销按钮将其删除后,再点击恢复按钮,则该算子会重新出现。 + +5.点击【放大按钮】、【缩小按钮】可对工作区视图比例进行放大缩小。。 + +5点击【居中按钮】会使工作焦点回到画布中心。 + +6点击【导出按钮】可以将完整的算子配置导出为json文件(该类型文件用于后续的导入功能使用),或导出为jpeg,png、svg图片。点击导出按钮后会出现类型选择弹框,再次点击即可从浏览器自动下载。 + + + +7.点击【导入按钮】可以将之前通过【导出按钮】导出的json文件上传并导入。 + +8.点击【预览代码按钮】可以在不运行数据流的情况下查看当前算子生成的对应SQL语句 + +9.点击【运行按钮】可以查看数据流真实运行的SQL、查询结果及返回数据(详细使用在后续章节说明) + +10.点击【可视化展示按钮】可以查看本数据流所有已生成图表快照,以及制作的报表内容(详细使用在后续章节说明) + + +## **数据流详情页面(算子绘制区)** +### **概述** +改区域用来组装各个拖入其中的算子,按用户所需绘制数据流结构。 +### **角色** +拥有数据流设计权限的角色可以访问 +### **界面** +1、算子工作区 + +![11](_resources/13.png) + +**说明:** + +1.每个算子通过四周4个圈点拖出箭头连接。 + +2.每个算子前面有个复选框,勾选之后会使得算子无效,但并不会删除该算子,可以用来进行一些临时调试。 + +3.每个算子上有两个按钮,分别为齿轮状的配置按钮,以及柱状图样式的可视化按钮。 + + + + +## **数据流详情页面(SQL运行及预览)** +### **概述** +该页面用来展示SQL查询结果,如果有多个查询结果,则通过多个tab标签的方式呈现。 +### **角色** +拥有数据流设计权限的角色可以访问。 +### **界面** +1. SQL运行界面![14](_resources/14.png) + + + +**说明:** + +1.配置好算子流之后,点击快捷操作栏中的【播放】按钮,可以看到SQL语句以及对应的查询结果。 + +2.(1)处为tab标签页,如果存在多个查询语句,则会分为多个标签点击切换。tab标签页的命名来源于该序列的基础算子的UUID,如查询算子,删除算子,该UUID为antv-X6组件随机生成。 + +3.(2)处为SQL语句预览区 + +4.(3)处为查询结果区 + + + +## **数据流算子配置篇** + +### **概述** + +本工具当前支持特殊算子1个,基础算子4个,组合算子8个,共计13个不同类型的算子。本章节将逐个介绍每个算子的配置说明,以及相互之间的连接、约束关系。 + +### **角色** + +拥有数据流设计权限的角色可以访问。 + +### **界面** + +1、特殊算子-数据源算子![14](_resources/ope-1.png) + + + +**说明:** + +1.数据源算子只有一个下拉框配置项,用于指定一体化平台中已经配置好的集群/节点/数据库/schema信息。 + +2.数据源算子可以指向多个基础算子,且只能指向基础算子,所有与之相连的基础算子,在执行增删改查等操作时,都会在该数据源算子配置的数据库中执行SQL。 + +3.数据源算子不是必要算子,若一个基础算子没有对应的数据源算子与之相连,则会使用新建数据流时配置的默认数据源进行SQL的执行。 + +4.如果修改一个已经连接使用的数据源算子,将有可能导致与之相连的后续所有算子不可用,比如:修改数据源后,新的数据库中不存在之前数据库中的表名,字段等。所以在修改数据源算子时,将会对用户进行二次确认的提示,并需要用户自行检查后续算子的配置是否可用。 + + + +2、基础算子-查询算子![14](_resources/ope-2.png) + + + +**说明:** + +1.查询算子的配置包含一个必选下拉框-表名,用户可以从下拉框中选择数据源中包含的所有表名。 + +2.查询算子可以用过点击加号配置提取字段,效果等同于`SELECT goods_name , brand_type , goods_model from modeling_test_data_sales_list` ,如果不配置提取字段,则默认会提取所有字段,效果等同于`SELECT * from modeling_test_data_sales_list` 。 + +3.查询算子下方可以连接多个组合算子,但是不可以连接其他基础算子。后续描述中【基础算子为查询算子的序列】即以查询算子作为某条算子流的开始的一系列算子组合。 + + + +3、基础算子-更新算子![14](_resources/ope-3.png) + +![14](_resources/ope-3A.png) + +**说明:** + +1.更新算子的配置包含一个必选下拉框-表名,用户可以从下拉框中选择数据源中包含的所有表名。 + +2.更新算子可以用过点击加号配置一个或多个更新字段,更新方式有两种,第一种默认为自定义语句,即将指定字段的值更新为输入框内的内容。另一种为字段模式,可以使用表内其他字段来进行更新操作,通过选择后置字段后方的下拉框(原值、拼接)可以将后置字段的原值覆盖或拼接更新到目标字段上。 + +3.更新算子一般需要搭配条件算子使用,如果更新算子不连接任何条件算子,则会对整表进行更新。 + +4.更新算子下方可以连接多个组合算子,但是不可以连接其他基础算子。后续描述中【基础算子为更新算子的序列】即以更新算子作为某条算子流的开始的一系列算子组合。 + + + +4、基础算子-删除算子![14](_resources/ope-4.png) + + + +**说明:** + +1.删除算子的配置包含一个必选下拉框-表名,用户可以从下拉框中选择数据源中包含的所有表名。 + +2.删除算子一般需要搭配条件算子使用,如果更新算子不连接任何条件算子,则会删除整张表数据。 + +3.删除算子下方可以连接多个组合算子,但是不可以连接其他基础算子。后续描述中【基础算子为删除算子的序列】即以删除算子作为某条算子流的开始的一系列算子组合。 + + + +5、基础算子-插入算子![14](_resources/ope-5.png) + + + +**说明:** + +1.插入算子的配置包含一个必选下拉框-表名,用户可以从下拉框中选择数据源中包含的所有表名。 + +2.插入算子可以通过点击【添加一条】按钮,添加多条插入数据。 + +3.插入算子下方可以连接多个组合算子,但是不可以连接其他基础算子。后续描述中【基础算子为插入算子的序列】即以插入算子作为某条算子流的开始的一系列算子组合。 + + + +6、组合算子-条件算子![14](_resources/ope-6.png) + + + +**说明:** + +1.条件算子的配置至少需要包含一个或条件,每个或条件内至少包含一个或多个且条件。 + +2.每个条件由3个部分组成,字段、条件类型、值。其中条件类型和值为用户手动选择或输入,字段将从条件算子上下游连接的算子中获取所有表的字段,举例:在查询算子-条件算子-桥接算子这样的连接序列中,查询算子选择的查询表为USER,而桥接算子将ROLE表桥接进来,那么在条件算子中,可以选择的字段将同时包含USER表和ROLE表的所有字段。 + +3.条件算子可以同时连接多个,并会同时生效。 + + + +7、组合算子-桥接算子![14](_resources/ope-7.png) + +![14](_resources/ope-7A.png) + + + + + + + +**说明:** + +1.桥接算子的配置包含需要桥接的表名,用户可以从下拉框中选择数据源中包含的所有表名。 + +2.桥接算子需要选择桥接类型,桥接类型包含内连接,左外连接,右外连接,全外连接,桥接条件。分别对应`join,left join,right join,full join` 四种语法。 + +3.桥接条件的选择由3个下拉框组成,第一个为基础算子中选择的表字段,第二个为条件类型一般选择等于即可,第三个为桥接表中的字段。如图示中配置将会得到如下的join语句:`left join goods_type on ( modeling_test_data_sales_list.brand_type = goods_type.type )` + +4.桥接算子可以同时连接多个,并会同时生效。 + +5.连接桥接算子后,会使得其他组合算子在选择配置字段时,可以使用桥接表中的字段,如条件算子。在图示2中可以看到,使用桥接算子后,后续连接的条件算子中选择字段的下拉框(1)处出现了桥接表的表名(灰色字体),在表名下方的(2)处即为桥接表拥有的字段。 + + + +8、组合算子-排序算子![14](_resources/ope-8.png) + + + +**说明:** + +1.排序算子的配置至少需要包含一个或多个字段排序设置。 + +2.每个排序条件由2个部分组成,字段、排序类型。字段将从算子序列中包含的所有字段中选择,排序类型分为三种,升序,降序,中文拼音降序。 + +3.其中中文拼音降序选项需要数据库有对应的格式支持,具体请查阅openGauss使用手册。 + +4.排序算子可以同时连接多个,并会同时生效。 + + + +9、组合算子-限条算子![14](_resources/ope-9.png) + + + +**说明:** + +1.限条算子的配置由两个输入框组成,跳过前N条记录,以及只提取前M条记录。 + +2.如图示配置得到的语法结果如下:`select * from modeling_test_data_sales_list limit 1000 offset 100;` + + + +10、组合算子-分组算子和聚合算子![14](_resources/ope-10A.png) + +![14](_resources/ope-10B.png) + + + + + +**说明:** + +1.分组算子和聚合算子都为组合算子,这两种算子需要搭配使用,所以放在一起说明。 + +2.分组算子需要至少配置一个或多个字段对数据进行分组,即GROUP BY语法,每个聚合字段的配置包含三个部分,字段名、聚合类型、别名。 + +3.在分组算子后使用聚合算子,可以对分组后的数据中的其他字段进行聚合函数操作,目前支持三种类型的聚合,求和、最大值、平均值,分别对应语法`SUM(),MAX(),AVG()` + +4.分组算子和聚合算子应该在基础算子为查询算子的序列中使用。 + + + +11、组合算子-映射算子![14](_resources/ope-11.png) + + + +**说明:** + +1.映射算子的配置至少包含一条或多条映射规则,每条规则由3部分组成,原字段,映射类型(目前只有as),映射别名。 + +2.映射算子只在基础算子为查询算子的序列中生效。 + +3.如上图配置得到的语法结果如下:`select goods_name as g_name` ... + + + +12、组合算子-字典算子![14](_resources/ope-12.png) + + + +**说明:** + +1.字典算子的配置包含4个下拉框选择,分别为字典表表名,原字段,匹配字段,转义字段。 + +2.举例说明,字典表goods_type中包含了每种商品名称(name)、商品类型(type),而主查询的结果中只包含了商品名称(goods_name),在这种情况下我们想要查询结果中出现的是商品类型,而不是商品名称,那么我们就可以使用字典算子来完成字典转义的功能。我们在字典算子中首先选择字典表的表名(goods_type),然后选择需要替换掉的原表字段(goods_name),然后在匹配字段下拉框中选择字典表中与(goods_name)一一对应的字段(name),到此已经完成字典表和原字段的匹配,最后在转义字段中选择需要从字典表中用来替换的字段即可,这里我们选择商品类型(type)。 + +3.字典算子的转义过程由left join语法完成,以上配置结果得到的完整语句如下:`select *,"goods_type".type as goods_name from modeling_test_data_sales_list left join "goods_type" on "goods_type".name = goods_name ;` + + + + +## **可视化图表新增** +### **概述** +在本页面可以对执行到该算子的查询结果进行可视化展示,可以通过配置一个或多个图表信息,支持的图表类型有:柱状图、折线图、混合图、热力图、饼图、地理图、关系图。 +### **角色** +拥有数据流设计权限的角色可以访问。 +### **界面** +1、新建图表 + +![15](_resources/15A.png) + +![15](_resources/15.png) + +**说明:** + +1.点击任意算子上的柱状图按钮,可以进入该算子的独立可视化配置界面。 + +2.在(1)处点击新建图表弹出可以新建的所有图表类型,选择任意一个即可开始配置可视化参数,每个算子可以新增多个图表。 + + + + + + + +## **可视化图表配置页面** +### **概述** +在本页面可以对执行到该算子的可视化图表进行配置,对同一组查询数据根据不同的参数会得到完全不同的图表。本章节将对柱状图、折线图、混合图、热力图、饼图、地理图、关系图的配置一一进行说明。 +### **角色** +拥有数据流设计权限的角色可以访问。 +### **界面** + +本章节使用测试数据表结构和部分内容如下: + +| area | goods_name | quantity | year | created_at | tax_rate | price_unit | goods_model | salesman | id | present | department | brand_type | price_total | +| ---- | ---------------------------------------------------- | -------- | ---- | ----------------------------- | -------- | ---------- | ------------------------------------------------------ | -------- | ---- | ------- | -------------- | ---------- | ----------- | +| 河南 | UPS电源\|C6KS\|2小时 | 1 | 2021 | 2019-01-02T17:06:43.000+08:00 | 16% | 19000 | UPS电源 \|C6KS\|2小时 | 史宗宗 | 428 | 销售品 | 业务部二 | 华为 | 19000 | +| 河南 | 服务器\|ThinkSe rver RS260 | 1 | 2021 | 2019-01-03T10:13:43.000+08:00 | 16% | 7950 | 服务器 \|ThinkSe rver RS260 | 史宗宗 | 443 | 销售品 | 业务部二 | 华为 | 7950 | +| 河南 | 超6类屏蔽网线\|千兆屏蔽网线\|RJ45型屏蔽网线插头\|5米 | 3 | 2021 | 2019-01-03T10:57:08.000+08:00 | 16% | 29.74 | 超6类屏蔽网线\|千兆屏蔽网线 \|RJ45型屏蔽网线 插头\|5米 | 史宗宗 | 445 | 销售品 | 业务部二 | 华为 | 89.22 | +| 河南 | 粉盒 | 1 | 2021 | 2019-01-03T13:16:48.000+08:00 | 16% | 1040 | 3503 | 马曙光 | 200 | 销售品 | ZD政教事业一部 | 理光 | 1040 | + +某项图表的绘制需要特定类型的字段内容,将在后续说明,如无特殊说明则任意查询结果都可以正常使用绘制;各类图表的展示有其侧重点,但没有办法像excel一样将全部数据展示,需根据实际情况选择图表类型及修改配置项使用。 + + + +1、柱状图配置-示例1 + +![16](_resources/chart-bar-1.png) + +2、柱状图配置-示例2 + +![16](_resources/chart-bar-2.png) + +3、柱状图配置-示例3 + +![16](_resources/chart-bar-3.png) + +4、柱状图配置-示例4 + +![16](_resources/chart-bar-4.png) + +**说明:** + +1.X轴配置需要选择一个字段作为X轴下标的分布,可理解为对数据按某字段进行分组,如分类统计不同品牌的销量,则应该在X轴选择品牌字段(brand_type)。 + +2.Y轴配置中需要选择一个字段,作为柱体高度值的来源,可以从查询结果中类型为整型、浮点型的字段中选择,如想统计不同品牌的销售金额总价,则在Y轴配置字段选择商品总价(price_total)。 + +3.统计维度,在X轴分组的基础之上,再次进行多个维度的分组可在此处配置,维度字段为必填项,每个维度可以设置只取前N条,避免条目过多展示效果不好。如在X轴选择了品牌字段(brand_type)后,还想在看到不同品牌内赠品和非赠品的销售情况差异,则可以在统计维度添加一条,选择是否赠品字段(present)。 + +4.自定义维度:可以通过自定义配置,对某个字段的所有枚举值进行分类,并形成一个新的统计维度,该统计维度使用方法与常规字段一致,用户完成新增自定义维度并保存后,则可以在该算子中任意图表使用。 + +5.点击预览按钮后,会出现生成快照按钮,点击则会保存一份独立图表快照信息到历史记录中。 + +**举例:** + +示例1-最简配置只需要选择X轴字段,Y轴字段,如示例1中,只需进行最简配置即可得到以品牌(brand_type)为X轴,以销售总价(price_total)为Y轴的二维数据展示。 + +示例2-当最简单的X轴配置不能满足我们需求时,可以通过添加统计维度来增加X轴分布的粒度,如示例2中,X轴我们选择了area进行不同地区销售情况的统计,然后再统计维度中我们新增了一项present(是否赠品),这样我们生成的柱状图中,每个X轴下标都会包含多组不同颜色的柱体,即得到了不同地区中,赠品和非赠品的销售情况。 + +示例3-在图示2的基础上,如果需要更加细的统计粒度,可以再继续添加统计维度,示例3中我们又添加了一个department(部门)维度,可以看到图表的结果中展现了更多的不同颜色柱体,分别对应了不同部门的赠品、非赠品销售情况。 + +示例4-在X轴配置中可以选择按时间维度分布,该类型的X轴字段需要满足通用日期格式YYYY-MM-DD,图表会根据选择的时间粒度(年、月、日)进行图表X轴下标的绘制。 + + + +5、折线图配置-示例1 + +![16](_resources/chart-line-1.png) + +6、折线图配置-示例2 + +![16](_resources/chart-line-2.png) + +7、折线图配置-示例3 + +![16](_resources/chart-line-3.png) + +8、折线图配置-示例4 + +![16](_resources/chart-line-4.png) + +**说明:** + +1.X轴配置需要选择一个字段作为X轴下标的分布,可理解为对数据按某字段进行分组,如分类统计不同品牌的销量,则应该在X轴选择品牌字段(brand_type)。 + +2.Y轴配置中需要选择一个字段,作为折线高度值的来源,可以从查询结果中类型为整型、浮点型的字段中选择。如想统计不同品牌的销售金额总价,则在Y轴配置字段选择商品总价(price_total)。 + +3.统计维度,在X轴分组的基础之上,再次进行多个维度的分组可在此处配置,维度字段为必填项,每个维度可以设置只取前N条,避免条目过多展示效果不佳。如在X轴选择了地区字段(area)后,还想在看到不同品牌内赠品和非赠品的销售情况差异,则可以在统计维度添加一条,选择是否赠品字段(present)。 + +4.自定义维度:可以通过自定义配置,对某个字段的所有枚举值进行分类,并形成一个新的统计维度,该统计维度使用方法与常规字段一致,用户完成新增自定义维度并保存后,则可以在该算子中任意图表使用。 + +5.点击预览按钮后,会出现生成快照按钮,点击则会保存一份独立图表快照信息到历史记录中。 + +**举例:** + +示例1-最简配置只需要选择X轴字段,Y轴字段,如示例1中,只需进行最简配置即可得到以品牌(brand_type)为X轴,以销售总价(price_total)为Y轴的二维数据展示。 + +示例2-当最简单的X轴配置不能满足我们需求时,可以通过添加统计维度来增加X轴分布的粒度,如示例2中,X轴我们选择了area进行不同地区销售情况的统计,然后再统计维度中我们新增了一项present(是否赠品),这样我们生成的折线图中,每个X轴下标都会包含多组不同颜色的折线,即得到了不同地区中,赠品和非赠品的销售情况。 + +示例3-在图示2的基础上,如果需要更加细的统计粒度,可以再继续添加统计维度,示例3中我们又添加了一个department(部门)维度,可以看到图表的结果中展现了更多的不同颜色折线,分别对应了不同部门的赠品、非赠品销售情况。 + +示例4-在X轴配置中可以选择按时间维度分布,该类型的X轴字段需要满足通用日期格式YYYY-MM-DD,图表会根据选择的时间粒度(年、月、日)进行图表X轴下标的绘制。 + + + +9、混合图配置-示例1 + +![16](_resources/chart-mix-1.png) + + + +**说明:** + +1.混合图的配置是通过添加多个副图(副图类型只能是柱状图或折线图),通过统一的X轴配置,将多个副图的Y轴内容叠加显示。 + +2.首先需要配置X轴维度,配置方式与前述柱状图和折线图的方式一致。 + +3.接着需要点击【添加副图】按钮,增加一个或多个副图,每个副图需要选择图表类型(柱状图,折线图),剩下的Y轴配置与统计维度配置,与与前述柱状图和折线图的方式一致。 + +4.自定义维度:可以通过自定义配置,对某个字段的所有枚举值进行分类,并形成一个新的统计维度,该统计维度使用方法与常规字段一致,用户完成新增自定义维度并保存后,则可以在该算子中任意图表使用。 + +5.点击预览按钮后,会出现生成快照按钮,点击则会保存一份独立图表快照信息到历史记录中。 + +**举例:** + +示例1-在该示例中,我们选择区域(area)字段作为X轴下标,然后点击【添加副图】按钮添加两个副图,并将第一个副图设置为折线图,Y轴为商品总价(price_total),统计维度添加一条品牌(brand_type),第二个副图设置为柱状图,Y轴同样配置为商品总价(price_total),如果此处配置的Y轴字段与其他副图不同,则在绘制的时候会出现多个Y坐标系,此处需要注意。统计维度我们选择是否赠品(present)。通过如上配置,我们可以得到的图表呈现如图所示,是两个独立的折线图和柱状图叠加显示在同一个坐标系中。 + + + +10、饼图配置-示例1 + +![16](_resources/chart-pie-1.png) + +11、饼图配置-示例2 + +![16](_resources/chart-pie-2.png) + + + +**说明:** + +1.饼图的配置包含两个必填属性,统计指标和统计维度,统计维度是将饼图按照某个字段进行分组,统计指标是用来计算每个维度所占百分比的求和字段。 + +2 .饼图还可选择内部嵌套一个维度展示,通过配置【展现方式】-【嵌套维度】使用,此为选填项,配置之后会在饼图内部镂空并绘制一个独立的饼图。 + +3.自定义维度:可以通过自定义配置,对某个字段的所有枚举值进行分类,并形成一个新的统计维度,该统计维度使用方法与常规字段一致,用户完成新增自定义维度并保存后,则可以在该算子中任意图表使用。 + +4.点击预览按钮后,会出现生成快照按钮,点击则会保存一份独立图表快照信息到历史记录中。 + +**举例:** + +示例1-在简单配置的情况下,我们只选择统计维度为业务员(salesman),统计指标为销售总价(price_total),我们可以得到一个简单的饼图,它将展示每个业务员的销售金额(求和)在同一个饼图上,所占百分比的不同。 + +示例2-在示例1的基础之上,如果我们想看到另一个维度的统计分布,比如该销售批次中,赠品和非赠品的占比,我们可以选择使用嵌套维度的方式,选择字段是否赠品(present),则可以看到我们的饼图中间出现一个独立的内嵌饼图,它将展示赠品和非赠品的统计占比。 + + + +12、热力图配置-示例1 + +![16](_resources/chart-heat-1.png) + + + + + +**说明:** + +1.热力图配置包含三个主要配置项,X轴字段,Y轴字段,统计指标字段。 + +2.统计指标中需要选择一个字段,作为热力值计算的来源(目前只支持【求和】方式统计),可以从查询结果中类型为整型、浮点型的字段中选择。 + +3.X轴字段与Y轴字段即对应热力图横纵坐标系的下标 。 + +4.热力范围是选填项,主要根据实际数据范围来调整,使得热力图颜色分布更合理。 + +5.自定义维度:可以通过自定义配置,对某个字段的所有枚举值进行分类,并形成一个新的统计维度,该统计维度使用方法与常规字段一致,用户完成新增自定义维度并保存后,则可以在该算子中任意图表使用。 + +6.点击预览按钮后,会出现生成快照按钮,点击则会保存一份独立图表快照信息到历史记录中。 + +**举例:** + +示例1-在示例中我们选择数量(quantity)作为统计指标,X轴我们选择品牌(brand_type),Y轴数据选择销售区域(area),我们可以得到一张热力图呈现,各区域对不同品牌的销售热度。 + + + +13、地理图配置-上传地图geo文件 + +![16](_resources/chart-map-1.png) + +14、地理图配置-示例1 + +![16](_resources/chart-map-2.png) + + + +**说明:** + +1.地理关系图的配置首选需要准备对应地区的geo地图文件(json格式),比如中国的省份地图(https://geojson.cn/api/data/china.json),在图1中点击(1)处调出上传弹窗,在(2)处输入地图别名,并选择下载好的geo文件点击上传即可,在同一个数据流中上传过的geo文件可以被其他算子重复使用,无需再次上传。 + +2.统计指标中需要选择一个字段,作为地图上每个区域统计值累加的来源(目前只支持【求和】方式统计),可以从查询结果中类型为整型、浮点型的字段中选择。 + +3 .在图2中,地理位置配置项的(1)处,需要选择一个地理名称字段,并且要求该字段内容与geo地图文件中的区域名称一致,如:商品销售区域(area)字段中存储的为省份信息的中文全称(山东、天津、广东、广西等),geo文件信息中也需要包含这些名称的省份的经纬度等信息。 + +4.在图2中,地理位置配置项的(2)处【经纬度偏移】【缩放比例】是可选配置,用来调整不同地图的偏移,缩放比例等呈现信息,如不配置也可通过鼠标滚轮,鼠标拖拽来做后续调整。【高亮前N条】选项则是将数值最大的统计区域做一个颜色的高亮区分。 + +5.在图2中,地理位置配置项的(3)处是必选配置,此处可选项即为第一步我们上传的geo文件,在下拉框中显示内容为上传时输入的地图名而非文件名。 + +6.在图2中,地理位置配置项的(4)处是选配配置,此处可更改左下角热力值色带的高,中,低颜色区域值。 + +7.点击预览按钮后,会出现生成快照按钮,点击则会保存一份独立图表快照信息到历史记录中。 + + + +**举例:** + +示例1-在本示例中我们选择使用的是中国省份地图,并且确认我们的测试数据中包含省份信息(area),统计指标我们选择销售总价(price_total),坐标系我们选择刚刚上传的地图(cc),通过调整偏移和缩放比例(非必须)得到在中国不同省份中,销售情况的分布,并且可以看到销售量最高的前5个区域颜色是处于橙色状态。 + + + +15、关系图配置-示例1 + +关系图所使用的查询数据结果应该至少包含如下结构: + +| node_type | size | node_name | x | y | from | id | to | category | value | node_id | +| --------- | ---- | --------- | ---- | ---- | ---- | ---- | ---- | -------- | ----- | ------- | +| A | 10 | 接口001 | 200 | 300 | n1 | 65 | n4 | 1 | 0 | n1 | +| A | 10 | 接口001 | 200 | 500 | n6 | 1122 | n4 | 5 | 59.51 | n6 | +| A | 10 | 接口002 | 20 | 3 | n1 | 383 | n2 | 1 | 195 | n11 | +| A | 10 | 接口003 | 20 | 3 | n1 | 335 | n2 | 6 | 300 | n13 | +| A | 10 | 接口005 | 20 | 50 | n1 | 58 | n2 | 3 | 345 | n15 | +| A | 10 | 接口006 | 20 | 50 | n1 | 132 | n2 | 1 | 530 | n16 | +| A | 10 | 接口007 | 200 | 500 | n7 | 445 | n13 | 3 | 89.22 | n7 | +| A | 10 | 接口007 | 200 | 500 | n8 | 52 | n13 | 5 | 100 | n8 | +| A | 10 | 接口007 | 200 | 500 | n9 | 375 | n13 | 1 | 135 | n9 | + +![16](_resources/chart-relations-1.png) + + + +**说明:** + +1.关系图的配置主要分为两部分,节点配置和关系配置。其中节点配置中的信息用来绘制图表中每个点的相对位置、名称、尺寸信息。关系配置用来绘制节点之间连线的信息,粗细程度。 + +2.使用关系图需保证查询结果中包含所有对应的字段。 + +3.点击预览按钮后,会出现生成快照按钮,点击则会保存一份独立图表快照信息到历史记录中。 + + + +**举例:** + +示例1-如图示1中将每项配置对应的字段选择好之后,工具会根据数据中对应的值直接绘制相应的关系图,该绘制过程属于直接绘制,工具不做额外处理。 + + + + + +## **可视化图表快照管理** +### **概述** +在本页面可以对前述章节生成的快照进行预览管理,快照的生成方式为:任意图表配置完成后,点击预览按钮后,会在左侧绘制图表同时在右侧下方出现【生成快照按钮】,点击则会保存一份独立图表快照信息到快照管理列表中,后续可使用它们生成多个不同的报表来进行分享展示。 +### **角色** +拥有数据流设计权限的角色可以访问。 +### **界面** +1. 可视化展示 + + + + ![图示1](_resources/17.png)![图示2](_resources/18.png) + +![图示3](_resources/19.png)![图示4](_resources/20.png) + +![图示5](_resources/21.png)![图示6](_resources/22.png) + +**说明:** + +1. 如图示1在工作区快捷操作栏最后找到可视化按钮,点击即可打开可视化页面。 + +2. 进入图示2页面默认显示本数据流所生成的所有快照预览。 + +3. 在图示2页面点击(2)处tab切换即可进入报表管理页面。 + +4. 图示3页面中点击新建报表按钮开始绘制一个报表。 + +5. 在图示4中,右侧快照拾取区中,【鼠标单击】需要使用的快照,则会自动进入左侧绘制区。 + +6. 在左侧绘制区中调整快照的位置,大小等信息后点击保存按钮。 + +7. 在报表管理页面点击分享会自动生成分享链接。 + +8. 任意用户访问该链接即可看到绘制的报表内容。 diff --git a/ER-Model/plugins/base-ops/doc/_resources/001.png b/ER-Model/plugins/base-ops/doc/_resources/001.png new file mode 100644 index 000000000..0cff3b670 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/001.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/002.png b/ER-Model/plugins/base-ops/doc/_resources/002.png new file mode 100644 index 000000000..c2a349714 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/002.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/003.png b/ER-Model/plugins/base-ops/doc/_resources/003.png new file mode 100644 index 000000000..f5195b71d Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/003.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/004.png b/ER-Model/plugins/base-ops/doc/_resources/004.png new file mode 100644 index 000000000..08bc2d9b2 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/004.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/005.png b/ER-Model/plugins/base-ops/doc/_resources/005.png new file mode 100644 index 000000000..cb194676e Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/005.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/006.png b/ER-Model/plugins/base-ops/doc/_resources/006.png new file mode 100644 index 000000000..d02872420 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/006.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/007.jpg b/ER-Model/plugins/base-ops/doc/_resources/007.jpg new file mode 100644 index 000000000..8ad37b393 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/007.jpg differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/007.png b/ER-Model/plugins/base-ops/doc/_resources/007.png new file mode 100644 index 000000000..7d48ed2eb Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/007.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/008.png b/ER-Model/plugins/base-ops/doc/_resources/008.png new file mode 100644 index 000000000..bbb5b7347 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/008.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/009.png b/ER-Model/plugins/base-ops/doc/_resources/009.png new file mode 100644 index 000000000..735c9db64 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/009.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/010.png b/ER-Model/plugins/base-ops/doc/_resources/010.png new file mode 100644 index 000000000..63bfabb4c Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/010.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/011.png b/ER-Model/plugins/base-ops/doc/_resources/011.png new file mode 100644 index 000000000..0651e38a7 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/011.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/012.png b/ER-Model/plugins/base-ops/doc/_resources/012.png new file mode 100644 index 000000000..9e2cf6688 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/012.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/013.png b/ER-Model/plugins/base-ops/doc/_resources/013.png new file mode 100644 index 000000000..779d62050 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/013.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/014.png b/ER-Model/plugins/base-ops/doc/_resources/014.png new file mode 100644 index 000000000..32ab4f6c8 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/014.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/015.png b/ER-Model/plugins/base-ops/doc/_resources/015.png new file mode 100644 index 000000000..33dc52b1e Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/015.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/016.png b/ER-Model/plugins/base-ops/doc/_resources/016.png new file mode 100644 index 000000000..a952c4964 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/016.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/017.png b/ER-Model/plugins/base-ops/doc/_resources/017.png new file mode 100644 index 000000000..092a31fb4 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/017.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/018.png b/ER-Model/plugins/base-ops/doc/_resources/018.png new file mode 100644 index 000000000..986c97d1a Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/018.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/019.png b/ER-Model/plugins/base-ops/doc/_resources/019.png new file mode 100644 index 000000000..10d2ccfd2 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/019.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/020.png b/ER-Model/plugins/base-ops/doc/_resources/020.png new file mode 100644 index 000000000..828d16d8e Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/020.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/021.png b/ER-Model/plugins/base-ops/doc/_resources/021.png new file mode 100644 index 000000000..6ba06ea55 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/021.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/022.png b/ER-Model/plugins/base-ops/doc/_resources/022.png new file mode 100644 index 000000000..87201dd98 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/022.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/023.png b/ER-Model/plugins/base-ops/doc/_resources/023.png new file mode 100644 index 000000000..d6bfaa5c2 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/023.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/024.png b/ER-Model/plugins/base-ops/doc/_resources/024.png new file mode 100644 index 000000000..8b24c322f Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/024.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/025.png b/ER-Model/plugins/base-ops/doc/_resources/025.png new file mode 100644 index 000000000..1bd3e3a67 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/025.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/026.jpg b/ER-Model/plugins/base-ops/doc/_resources/026.jpg new file mode 100644 index 000000000..4af0dc42b Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/026.jpg differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/026.png b/ER-Model/plugins/base-ops/doc/_resources/026.png new file mode 100644 index 000000000..8029ade83 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/026.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/027.png b/ER-Model/plugins/base-ops/doc/_resources/027.png new file mode 100644 index 000000000..1177ef577 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/027.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/028.png b/ER-Model/plugins/base-ops/doc/_resources/028.png new file mode 100644 index 000000000..ec10f6f56 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/028.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/029.png b/ER-Model/plugins/base-ops/doc/_resources/029.png new file mode 100644 index 000000000..140f15933 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/029.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/030.png b/ER-Model/plugins/base-ops/doc/_resources/030.png new file mode 100644 index 000000000..bb6c331d2 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/030.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/031.png b/ER-Model/plugins/base-ops/doc/_resources/031.png new file mode 100644 index 000000000..6a1d5cf80 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/031.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/032.png b/ER-Model/plugins/base-ops/doc/_resources/032.png new file mode 100644 index 000000000..03af50358 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/032.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/033.png b/ER-Model/plugins/base-ops/doc/_resources/033.png new file mode 100644 index 000000000..ea2395e6b Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/033.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/034.png b/ER-Model/plugins/base-ops/doc/_resources/034.png new file mode 100644 index 000000000..bdeb875fd Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/034.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/035.png b/ER-Model/plugins/base-ops/doc/_resources/035.png new file mode 100644 index 000000000..ab340b3fc Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/035.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/036.png b/ER-Model/plugins/base-ops/doc/_resources/036.png new file mode 100644 index 000000000..c0198bd7e Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/036.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/037.png b/ER-Model/plugins/base-ops/doc/_resources/037.png new file mode 100644 index 000000000..16b3c0e39 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/037.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/038.png b/ER-Model/plugins/base-ops/doc/_resources/038.png new file mode 100644 index 000000000..24020f4c3 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/038.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/039.png b/ER-Model/plugins/base-ops/doc/_resources/039.png new file mode 100644 index 000000000..0b192af29 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/039.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/040.png b/ER-Model/plugins/base-ops/doc/_resources/040.png new file mode 100644 index 000000000..0bf8c37a0 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/040.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/041.png b/ER-Model/plugins/base-ops/doc/_resources/041.png new file mode 100644 index 000000000..8a4e724b6 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/041.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/042.png b/ER-Model/plugins/base-ops/doc/_resources/042.png new file mode 100644 index 000000000..e3e859c21 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/042.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/043.png b/ER-Model/plugins/base-ops/doc/_resources/043.png new file mode 100644 index 000000000..b660b6ebd Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/043.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/044.png b/ER-Model/plugins/base-ops/doc/_resources/044.png new file mode 100644 index 000000000..75f21f2f4 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/044.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/045.png b/ER-Model/plugins/base-ops/doc/_resources/045.png new file mode 100644 index 000000000..e0ad641dc Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/045.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/046.png b/ER-Model/plugins/base-ops/doc/_resources/046.png new file mode 100644 index 000000000..be2725af1 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/046.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/047.png b/ER-Model/plugins/base-ops/doc/_resources/047.png new file mode 100644 index 000000000..868747e91 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/047.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/048.png b/ER-Model/plugins/base-ops/doc/_resources/048.png new file mode 100644 index 000000000..e011b812a Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/048.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/049.png b/ER-Model/plugins/base-ops/doc/_resources/049.png new file mode 100644 index 000000000..f90eadf88 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/049.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/050.jpg b/ER-Model/plugins/base-ops/doc/_resources/050.jpg new file mode 100644 index 000000000..d80502ead Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/050.jpg differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/050.png b/ER-Model/plugins/base-ops/doc/_resources/050.png new file mode 100644 index 000000000..83ebdb78b Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/050.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/051.png b/ER-Model/plugins/base-ops/doc/_resources/051.png new file mode 100644 index 000000000..979429f00 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/051.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/052.png b/ER-Model/plugins/base-ops/doc/_resources/052.png new file mode 100644 index 000000000..f3dab6ee3 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/052.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/053.png b/ER-Model/plugins/base-ops/doc/_resources/053.png new file mode 100644 index 000000000..4d54c9f1f Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/053.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/054.png b/ER-Model/plugins/base-ops/doc/_resources/054.png new file mode 100644 index 000000000..73b5fb6a2 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/054.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/055.png b/ER-Model/plugins/base-ops/doc/_resources/055.png new file mode 100644 index 000000000..090aabd38 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/055.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/056.png b/ER-Model/plugins/base-ops/doc/_resources/056.png new file mode 100644 index 000000000..1735e6caa Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/056.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/057.png b/ER-Model/plugins/base-ops/doc/_resources/057.png new file mode 100644 index 000000000..692153ac5 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/057.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/058.png b/ER-Model/plugins/base-ops/doc/_resources/058.png new file mode 100644 index 000000000..dc579861e Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/058.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/059.png b/ER-Model/plugins/base-ops/doc/_resources/059.png new file mode 100644 index 000000000..dceb063df Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/059.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/060.png b/ER-Model/plugins/base-ops/doc/_resources/060.png new file mode 100644 index 000000000..dcf403012 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/060.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/061.png b/ER-Model/plugins/base-ops/doc/_resources/061.png new file mode 100644 index 000000000..913db4705 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/061.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/062.png b/ER-Model/plugins/base-ops/doc/_resources/062.png new file mode 100644 index 000000000..899dd9454 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/062.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/063.png b/ER-Model/plugins/base-ops/doc/_resources/063.png new file mode 100644 index 000000000..a6f947646 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/063.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/064.png b/ER-Model/plugins/base-ops/doc/_resources/064.png new file mode 100644 index 000000000..c0fa30164 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/064.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/065.png b/ER-Model/plugins/base-ops/doc/_resources/065.png new file mode 100644 index 000000000..50e370eb2 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/065.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/066.png b/ER-Model/plugins/base-ops/doc/_resources/066.png new file mode 100644 index 000000000..dc7030597 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/066.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/067.png b/ER-Model/plugins/base-ops/doc/_resources/067.png new file mode 100644 index 000000000..68c4a0ff2 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/067.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/068.png b/ER-Model/plugins/base-ops/doc/_resources/068.png new file mode 100644 index 000000000..db0da24f1 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/068.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/069.png b/ER-Model/plugins/base-ops/doc/_resources/069.png new file mode 100644 index 000000000..926c3a061 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/069.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/070.png b/ER-Model/plugins/base-ops/doc/_resources/070.png new file mode 100644 index 000000000..a5a49565d Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/070.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/071.png b/ER-Model/plugins/base-ops/doc/_resources/071.png new file mode 100644 index 000000000..cfb6a9c49 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/071.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/072.png b/ER-Model/plugins/base-ops/doc/_resources/072.png new file mode 100644 index 000000000..07f18ae89 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/072.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/073.png b/ER-Model/plugins/base-ops/doc/_resources/073.png new file mode 100644 index 000000000..90918cec6 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/073.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/1.png b/ER-Model/plugins/base-ops/doc/_resources/1.png new file mode 100644 index 000000000..f2feb8a86 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/10.png b/ER-Model/plugins/base-ops/doc/_resources/10.png new file mode 100644 index 000000000..15e9d49e1 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/10.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/11.png b/ER-Model/plugins/base-ops/doc/_resources/11.png new file mode 100644 index 000000000..f4267d037 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/11.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/12.png b/ER-Model/plugins/base-ops/doc/_resources/12.png new file mode 100644 index 000000000..baa13b9bb Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/12.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/13.png b/ER-Model/plugins/base-ops/doc/_resources/13.png new file mode 100644 index 000000000..7fd9afeec Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/13.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/14.png b/ER-Model/plugins/base-ops/doc/_resources/14.png new file mode 100644 index 000000000..308382867 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/14.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/15.png b/ER-Model/plugins/base-ops/doc/_resources/15.png new file mode 100644 index 000000000..1c10cf87d Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/15.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/15A.png b/ER-Model/plugins/base-ops/doc/_resources/15A.png new file mode 100644 index 000000000..c969759d8 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/15A.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/16.png b/ER-Model/plugins/base-ops/doc/_resources/16.png new file mode 100644 index 000000000..ac6e79513 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/16.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/17.png b/ER-Model/plugins/base-ops/doc/_resources/17.png new file mode 100644 index 000000000..2e8b38813 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/17.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/18.png b/ER-Model/plugins/base-ops/doc/_resources/18.png new file mode 100644 index 000000000..ecdce54db Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/18.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/19.png b/ER-Model/plugins/base-ops/doc/_resources/19.png new file mode 100644 index 000000000..522263a09 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/19.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/2.png b/ER-Model/plugins/base-ops/doc/_resources/2.png new file mode 100644 index 000000000..3e9aeeb12 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/2.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/20.png b/ER-Model/plugins/base-ops/doc/_resources/20.png new file mode 100644 index 000000000..bc12dbc49 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/20.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/21.png b/ER-Model/plugins/base-ops/doc/_resources/21.png new file mode 100644 index 000000000..e27fd965a Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/21.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/22.png b/ER-Model/plugins/base-ops/doc/_resources/22.png new file mode 100644 index 000000000..b195ac311 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/22.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/3.png b/ER-Model/plugins/base-ops/doc/_resources/3.png new file mode 100644 index 000000000..6d5fc7935 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/3.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/4.png b/ER-Model/plugins/base-ops/doc/_resources/4.png new file mode 100644 index 000000000..3993f7e98 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/4.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/5.png b/ER-Model/plugins/base-ops/doc/_resources/5.png new file mode 100644 index 000000000..023a730fd Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/5.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/6.png b/ER-Model/plugins/base-ops/doc/_resources/6.png new file mode 100644 index 000000000..5cebb96ed Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/6.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/7.png b/ER-Model/plugins/base-ops/doc/_resources/7.png new file mode 100644 index 000000000..4ebaa80e5 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/7.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/8.png b/ER-Model/plugins/base-ops/doc/_resources/8.png new file mode 100644 index 000000000..8d4dc9c7a Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/8.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/9.png b/ER-Model/plugins/base-ops/doc/_resources/9.png new file mode 100644 index 000000000..33d1c7bef Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/9.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/backup-running.png b/ER-Model/plugins/base-ops/doc/_resources/backup-running.png new file mode 100644 index 000000000..cd5207472 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/backup-running.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/backup.png b/ER-Model/plugins/base-ops/doc/_resources/backup.png new file mode 100644 index 000000000..f0ff1f9fc Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/backup.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/btn-1.png b/ER-Model/plugins/base-ops/doc/_resources/btn-1.png new file mode 100644 index 000000000..3dfd696c1 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/btn-1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/btn-10.png b/ER-Model/plugins/base-ops/doc/_resources/btn-10.png new file mode 100644 index 000000000..e5217c5ba Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/btn-10.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/btn-11.png b/ER-Model/plugins/base-ops/doc/_resources/btn-11.png new file mode 100644 index 000000000..c738759e4 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/btn-11.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/btn-12.png b/ER-Model/plugins/base-ops/doc/_resources/btn-12.png new file mode 100644 index 000000000..1a75fbb13 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/btn-12.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/btn-2.png b/ER-Model/plugins/base-ops/doc/_resources/btn-2.png new file mode 100644 index 000000000..33ceb090d Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/btn-2.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/btn-3.png b/ER-Model/plugins/base-ops/doc/_resources/btn-3.png new file mode 100644 index 000000000..3c15796db Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/btn-3.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/btn-4.png b/ER-Model/plugins/base-ops/doc/_resources/btn-4.png new file mode 100644 index 000000000..8ce752865 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/btn-4.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/btn-5.png b/ER-Model/plugins/base-ops/doc/_resources/btn-5.png new file mode 100644 index 000000000..d82081e15 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/btn-5.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/btn-6.png b/ER-Model/plugins/base-ops/doc/_resources/btn-6.png new file mode 100644 index 000000000..a5d8637c4 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/btn-6.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/btn-7.png b/ER-Model/plugins/base-ops/doc/_resources/btn-7.png new file mode 100644 index 000000000..7b18307a3 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/btn-7.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/btn-8.png b/ER-Model/plugins/base-ops/doc/_resources/btn-8.png new file mode 100644 index 000000000..99e9d8704 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/btn-8.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/btn-9.png b/ER-Model/plugins/base-ops/doc/_resources/btn-9.png new file mode 100644 index 000000000..b24dc7ac1 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/btn-9.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/btn-9A.png b/ER-Model/plugins/base-ops/doc/_resources/btn-9A.png new file mode 100644 index 000000000..5aed19e82 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/btn-9A.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/bulkImport-confirm.png b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-confirm.png new file mode 100644 index 000000000..709a6f485 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-confirm.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/bulkImport-downErrorReport.png b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-downErrorReport.png new file mode 100644 index 000000000..1b29fda30 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-downErrorReport.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/bulkImport-downloadTemplate.png b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-downloadTemplate.png new file mode 100644 index 000000000..873aeae04 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-downloadTemplate.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/bulkImport-editTemplate.png b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-editTemplate.png new file mode 100644 index 000000000..ff50abd03 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-editTemplate.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/bulkImport-entrance.png b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-entrance.png new file mode 100644 index 000000000..34f57bbaf Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-entrance.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/bulkImport-errorInfo.png b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-errorInfo.png new file mode 100644 index 000000000..51ada3572 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-errorInfo.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/bulkImport-parseExcel.png b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-parseExcel.png new file mode 100644 index 000000000..6cfdcc655 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-parseExcel.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/bulkImport-upload.png b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-upload.png new file mode 100644 index 000000000..ed82c8a34 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/bulkImport-upload.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-bar-1.png b/ER-Model/plugins/base-ops/doc/_resources/chart-bar-1.png new file mode 100644 index 000000000..696b98ad6 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-bar-1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-bar-2.png b/ER-Model/plugins/base-ops/doc/_resources/chart-bar-2.png new file mode 100644 index 000000000..541321104 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-bar-2.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-bar-3.png b/ER-Model/plugins/base-ops/doc/_resources/chart-bar-3.png new file mode 100644 index 000000000..7641507fe Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-bar-3.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-bar-4.png b/ER-Model/plugins/base-ops/doc/_resources/chart-bar-4.png new file mode 100644 index 000000000..adeabfc1e Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-bar-4.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-heat-1.png b/ER-Model/plugins/base-ops/doc/_resources/chart-heat-1.png new file mode 100644 index 000000000..d15f48a48 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-heat-1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-line-1.png b/ER-Model/plugins/base-ops/doc/_resources/chart-line-1.png new file mode 100644 index 000000000..d16d4e3c3 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-line-1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-line-2.png b/ER-Model/plugins/base-ops/doc/_resources/chart-line-2.png new file mode 100644 index 000000000..49c7170b9 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-line-2.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-line-3.png b/ER-Model/plugins/base-ops/doc/_resources/chart-line-3.png new file mode 100644 index 000000000..46f69fc6d Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-line-3.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-line-4.png b/ER-Model/plugins/base-ops/doc/_resources/chart-line-4.png new file mode 100644 index 000000000..eaa4104a7 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-line-4.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-map-1.png b/ER-Model/plugins/base-ops/doc/_resources/chart-map-1.png new file mode 100644 index 000000000..0803373ba Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-map-1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-map-2.png b/ER-Model/plugins/base-ops/doc/_resources/chart-map-2.png new file mode 100644 index 000000000..9c02b5334 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-map-2.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-mix-1.png b/ER-Model/plugins/base-ops/doc/_resources/chart-mix-1.png new file mode 100644 index 000000000..a4177da99 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-mix-1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-pie-1.png b/ER-Model/plugins/base-ops/doc/_resources/chart-pie-1.png new file mode 100644 index 000000000..9ce686b52 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-pie-1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-pie-2.png b/ER-Model/plugins/base-ops/doc/_resources/chart-pie-2.png new file mode 100644 index 000000000..201f0c413 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-pie-2.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/chart-relations-1.png b/ER-Model/plugins/base-ops/doc/_resources/chart-relations-1.png new file mode 100644 index 000000000..ed267a0ee Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/chart-relations-1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/cluster-op-build.png b/ER-Model/plugins/base-ops/doc/_resources/cluster-op-build.png new file mode 100644 index 000000000..1795c371b Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/cluster-op-build.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/cluster-op-extra.png b/ER-Model/plugins/base-ops/doc/_resources/cluster-op-extra.png new file mode 100644 index 000000000..b7394374b Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/cluster-op-extra.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/cluster-op-genconf.png b/ER-Model/plugins/base-ops/doc/_resources/cluster-op-genconf.png new file mode 100644 index 000000000..04e5883b0 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/cluster-op-genconf.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/cluster-op-switch.png b/ER-Model/plugins/base-ops/doc/_resources/cluster-op-switch.png new file mode 100644 index 000000000..b603f16ed Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/cluster-op-switch.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/confirmationpage.png b/ER-Model/plugins/base-ops/doc/_resources/confirmationpage.png new file mode 100644 index 000000000..f91380f00 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/confirmationpage.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/create-wdr.png b/ER-Model/plugins/base-ops/doc/_resources/create-wdr.png new file mode 100644 index 000000000..38069fe88 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/create-wdr.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/create_device_manager_interface.png b/ER-Model/plugins/base-ops/doc/_resources/create_device_manager_interface.png new file mode 100644 index 000000000..0a920f26b Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/create_device_manager_interface.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/draftcopy.png b/ER-Model/plugins/base-ops/doc/_resources/draftcopy.png new file mode 100644 index 000000000..d79d0ca0a Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/draftcopy.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/draftdelete.png b/ER-Model/plugins/base-ops/doc/_resources/draftdelete.png new file mode 100644 index 000000000..5bb8e3179 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/draftdelete.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/draftenvcheck.png b/ER-Model/plugins/base-ops/doc/_resources/draftenvcheck.png new file mode 100644 index 000000000..513cfd64d Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/draftenvcheck.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/draftsubmit.png b/ER-Model/plugins/base-ops/doc/_resources/draftsubmit.png new file mode 100644 index 000000000..2a0ac5331 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/draftsubmit.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/edit.png b/ER-Model/plugins/base-ops/doc/_resources/edit.png new file mode 100644 index 000000000..2728036e2 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/edit.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/envchecksuccess.png b/ER-Model/plugins/base-ops/doc/_resources/envchecksuccess.png new file mode 100644 index 000000000..5eabfd91f Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/envchecksuccess.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/execute_disaster_cluster_installing.png b/ER-Model/plugins/base-ops/doc/_resources/execute_disaster_cluster_installing.png new file mode 100644 index 000000000..647c4cf34 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/execute_disaster_cluster_installing.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/finish_disaster_cluster_installing.png b/ER-Model/plugins/base-ops/doc/_resources/finish_disaster_cluster_installing.png new file mode 100644 index 000000000..e1059db95 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/finish_disaster_cluster_installing.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/guc-setting-1.png b/ER-Model/plugins/base-ops/doc/_resources/guc-setting-1.png new file mode 100644 index 000000000..b48c5c58b Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/guc-setting-1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/guc-setting-2.png b/ER-Model/plugins/base-ops/doc/_resources/guc-setting-2.png new file mode 100644 index 000000000..46019178c Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/guc-setting-2.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/guc-setting-3.png b/ER-Model/plugins/base-ops/doc/_resources/guc-setting-3.png new file mode 100644 index 000000000..ba945cc15 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/guc-setting-3.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-cluster-check.png b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-check.png new file mode 100644 index 000000000..7b7dee20e Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-check.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-cluster-config.png b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-config.png new file mode 100644 index 000000000..3c57a6a4d Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-config.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-cluster-confirm.png b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-confirm.png new file mode 100644 index 000000000..6a88d6335 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-confirm.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-cluster-connect-fail.png b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-connect-fail.png new file mode 100644 index 000000000..ca6c3cf4e Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-connect-fail.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-cluster-node-config.png b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-node-config.png new file mode 100644 index 000000000..688166f42 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-node-config.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-cluster-ok.png b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-ok.png new file mode 100644 index 000000000..3a0515498 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-ok.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-cluster-select-version.png b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-select-version.png new file mode 100644 index 000000000..728780100 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-cluster-select-version.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-lite-check.png b/ER-Model/plugins/base-ops/doc/_resources/import-lite-check.png new file mode 100644 index 000000000..ee583cb82 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-lite-check.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-lite-confirm.png b/ER-Model/plugins/base-ops/doc/_resources/import-lite-confirm.png new file mode 100644 index 000000000..6dd89c265 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-lite-confirm.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-lite-connect-fail.png b/ER-Model/plugins/base-ops/doc/_resources/import-lite-connect-fail.png new file mode 100644 index 000000000..7d52df27a Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-lite-connect-fail.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-lite-deploy-config.png b/ER-Model/plugins/base-ops/doc/_resources/import-lite-deploy-config.png new file mode 100644 index 000000000..4208591c8 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-lite-deploy-config.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-lite-node-config.png b/ER-Model/plugins/base-ops/doc/_resources/import-lite-node-config.png new file mode 100644 index 000000000..7ce77da8b Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-lite-node-config.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-lite-ok.png b/ER-Model/plugins/base-ops/doc/_resources/import-lite-ok.png new file mode 100644 index 000000000..1e48ffd5b Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-lite-ok.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-lite-select-version.png b/ER-Model/plugins/base-ops/doc/_resources/import-lite-select-version.png new file mode 100644 index 000000000..26fe5667a Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-lite-select-version.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-mini-check.png b/ER-Model/plugins/base-ops/doc/_resources/import-mini-check.png new file mode 100644 index 000000000..f76c78b83 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-mini-check.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-mini-confirm.png b/ER-Model/plugins/base-ops/doc/_resources/import-mini-confirm.png new file mode 100644 index 000000000..ff51fe6a0 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-mini-confirm.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-mini-connect-fail.png b/ER-Model/plugins/base-ops/doc/_resources/import-mini-connect-fail.png new file mode 100644 index 000000000..c59b77bd0 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-mini-connect-fail.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-mini-deploy-config.png b/ER-Model/plugins/base-ops/doc/_resources/import-mini-deploy-config.png new file mode 100644 index 000000000..43cd2ea00 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-mini-deploy-config.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-mini-node-config.png b/ER-Model/plugins/base-ops/doc/_resources/import-mini-node-config.png new file mode 100644 index 000000000..9a641325f Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-mini-node-config.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-mini-ok.png b/ER-Model/plugins/base-ops/doc/_resources/import-mini-ok.png new file mode 100644 index 000000000..9acf24440 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-mini-ok.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/import-mini-select-version.png b/ER-Model/plugins/base-ops/doc/_resources/import-mini-select-version.png new file mode 100644 index 000000000..fc5117688 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/import-mini-select-version.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-check-mini.png b/ER-Model/plugins/base-ops/doc/_resources/install-check-mini.png new file mode 100644 index 000000000..ed5af6bac Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-check-mini.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-cluster-check.png b/ER-Model/plugins/base-ops/doc/_resources/install-cluster-check.png new file mode 100644 index 000000000..3bf0f1d7c Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-cluster-check.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-cluster-global-config.png b/ER-Model/plugins/base-ops/doc/_resources/install-cluster-global-config.png new file mode 100644 index 000000000..9ce34e8cb Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-cluster-global-config.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-cluster-node-config-cm.png b/ER-Model/plugins/base-ops/doc/_resources/install-cluster-node-config-cm.png new file mode 100644 index 000000000..9f4231773 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-cluster-node-config-cm.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-cluster-node-config.png b/ER-Model/plugins/base-ops/doc/_resources/install-cluster-node-config.png new file mode 100644 index 000000000..584072d0e Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-cluster-node-config.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-lite-check.png b/ER-Model/plugins/base-ops/doc/_resources/install-lite-check.png new file mode 100644 index 000000000..e43f36aba Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-lite-check.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-lite-error.png b/ER-Model/plugins/base-ops/doc/_resources/install-lite-error.png new file mode 100644 index 000000000..6dd82027d Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-lite-error.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-olk-config-dad.png b/ER-Model/plugins/base-ops/doc/_resources/install-olk-config-dad.png new file mode 100644 index 000000000..ef1eba5aa Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-olk-config-dad.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-olk-config-olk.png b/ER-Model/plugins/base-ops/doc/_resources/install-olk-config-olk.png new file mode 100644 index 000000000..39e2ed9ba Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-olk-config-olk.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-olk-config-ss.png b/ER-Model/plugins/base-ops/doc/_resources/install-olk-config-ss.png new file mode 100644 index 000000000..c4d3a7bb8 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-olk-config-ss.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-olk-config-view.png b/ER-Model/plugins/base-ops/doc/_resources/install-olk-config-view.png new file mode 100644 index 000000000..3c2ab4a0d Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-olk-config-view.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-olk-config.png b/ER-Model/plugins/base-ops/doc/_resources/install-olk-config.png new file mode 100644 index 000000000..054b14762 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-olk-config.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-olk-error.png b/ER-Model/plugins/base-ops/doc/_resources/install-olk-error.png new file mode 100644 index 000000000..a8f6e996d Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-olk-error.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-olk-exe.png b/ER-Model/plugins/base-ops/doc/_resources/install-olk-exe.png new file mode 100644 index 000000000..d0cb86138 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-olk-exe.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-olk-list-stop.png b/ER-Model/plugins/base-ops/doc/_resources/install-olk-list-stop.png new file mode 100644 index 000000000..435031b3b Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-olk-list-stop.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-olk-list.png b/ER-Model/plugins/base-ops/doc/_resources/install-olk-list.png new file mode 100644 index 000000000..3dade21b4 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-olk-list.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-olk-success.png b/ER-Model/plugins/base-ops/doc/_resources/install-olk-success.png new file mode 100644 index 000000000..f152a0a19 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-olk-success.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-select-version.png b/ER-Model/plugins/base-ops/doc/_resources/install-select-version.png new file mode 100644 index 000000000..49283d3d3 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-select-version.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-type-select.png b/ER-Model/plugins/base-ops/doc/_resources/install-type-select.png new file mode 100644 index 000000000..8fd4e879e Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-type-select.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install-upload-pkg.png b/ER-Model/plugins/base-ops/doc/_resources/install-upload-pkg.png new file mode 100644 index 000000000..2f76dcb69 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install-upload-pkg.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install_disaster_cluster_failed.png b/ER-Model/plugins/base-ops/doc/_resources/install_disaster_cluster_failed.png new file mode 100644 index 000000000..807d003cb Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install_disaster_cluster_failed.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/install_task_create_draft.png b/ER-Model/plugins/base-ops/doc/_resources/install_task_create_draft.png new file mode 100644 index 000000000..9b0f51435 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/install_task_create_draft.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/installencluster.png b/ER-Model/plugins/base-ops/doc/_resources/installencluster.png new file mode 100644 index 000000000..154cdb0dd Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/installencluster.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/installenpackage.png b/ER-Model/plugins/base-ops/doc/_resources/installenpackage.png new file mode 100644 index 000000000..6a3fbacf2 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/installenpackage.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/logdownload.png b/ER-Model/plugins/base-ops/doc/_resources/logdownload.png new file mode 100644 index 000000000..06be5f3bf Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/logdownload.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/manage_disaster_cluster.png b/ER-Model/plugins/base-ops/doc/_resources/manage_disaster_cluster.png new file mode 100644 index 000000000..8ad03cfe6 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/manage_disaster_cluster.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/monitor.png b/ER-Model/plugins/base-ops/doc/_resources/monitor.png new file mode 100644 index 000000000..d9ae080c2 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/monitor.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/one-click-install-not-close-tab.png b/ER-Model/plugins/base-ops/doc/_resources/one-click-install-not-close-tab.png new file mode 100644 index 000000000..efa98d296 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/one-click-install-not-close-tab.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/one-click-taken.png b/ER-Model/plugins/base-ops/doc/_resources/one-click-taken.png new file mode 100644 index 000000000..91b1ace93 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/one-click-taken.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/onstepinstall.png b/ER-Model/plugins/base-ops/doc/_resources/onstepinstall.png new file mode 100644 index 000000000..6e20319e5 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/onstepinstall.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-1.png b/ER-Model/plugins/base-ops/doc/_resources/ope-1.png new file mode 100644 index 000000000..79695cacc Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-10A.png b/ER-Model/plugins/base-ops/doc/_resources/ope-10A.png new file mode 100644 index 000000000..4347588b9 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-10A.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-10B.png b/ER-Model/plugins/base-ops/doc/_resources/ope-10B.png new file mode 100644 index 000000000..5a3e77822 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-10B.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-11.png b/ER-Model/plugins/base-ops/doc/_resources/ope-11.png new file mode 100644 index 000000000..88dd3518e Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-11.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-12.png b/ER-Model/plugins/base-ops/doc/_resources/ope-12.png new file mode 100644 index 000000000..6e96553e2 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-12.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-2.png b/ER-Model/plugins/base-ops/doc/_resources/ope-2.png new file mode 100644 index 000000000..58100c3c9 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-2.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-3.png b/ER-Model/plugins/base-ops/doc/_resources/ope-3.png new file mode 100644 index 000000000..0c31dcf50 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-3.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-3A.png b/ER-Model/plugins/base-ops/doc/_resources/ope-3A.png new file mode 100644 index 000000000..1ef2e8f35 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-3A.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-4.png b/ER-Model/plugins/base-ops/doc/_resources/ope-4.png new file mode 100644 index 000000000..a14be4fff Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-4.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-5.png b/ER-Model/plugins/base-ops/doc/_resources/ope-5.png new file mode 100644 index 000000000..3c327e849 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-5.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-6.png b/ER-Model/plugins/base-ops/doc/_resources/ope-6.png new file mode 100644 index 000000000..116fac049 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-6.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-7.png b/ER-Model/plugins/base-ops/doc/_resources/ope-7.png new file mode 100644 index 000000000..e3dbfb675 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-7.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-7A.png b/ER-Model/plugins/base-ops/doc/_resources/ope-7A.png new file mode 100644 index 000000000..8133aea93 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-7A.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-8.png b/ER-Model/plugins/base-ops/doc/_resources/ope-8.png new file mode 100644 index 000000000..f7a3668ef Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-8.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/ope-9.png b/ER-Model/plugins/base-ops/doc/_resources/ope-9.png new file mode 100644 index 000000000..40d1e84d3 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/ope-9.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/recovery-delete.png b/ER-Model/plugins/base-ops/doc/_resources/recovery-delete.png new file mode 100644 index 000000000..fec4e43c7 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/recovery-delete.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/recovery-list.png b/ER-Model/plugins/base-ops/doc/_resources/recovery-list.png new file mode 100644 index 000000000..74a899141 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/recovery-list.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/recovery-running.png b/ER-Model/plugins/base-ops/doc/_resources/recovery-running.png new file mode 100644 index 000000000..153c7d5e6 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/recovery-running.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/sharing_storage_configure.png b/ER-Model/plugins/base-ops/doc/_resources/sharing_storage_configure.png new file mode 100644 index 000000000..71715b793 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/sharing_storage_configure.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/start_disaster_cluster.png b/ER-Model/plugins/base-ops/doc/_resources/start_disaster_cluster.png new file mode 100644 index 000000000..7b5a1f73c Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/start_disaster_cluster.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/upgrade-check-1.png b/ER-Model/plugins/base-ops/doc/_resources/upgrade-check-1.png new file mode 100644 index 000000000..418fcf70f Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/upgrade-check-1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/upgrade-check-2.png b/ER-Model/plugins/base-ops/doc/_resources/upgrade-check-2.png new file mode 100644 index 000000000..7eae431e5 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/upgrade-check-2.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/upgrade-config-1.png b/ER-Model/plugins/base-ops/doc/_resources/upgrade-config-1.png new file mode 100644 index 000000000..e19ef9aaa Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/upgrade-config-1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/upgrade-config-2.png b/ER-Model/plugins/base-ops/doc/_resources/upgrade-config-2.png new file mode 100644 index 000000000..f1db7a3b8 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/upgrade-config-2.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/upgrade-config-3.png b/ER-Model/plugins/base-ops/doc/_resources/upgrade-config-3.png new file mode 100644 index 000000000..35fc24d87 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/upgrade-config-3.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-1.png b/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-1.png new file mode 100644 index 000000000..a1eca00f5 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-1.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-2.png b/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-2.png new file mode 100644 index 000000000..1f9d5f335 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-2.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-3.png b/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-3.png new file mode 100644 index 000000000..2b94d5da8 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-3.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-4.png b/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-4.png new file mode 100644 index 000000000..e62fd4624 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-4.png differ diff --git a/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-5.png b/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-5.png new file mode 100644 index 000000000..a33c6f973 Binary files /dev/null and b/ER-Model/plugins/base-ops/doc/_resources/upgrade-exe-5.png differ diff --git a/ER-Model/plugins/base-ops/pom.xml b/ER-Model/plugins/base-ops/pom.xml new file mode 100644 index 000000000..4150259d7 --- /dev/null +++ b/ER-Model/plugins/base-ops/pom.xml @@ -0,0 +1,311 @@ + + + 4.0.0 + org.opengauss + base-ops + + org.opengauss + visualtool-parent + ${admin.version} + ../../pom.xml + + jar + + false + + + + + org.springframework.boot + spring-boot-starter + + + com.gitee.starblues + spring-brick-bootstrap + + + com.gitee.starblues + spring-brick-maven-packager + + + + org.opengauss + visualtool-framework + provided + + + com.alibaba + druid-spring-boot-3-starter + + + com.baomidou + * + + + com.alibaba + fastjson + + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + com.baomidou + mybatis-plus-extension + + + com.baomidou + mybatis-plus-jsqlparser + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + org.mybatis + mybatis + + + mybatis-spring + org.mybatis + + + + + + com.alibaba + druid-spring-boot-3-starter + + + jakarta.websocket + jakarta.websocket-client-api + ${jakarta.websocket.version} + + + org.projectlombok + lombok + ${lombok.version} + + + org.yaml + snakeyaml + ${snakeyaml.version} + + + cn.hutool + hutool-all + + + + com.alibaba + fastjson + + + + com.alibaba + easyexcel + + + org.apache.poi + poi + + + org.apache.poi + poi-ooxml + + + com.google.guava + guava + + + junit + junit + ${junit.version} + test + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.opengauss + opengauss-jdbc + + + com.github.mwiede + jsch + + + + jakarta.servlet + jakarta.servlet-api + + + org.apache.httpcomponents + httpclient + + + commons-logging + commons-logging + + + + + org.apache.httpcomponents + httpcore + + + + + + + + com.gitee.starblues + spring-brick-maven-packager + ${spring-brick.version} + + + + + + com.gitee.starblues + spring-brick-maven-packager + ${spring-brick.version} + + prod + + base-ops + org.opengauss.admin.plugin.BaseOpsPluginApplication + ${project.version} + lita + 基础运维插件 + + + + org.intarkdb.** + + + + + + + repackage + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.2.0 + + + + web-ui/dist + + **/* + + false + + + ${basedir}/src/main/resources/resources + + **/* + + false + + + false + true + + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + ${build.frontend.skip} + + + + exec-npm-install + generate-resources + + exec + + + npm + + install + --frozen-lockfile + --prefer-offline + --no-optional + + web-ui + + + + + exec-npm-run-build + generate-resources + + exec + + + npm + + run + build:prod + + web-ui + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + + copy-web + generate-resources + + copy-resources + + + ${basedir}/src/main/resources + + + web-ui/dist + resources + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/BaseOpsPluginApplication.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/BaseOpsPluginApplication.java new file mode 100644 index 000000000..8b1939548 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/BaseOpsPluginApplication.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BaseOpsPluginApplication.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/BaseOpsPluginApplication.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin; + +import com.gitee.starblues.bootstrap.SpringPluginBootstrap; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * MessageSourceUtils + * + * @author: wangchao + * @Date: 2025/9/11 21:21 + * @since 7.0.0-RC2 + **/ +@SpringBootApplication +@MapperScan("org.opengauss.admin.plugin.mapper") +public class BaseOpsPluginApplication extends SpringPluginBootstrap { + public static void main(String[] args) { + new BaseOpsPluginApplication().run(args); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/aspectj/LogAspect.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/aspectj/LogAspect.java new file mode 100644 index 000000000..e8cf053cc --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/aspectj/LogAspect.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * LogAspect.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/aspectj/LogAspect.java + * + * ------------------------------------------------------------------------- + */ + +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.aspectj; + +import com.alibaba.fastjson.JSON; +import org.opengauss.admin.common.annotation.Log; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.enums.BusinessStatus; +import org.opengauss.admin.common.enums.HttpMethod; +import org.opengauss.admin.common.utils.SecurityUtils; +import org.opengauss.admin.common.utils.ServletUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.common.utils.ip.IpUtils; +import org.opengauss.admin.framework.manager.AsyncManager; +import org.opengauss.admin.framework.manager.factory.AsyncFactory; +import org.opengauss.admin.system.domain.SysOperLog; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.HandlerMapping; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Collection; +import java.util.Map; + +/** + * Operation log record AOP processing + * + * @author xielibo + */ +@Slf4j +@Aspect +@Component +public class LogAspect { + + /** + * doAfterReturning + * + * @param joinPoint + * @param controllerLog + * @param jsonResult + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * doAfterThrowing + * + * @param joinPoint + * @param e + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) { + handleLog(joinPoint, controllerLog, e, null); + } + + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) { + try { + ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = (HttpServletRequest) sra.resolveReference(RequestAttributes.REFERENCE_REQUEST); + SysOperLog operLog = new SysOperLog(); + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (loginUser != null) { + operLog.setOperName(loginUser.getUsername()); + } + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + String ip = IpUtils.getIpAddr(request); + operLog.setOperIp(ip); + operLog.setOperUrl(request.getRequestURI()); + if (e != null) { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); + } + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + operLog.setRequestMethod(request.getMethod()); + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + RequestContextHolder.setRequestAttributes(sra, true); + AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + } catch (Exception exp) { + log.error("error:{}", exp.getMessage()); + exp.printStackTrace(); + } + } + + /** + * Obtain the description information of the method in the annotation for Controller layer annotation + * + * @param log log + * @param operLog operLog + * @param jsonResult + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception { + operLog.setBusinessType(log.businessType().ordinal()); + operLog.setTitle(log.title()); + operLog.setOperatorType(log.operatorType().ordinal()); + if (log.isSaveRequestData()) { + setRequestValue(joinPoint, operLog); + } + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) { + operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); + } + } + + /** + * Get the parameters of the request and put them in the log + * + * @param joinPoint + * @param operLog operLog + * @throws Exception e + */ + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception { + String requestMethod = operLog.getRequestMethod(); + if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { + String params = argsArrayToString(joinPoint.getArgs()); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + } else { + Map paramsMap = (Map) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000)); + } + } + + /** + * parameter assembly + * @param paramsArray + */ + private String argsArrayToString(Object[] paramsArray) { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) { + for (Object o : paramsArray) { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) { + try { + Object jsonObj = JSON.toJSON(o); + params += jsonObj.toString() + " "; + } catch (Exception e) { + } + } + } + } + return params.trim(); + } + + /** + * Determine whether to filter the object. + * @param o + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection) o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map) o; + for (Object value : map.entrySet()) { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/base/BaseController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/base/BaseController.java new file mode 100644 index 000000000..1ab6afca6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/base/BaseController.java @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BaseController.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/base/BaseController.java + * + * ------------------------------------------------------------------------- + */ + +/** + * Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * Copyright (c) 2021 openGauss Contributors + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless 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. + */ +package org.opengauss.admin.plugin.base; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.opengauss.admin.common.constant.SqlConstants; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.core.page.PageDomain; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.core.page.TableSupport; +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.common.utils.DateUtils; +import org.opengauss.admin.common.utils.SecurityUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; + +import java.beans.PropertyEditorSupport; +import java.util.ArrayList; +import java.util.Date; + +/** + * base controller + * + * @author xielibo + */ +public class BaseController { + + /** + * Automatically convert the date format string passed from the front desk to Date type + */ + @InitBinder + public void initBinder(WebDataBinder binder) { + // Date format + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() { + @Override + public void setAsText(String text) { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * Set request pagination params + */ + protected Page startPage() { + Page page = new Page<>(); + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + String orderBy = pageDomain.getOrderBy(); + String orderColumn = pageDomain.getOrderByColumn(); + String isAsc = pageDomain.getIsAsc(); + + if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize)) { + page.setCurrent(pageNum); + page.setSize(pageSize); + page.setOptimizeCountSql(false); + page.setMaxLimit(500L); + } + + addOrder(orderColumn, isAsc, page); + + return page; + } + + /** + * Set request pagination params + */ + protected Page startPage(Integer pageNum, Integer size, String orderColumn, String order) { + Page page = new Page(); + page.setCurrent(pageNum); + page.setSize(size); + page.setOptimizeCountSql(false); + page.setMaxLimit(500L); + + addOrder(orderColumn, order, page); + return page; + } + + /** + * add order params + */ + private void addOrder(String orderColumn, String order, Page page) { + if (StringUtils.isNotBlank(orderColumn)) { + if (SqlConstants.ASC.equalsIgnoreCase(order)) { + page.addOrder(OrderItem.asc(orderColumn)); + } else { + page.addOrder(OrderItem.desc(orderColumn)); + } + } + } + + /** + * Set response pagination params + * + * @param page + */ + protected TableDataInfo getDataTable(IPage page) { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(ResponseCode.SUCCESS.code()); + rspData.setMsg(ResponseCode.SUCCESS.msg()); + rspData.setRows(page.getRecords()); + rspData.setTotal(page.getTotal()); + return rspData; + } + + /** + * Set response pagination params + * + * @return page + */ + protected TableDataInfo getDataTable() { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(ResponseCode.SUCCESS.code()); + rspData.setMsg(ResponseCode.SUCCESS.msg()); + rspData.setRows(new ArrayList<>()); + rspData.setTotal(0); + return rspData; + } + /** + * response result + * + */ + protected AjaxResult toAjax(int rows) { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * response result + * + */ + protected AjaxResult toAjax(boolean result) { + return result ? success() : error(); + } + + /** + * response success result + */ + public AjaxResult success() { + return AjaxResult.success(); + } + + /** + * response error result + */ + public AjaxResult error() { + return AjaxResult.error(); + } + + /** + * response success result + */ + public AjaxResult success(String message) { + return AjaxResult.success(message); + } + + /** + * response error result + */ + public AjaxResult error(String message) { + return AjaxResult.error(message); + } + + + /** + * Get the currently logged in user + */ + public LoginUser getLoginUser() { + return SecurityUtils.getLoginUser(); + } + + /** + * Get the currently logged in userId + */ + public Integer getUserId() { + return getLoginUser().getUser().getUserId(); + } + + /** + * Get the currently logged in username + */ + public String getUsername() { + return getLoginUser().getUsername(); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/AutoFillMetaObjectHandler.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/AutoFillMetaObjectHandler.java new file mode 100644 index 000000000..d66d81264 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/AutoFillMetaObjectHandler.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AutoFillMetaObjectHandler.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/config/AutoFillMetaObjectHandler.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.config; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.utils.SecurityUtils; +import org.springframework.stereotype.Component; + +import java.util.Date; + +/** + * Autofill public attribute data + * + * @className: AutoFillMetaObjectHandler + * @author: xielibo + * @date: 2022-08-06 10:36 PM + **/ +@Slf4j +@Component +public class AutoFillMetaObjectHandler implements MetaObjectHandler { + + @Override + public void insertFill(MetaObject metaObject) { + LoginUser loginUser = null; + try { + loginUser = SecurityUtils.getLoginUser(); + } catch (Exception e) { + log.warn("No user information found, this is a new installation."); + } + if (loginUser != null) { + this.setFieldValByName("createBy", loginUser.getUser().getUserName(), metaObject); + this.setFieldValByName("updateBy", loginUser.getUser().getUserName(), metaObject); + } + this.setFieldValByName("createTime", new Date(), metaObject); + this.setFieldValByName("updateTime", new Date(), metaObject); + } + + + @Override + public void updateFill(MetaObject metaObject) { + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (loginUser != null) { + this.setFieldValByName("updateBy", loginUser.getUser().getUserName(), metaObject); + } + this.setFieldValByName("updateTime", new Date(), metaObject); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/DataSourceConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/DataSourceConfig.java new file mode 100644 index 000000000..d2228381b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/DataSourceConfig.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DataSourceConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/config/DataSourceConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.config; + +import com.gitee.starblues.bootstrap.PluginContextHolder; +import com.gitee.starblues.spring.environment.EnvironmentProvider; +import org.opengauss.admin.plugin.enums.DbDataLocationEnum; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer; +import org.springframework.boot.sql.init.DatabaseInitializationMode; +import org.springframework.boot.sql.init.DatabaseInitializationSettings; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +import javax.sql.DataSource; +import java.util.Optional; + +/** + * @author LZW + * @date 2023/1/5 + */ +@Configuration +public class DataSourceConfig { + + @Bean + @ConfigurationProperties(prefix = "spring.datasource") + public DataSource dataSource() { + EnvironmentProvider environmentProvider = PluginContextHolder.getEnvironmentProvider(); + // read config from dataKit platform + String url = environmentProvider.getString("spring.datasource.url"); + String username = environmentProvider.getString("spring.datasource.username"); + String password = environmentProvider.getString("spring.datasource.password"); + String driverClassName = environmentProvider.getString("spring.datasource.driver-class-name"); + + return DataSourceBuilder.create() + .driverClassName(driverClassName) + .url(url) + .username(username) + .password(password) + .build(); + } + + @Bean("opsDataSourceScriptDatabaseInitializer") + @Profile("!dev") + DataSourceScriptDatabaseInitializer dataSourceScriptDatabaseInitializer(DataSource dataSource) { + EnvironmentProvider environmentProvider = PluginContextHolder.getEnvironmentProvider(); + String driverClassName = environmentProvider.getString("spring.datasource.driver-class-name"); + Optional dbDataLocationEnum = DbDataLocationEnum.of(driverClassName); + DatabaseInitializationSettings settings = new DatabaseInitializationSettings(); + settings.setContinueOnError(true); + settings.setSeparator(";"); + settings.setMode(DatabaseInitializationMode.ALWAYS); + if (dbDataLocationEnum.isEmpty()) { + return new DataSourceScriptDatabaseInitializer(dataSource, new DatabaseInitializationSettings()); + } + settings.setDataLocations(dbDataLocationEnum.get().getLocations()); + return new DataSourceScriptDatabaseInitializer(dataSource, settings); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/MybatisPlusConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/MybatisPlusConfig.java new file mode 100644 index 000000000..81a11caa1 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/MybatisPlusConfig.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MybatisPlusConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/config/MybatisPlusConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer; +import com.baomidou.mybatisplus.core.MybatisConfiguration; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.opengauss.admin.plugin.mapper.handler.LocalDateTimeTypeHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * Description: mybatis-plus configuration + * + * @author xielibo + * @version 1.0 + * @date: 2021/9/28 5:54 PM + * @since JDK 1.8 + */ +@EnableTransactionManagement(proxyTargetClass = true) +@Configuration +public class MybatisPlusConfig { + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(paginationInnerInterceptor()); + interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); + interceptor.addInnerInterceptor(blockAttackInnerInterceptor()); + return interceptor; + } + + /** + * 创建并返回一个 ConfigurationCustomizer 用于注册自定义类型处理器 + * + * @return 配置定制器 + */ + @Bean + public ConfigurationCustomizer configurationCustomizer() { + return new ConfigurationCustomizer() { + /** + * 定制 MyBatis 的配置 + * + * @param configuration MyBatis 的配置对象 + */ + @Override + public void customize(MybatisConfiguration configuration) { + configuration.getTypeHandlerRegistry().register(new LocalDateTimeTypeHandler()); + } + }; + } + + /** + * Page Intercaptor + */ + public PaginationInnerInterceptor paginationInnerInterceptor() { + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); + paginationInnerInterceptor.setDbType(DbType.POSTGRE_SQL); + paginationInnerInterceptor.setMaxLimit(-1L); + return paginationInnerInterceptor; + } + + /** + * Optimistic Lock Plugin + */ + public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() { + return new OptimisticLockerInnerInterceptor(); + } + + /** + * Prevent full table deletion + */ + public BlockAttackInnerInterceptor blockAttackInnerInterceptor() { + return new BlockAttackInnerInterceptor(); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/OpsExtensionInfoServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/OpsExtensionInfoServiceImpl.java new file mode 100644 index 000000000..32db4b664 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/OpsExtensionInfoServiceImpl.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +package org.opengauss.admin.plugin.config; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.enums.PluginLicenseType; +import org.opengauss.admin.system.plugin.beans.PluginExtensionInfoDto; +import org.opengauss.admin.system.plugin.extract.PluginExtensionInfoExtract; +import org.springframework.stereotype.Service; + +import java.util.Date; + +/** + * OpsExtensionInfoServiceImpl + * + * @Author wangchao + * @date 2025-10-14 + * @since 7.0.0-RC2 + */ +@Slf4j +@Service +public class OpsExtensionInfoServiceImpl implements PluginExtensionInfoExtract { + @Override + public PluginExtensionInfoDto getPluginExtensionInfo() { + PluginExtensionInfoDto dto = new PluginExtensionInfoDto(); + dto.setPluginId(PluginExtensionInfoConfig.PLUGIN_ID); + dto.setPluginName("base-ops"); + dto.setPluginHome(PluginExtensionInfoConfig.PLUGIN_ID); + dto.setPluginDevelopmentCompany("openGauss community"); + dto.setPhoneNumber("400-123-4567"); + dto.setEmail(""); + dto.setCompanyAddress("opengauss.org"); + dto.setAuthAddress("/license"); + dto.setPluginLicenseType(PluginLicenseType.TRIAL); + dto.setPluginExpirationTime(new Date()); + return dto; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/PluginExtensionInfoConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/PluginExtensionInfoConfig.java new file mode 100644 index 000000000..837646ea0 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/PluginExtensionInfoConfig.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * PluginExtensionInfoConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/config/PluginExtensionInfoConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.config; + +import com.gitee.starblues.core.PluginExtensionInfo; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * @className: PluginExtensionInfoConfig + * @author: xielibo + * @date: 2023-01-26 16:39 + **/ +@Component +public class PluginExtensionInfoConfig implements PluginExtensionInfo { + /** + * plugin id base-ops + */ + public static final String PLUGIN_ID = "base-ops"; + + @Override + public Map extensionInfo() { + Map map = new HashMap<>(); + map.put("logo", "PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAzMTAgMzEwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAzMTAgMzEwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGQ9Ik0zMDAuNTY0LDE3OS4zMTFMMjgyLjQsMTY4LjgyNGMwLjQ4OS00LjU0MywwLjc0Ny05LjE1NCwwLjc0Ny0xMy44MjRzLTAuMjU4LTkuMjgxLTAuNzQ3LTEzLjgyNGwxOC4xNjQtMTAuNDg3CgljMy44ODEtMi4yNDEsNi42NTgtNS44Niw3LjgxNi0xMC4xOTFjMS4xNi00LjMzLDAuNTY0LTguODU0LTEuNjc2LTEyLjczNWwtMzQuOTQzLTYwLjUyNGMtMi45OS01LjE4LTguNTY0LTguMzk2LTE0LjU1MS04LjM5NgoJYy0yLjkzLDAtNS44MjYsMC43NzgtOC4zNzcsMi4yNTFMMjMwLjYxOSw1MS42MWMtNy40MDItNS40MjktMTUuNDA2LTEwLjA4My0yMy44OTMtMTMuODQyVjE2Ljc4MwoJQzIwNi43MjcsNy41MjksMTk5LjE5NSwwLDE4OS45NDUsMGgtNjkuODkxYy05LjI1NCwwLTE2Ljc4MSw3LjUyOS0xNi43ODEsMTYuNzgzdjIwLjk4NWMtOC40ODYsMy43NTktMTYuNDksOC40MTMtMjMuODk0LDEzLjg0MgoJTDYxLjE2NCw0MS4wOTRjLTIuNTUxLTEuNDczLTUuNDQ1LTIuMjUtOC4zNzUtMi4yNWMtNS45ODYsMC0xMS41NjMsMy4yMTUtMTQuNTUzLDguMzk1TDMuMjk1LDEwNy43NjIKCWMtMi4yNDIsMy44ODEtMi44MzYsOC40MDYtMS42NzQsMTIuNzM2YzEuMTU2LDQuMzMsMy45MzUsNy45NDksNy44MTQsMTAuMTkxTDI3LjYsMTQxLjE3NmMtMC40ODksNC41NDMtMC43NDcsOS4xNTQtMC43NDcsMTMuODI0CglzMC4yNTgsOS4yODEsMC43NDcsMTMuODI0TDkuNDM1LDE3OS4zMTFjLTMuODc5LDIuMjQxLTYuNjU4LDUuODYtNy44MTQsMTAuMTkxYy0xLjE2Miw0LjMzLTAuNTY4LDguODU1LDEuNjc0LDEyLjczNWwzNC45NDEsNjAuNTI0CgljMi45OSw1LjE4LDguNTY2LDguMzk1LDE0LjU1Myw4LjM5NWMyLjkzLDAsNS44MjQtMC43NzcsOC4zNzUtMi4yNUw3OS4zOCwyNTguMzljNy40MDMsNS40MjksMTUuNDA3LDEwLjA4MywyMy44OTQsMTMuODQydjIwLjk4NgoJYzAsNC40ODIsMS43NDQsOC42OTUsNC45MTQsMTEuODY2YzMuMTc0LDMuMTY5LDcuMzg1LDQuOTE2LDExLjg2Nyw0LjkxNmg2OS44OTFjOS4yNSwwLDE2Ljc4MS03LjUyOSwxNi43ODEtMTYuNzgydi0yMC45ODYKCWM4LjQ4Ni0zLjc1OSwxNi40OS04LjQxMywyMy44OTMtMTMuODQybDE4LjIxNSwxMC41MTdjMi41NTEsMS40NzMsNS40NDcsMi4yNSw4LjM3NywyLjI1YzUuOTg2LDAsMTEuNTYxLTMuMjE1LDE0LjU1MS04LjM5NQoJbDM0Ljk0My02MC41MjNjMi4yNC0zLjg4MSwyLjgzNi04LjQwNiwxLjY3Ni0xMi43MzZDMzA3LjIyMywxODUuMTcyLDMwNC40NDUsMTgxLjU1MywzMDAuNTY0LDE3OS4zMTF6IE0xNTUsMjQ2LjEwMQoJYy0xOC4yMywwLTM1LjIwNy01LjM1Ny00OS40NDktMTQuNTc5bDMwLjgwMS0zMC44MDRjNi40NDksMi43NzIsMTMuNDUsNC4yNCwyMC42NzcsNC4yNDFjMC4wMDIsMCwwLjAwMywwLDAuMDA0LDAKCWMxNC4wMTEsMCwyNy4xNzUtNS40NjYsMzcuMDY0LTE1LjM5YzEzLjUtMTMuNTM2LDE4LjU0MS0zMy4zNjMsMTMuMTU1LTUxLjc0M2MtMC4zMTMtMS4wNjktMS4xNjgtMS44OTQtMi4yNDgtMi4xNjkKCWMtMS4wNzgtMC4yNzctMi4yMjUsMC4wNC0zLjAxMSwwLjgyOWwtMzIuOTcsMzMuMDY5Yy0xLjk3OS0wLjgwNC02Ljk2MS0zLjU0Mi0xNi4xODYtMTIuNzM2CgljLTkuMjI2LTkuMTk3LTExLjk3Ni0xNC4xNzMtMTIuNzgzLTE2LjE0OGwzMi45NjYtMzMuMDY5YzAuNzg3LTAuNzg5LDEuMDk4LTEuOTM1LDAuODItMy4wMTNjLTAuMjc4LTEuMDc5LTEuMTA1LTEuOTMxLTIuMTc1LTIuMjQxCgljLTQuNzUxLTEuMzc4LTkuNjc2LTIuMDc4LTE0LjYzNy0yLjA3OGMtMTQuMDE2LDAtMjcuMTgxLDUuNDY0LTM3LjA2OSwxNS4zODVjLTkuODczLDkuOTAzLTE1LjI5OSwyMy4wNTctMTUuMjgsMzcuMDM5CgljMC4wMSw3LjIwNCwxLjQ3NiwxNC4xOCw0LjI0LDIwLjYwNGwtMzAuNzIyLDMwLjcyNEM2OS4xNDUsMTg5Ljg2OCw2My44OTYsMTczLjA0Nyw2My44OTYsMTU1CgljMC01MC4zMTMsNDAuNzg3LTkxLjEwMiw5MS4xMDQtOTEuMTAyczkxLjEwMiw0MC43ODksOTEuMTAyLDkxLjEwMlMyMDUuMzE2LDI0Ni4xMDEsMTU1LDI0Ni4xMDF6Ii8+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPg=="); + map.put("descriptionEn", "Basic Operation Plugin"); + return map; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/RestTemplateConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/RestTemplateConfig.java new file mode 100644 index 000000000..b2eda21c4 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/RestTemplateConfig.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * RestTemplateConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/config/RestTemplateConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + @Bean + public RestTemplate restTemplate() { + SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); + requestFactory.setConnectTimeout(30000); + requestFactory.setReadTimeout(30000); + return new RestTemplate(requestFactory); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/ThreadPoolConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/ThreadPoolConfig.java new file mode 100644 index 000000000..7e2287eb6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/config/ThreadPoolConfig.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ThreadPoolConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/config/ThreadPoolConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.config; + +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.opengauss.admin.common.utils.Threads; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * ThreadPool Configuration + * + * @author xielibo + **/ +@Configuration +public class ThreadPoolConfig { + private int corePoolSize = 10; + + private int maxPoolSize = 20; + + private int queueCapacity = 2000; + + private int keepAliveSeconds = 300; + + @Bean(name = "threadPoolTaskExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(maxPoolSize); + executor.setCorePoolSize(corePoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() { + return new ScheduledThreadPoolExecutor(corePoolSize, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) { + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/constant/OpsConstants.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/constant/OpsConstants.java new file mode 100644 index 000000000..b9699828a --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/constant/OpsConstants.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024-2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsConstants.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/constant/OpsConstants.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.constant; + +/** + * OpsConstants + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +public interface OpsConstants { + /** + * package remark v2 + */ + String PACKAGE_REMARK = "v2"; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingDataFlowController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingDataFlowController.java new file mode 100644 index 000000000..d77599aa5 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingDataFlowController.java @@ -0,0 +1,128 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.controller.modeling; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.opengauss.admin.common.annotation.Log; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.enums.BusinessType; +import org.opengauss.admin.common.enums.OperatorType; +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowEntity; +import org.opengauss.admin.plugin.service.modeling.IModelingDataFlowService; +import org.opengauss.admin.plugin.service.modeling.operator.IModelingDataFlowProcessService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@RestController +@RequestMapping("/modeling/dataflow") +public class ModelingDataFlowController extends BaseController { + + @Autowired + private IModelingDataFlowService modelingDataFlowService; + @Autowired + private IModelingDataFlowProcessService modelingDataFlowProcessService; + + @GetMapping("/list") + public AjaxResult page(@RequestParam(required = false, value = "name") String name) { + IPage page = modelingDataFlowService.selectList(startPage(), name); + AjaxResult result = AjaxResult.success(); + result.put("data", Map.of("total",page.getTotal())); + result.put("rows",page.getRecords()); + return result; + } + + @GetMapping("/getById/{dataFlowId}") + public AjaxResult get(@PathVariable String dataFlowId){ + return AjaxResult.success(modelingDataFlowService.getById(dataFlowId)); + } + + /** + * add + */ + @Log(title = "base-ops-dataflow",operatorType = OperatorType.PLUGIN,businessType = BusinessType.INSERT) + @RequestMapping("/add") + public AjaxResult add(@RequestBody ModelingDataFlowEntity dataFlowData) { + if (modelingDataFlowService.findByName(dataFlowData.getName()).isEmpty()) { + return AjaxResult.success(modelingDataFlowService.insertDataFlow(dataFlowData)); + } else { + return AjaxResult.error("The data flow with the same name already exists, please try again with a different name."); + } + } + + /** + * delete + */ + @Log(title = "dataflow",operatorType = OperatorType.PLUGIN,businessType = BusinessType.DELETE) + @DeleteMapping("/delete/{flowIds}") + public AjaxResult remove(@PathVariable String[] flowIds) { + return toAjax(modelingDataFlowService.deleteDataFlowByIds(flowIds)); + } + + /** + * save + */ + @Log(title = "dataflow",operatorType = OperatorType.PLUGIN,businessType = BusinessType.UPDATE) + @PutMapping("/edit") + public AjaxResult edit(@RequestBody ModelingDataFlowEntity dataFlowData) { + if (dataFlowData.getOperatorContent() != null) { + if (dataFlowData.getOperatorContent().equals(modelingDataFlowService.getById(dataFlowData.getId()).getOperatorContent())) { + return AjaxResult.success("nothing changed"); + } else { + dataFlowData.setType("query"); + } + } + + return toAjax(modelingDataFlowService.updateDataFlow(dataFlowData)); + } + + /** + * distribution info for home page + */ + @GetMapping("/distribution") + public AjaxResult distribution() { + AjaxResult ajax = AjaxResult.success(); + ajax.put("distribution", modelingDataFlowService.queryGroupByType()); + return ajax; + } + + /** + * processInfo for home page + */ + @GetMapping("/processInfo") + public AjaxResult processInfo() { + AjaxResult ajax = AjaxResult.success(); + ajax.put("distribution", modelingDataFlowService.queryProcessInfo()); + return ajax; + } + + /** + * count for home page + */ + @GetMapping("/count") + public AjaxResult count() { + AjaxResult ajax = AjaxResult.success(); + ajax.put("count", modelingDataFlowService.count()); + return ajax; + } +} + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingDataFlowProcessController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingDataFlowProcessController.java new file mode 100644 index 000000000..e755248df --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingDataFlowProcessController.java @@ -0,0 +1,96 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.controller.modeling; + +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowEntity; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.opengauss.admin.plugin.service.modeling.IModelingDataBaseService; +import org.opengauss.admin.plugin.service.modeling.IModelingDataFlowService; +import org.opengauss.admin.plugin.service.modeling.operator.IModelingDataFlowProcessService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +/** + * @author LZW + * @date 2022/10/29 22:38 + */ +@RestController +@RequestMapping("/modeling/dataflow") +public class ModelingDataFlowProcessController extends BaseController { + + @Autowired + private IModelingDataFlowProcessService modelingDataFlowProcessService; + @Autowired + private IModelingDataBaseService modelingDataBaseService; + @Autowired + private IModelingDataFlowService modelingDataFlowService; + + @PostMapping(value = "/getSql") + public AjaxResult getSql(@RequestBody JSONObject param) { + AjaxResult ajax = AjaxResult.success(); + Map result = new HashMap<>(); + modelingDataFlowProcessService.getAllSqlObject(param).forEach(sqlObj -> { + result.put(sqlObj.getMainOperatorId(),sqlObj.toSql()); + }); + ajax.put("data",result); + return ajax; + } + + @PostMapping(value = "/runSql") + public AjaxResult runSql(@RequestBody JSONObject param) { + AjaxResult ajax = AjaxResult.success(); + Map result = new HashMap<>(); + modelingDataFlowProcessService.getAllSqlObject(param).forEach(sqlObj -> { + result.put(sqlObj.getMainOperatorId(),modelingDataBaseService.getSqlResult(sqlObj)); + }); + ajax.put("data",result); + return ajax; + } + + /** + * get result fields when sql running to target operator + */ + @PostMapping(value = "/getResultFieldsByOperator") + public AjaxResult getResultFieldsByOperator(@RequestBody JSONObject params) { + AjaxResult ajax = AjaxResult.success(); + ModelingDataFlowSqlObject sqlObject = modelingDataFlowProcessService.getSqlObjectByTargetOperatorId(params,params.getString("id"),params.getString("dataFlowId")); + List> result; + try { + result = modelingDataBaseService.queryWithSqlObject(sqlObject); + } catch (SQLException | ClassNotFoundException e) { + return AjaxResult.error("Database query error.Please check exception info : "+ e.getMessage()); + } + if (result.size() == 0) { + return AjaxResult.error("Query no data when running to this operator!"); + } + ajax.put("dimension_options", result.get(0).keySet()); + ajax.put("query_data", result); + return ajax; + } + +} + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingDataSourceDbController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingDataSourceDbController.java new file mode 100644 index 000000000..8aa73bce6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingDataSourceDbController.java @@ -0,0 +1,111 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.controller.modeling; + +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO; +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO; +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.service.modeling.IModelingDataBaseService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +/** + * @author LZW + * @date 2022/10/29 22:38 + */ +@RestController +@RequestMapping("/modeling/dataflow/dataSourceDb") +public class ModelingDataSourceDbController extends BaseController { + + @Autowired + private IModelingDataBaseService modelingDataSourceDbService; + + @GetMapping("/list") + public AjaxResult getList() { + List list = modelingDataSourceDbService.getClusterListWithDataBaseName(); + return AjaxResult.success(list); + } + + @GetMapping("/getSchemaByClusterNodeId/{dbName}/{clusterNodeId}") + public AjaxResult getSchemaByClusterNodeId(@PathVariable String dbName,@PathVariable String clusterNodeId) { + List list = modelingDataSourceDbService.getClusterList(); + List schemaList = new ArrayList<>(); + list.forEach(opsClusterVO -> { + opsClusterVO.getClusterNodes().forEach(node->{ + if (Objects.equals(node.getNodeId(), clusterNodeId)) { + String querySchemaSql = "select * from information_schema.schemata where catalog_name = '" + dbName + "';"; + try { + node.setDbName(dbName); + List> resultSet = modelingDataSourceDbService.executeWithClusterNode(node,querySchemaSql,null); + resultSet.forEach(ret->{ + schemaList.add((String) ret.get("schema_name")); + }); + } catch (SQLException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + }); + }); + return AjaxResult.success(schemaList); + } + + @GetMapping(value = "/getTablesBySchema/{dbName}/{clusterNodeId}/{schema}") + public AjaxResult getTablesBySchema(@PathVariable String dbName,@PathVariable String clusterNodeId, @PathVariable String schema) throws ClassNotFoundException, SQLException { + String sql = "SELECT tablename FROM pg_tables " + + "WHERE tablename NOT LIKE 'pg%' " + + "AND tablename NOT LIKE 'gs%' " + + "AND tablename NOT LIKE 'sql_%' " + + "AND schemaname='"+schema+"' " + + "ORDER BY tablename;"; + OpsClusterNodeVO node = modelingDataSourceDbService.getClusterNodeById(clusterNodeId); + if (node == null) { + return AjaxResult.success(new ArrayList<>()); + } + + node.setDbName(dbName); + + List> resultSet = modelingDataSourceDbService.executeWithClusterNodeAndSchema(node,schema,sql,null); + + return AjaxResult.success(resultSet); + } + + @GetMapping(value = "/getFieldsByTable/{dbName}/{clusterNodeId}/{schema}/{tableName}") + public AjaxResult getFieldsByTable(@PathVariable String dbName,@PathVariable String clusterNodeId, @PathVariable String schema, @PathVariable String tableName) throws ClassNotFoundException, SQLException { + String sql = "SELECT column_name AS name, data_type as type, is_nullable AS NOTNULL\n" + + "FROM information_schema.columns\n" + + "WHERE table_name='"+tableName+"' AND table_schema = '"+schema+"';"; + + OpsClusterNodeVO node = modelingDataSourceDbService.getClusterNodeById(clusterNodeId); + + node.setDbName(dbName); + + List> resultSet = modelingDataSourceDbService.executeWithClusterNodeAndSchema(node,schema,sql,null); + return AjaxResult.success(resultSet); + } + +} + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingDatabaseDesignController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingDatabaseDesignController.java new file mode 100644 index 000000000..0a05e41ab --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingDatabaseDesignController.java @@ -0,0 +1,41 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.controller.modeling; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.base.BaseController; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author LZW + * @date 2023/4/19 22:38 + */ +@RestController +@RequestMapping("/modeling/databaseDesign") +public class ModelingDatabaseDesignController extends BaseController { + /** + * count for home page + */ + @GetMapping("/count") + public AjaxResult count() { + AjaxResult ajax = AjaxResult.success(); + ajax.put("count", 0); + return ajax; + } +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationCustomDimensionController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationCustomDimensionController.java new file mode 100644 index 000000000..f5e6ded9f --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationCustomDimensionController.java @@ -0,0 +1,71 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.controller.modeling; + +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationCustomDimensionsEntity; +import org.opengauss.admin.plugin.domain.model.modeling.CustomDimension; +import org.opengauss.admin.plugin.service.modeling.visualization.IModelingVisualizationCustomDimensionsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + */ +@RestController +@RequestMapping("/modeling/dataflow/visualization/customDimension") +public class ModelingVisualizationCustomDimensionController extends BaseController { + + @Autowired + private IModelingVisualizationCustomDimensionsService modelingVisualizationCustomDimensionsService; + + @GetMapping("/getListByOperatorId/{operatorId}") + public AjaxResult getListByOperatorId(@PathVariable String operatorId) { + List list = modelingVisualizationCustomDimensionsService.getByOperatorId(operatorId); + return AjaxResult.success(list); + } + + /** + * add + */ + @RequestMapping("/add") + public AjaxResult add(@RequestBody CustomDimension customDimension) { + return toAjax(modelingVisualizationCustomDimensionsService.add(customDimension)); + } + + /** + * delete + */ + @DeleteMapping("/delete/{dimensionIds}") + public AjaxResult delete(@PathVariable String[] dimensionIds) { + return toAjax(modelingVisualizationCustomDimensionsService.deleteByIds(dimensionIds)); + } + + + /** + * save + */ + @PutMapping("/edit") + public AjaxResult edit(@RequestBody CustomDimension customDimension) { + return toAjax(modelingVisualizationCustomDimensionsService.update(customDimension)); + } + +} + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationParamsController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationParamsController.java new file mode 100644 index 000000000..21d9d8240 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationParamsController.java @@ -0,0 +1,173 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.controller.modeling; + +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.constant.Constants; +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.utils.http.HttpUtils; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationGeoFilesEntity; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationParamsEntity; +import org.opengauss.admin.plugin.service.modeling.IModelingVisualizationGeoFilesService; +import org.opengauss.admin.plugin.service.modeling.visualization.IModelingVisualizationParamsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +/** + * @author LZW + * @date 2022/10/29 22:38 + */ +@Slf4j +@RestController +@RequestMapping("/modeling/dataflow/visualization/params") +public class ModelingVisualizationParamsController extends BaseController { + + @Autowired + private IModelingVisualizationParamsService modelingVisualizationParamsService; + @Autowired + private IModelingVisualizationGeoFilesService modelingVisualizationGeoFilesService; + + @GetMapping("/getListByOperatorId/{operatorId}") + public AjaxResult getListByOperatorId(@PathVariable String operatorId) { + List list = modelingVisualizationParamsService.getByOperatorId(operatorId); + return AjaxResult.success(list); + } + + /** + * add + */ + @RequestMapping("/add") + public AjaxResult add(@RequestBody ModelingVisualizationParamsEntity modelingVisualizationParamsEntity) { + return toAjax(modelingVisualizationParamsService.add(modelingVisualizationParamsEntity)); + } + + /** + * delete + */ + @DeleteMapping("/delete/{flowIds}") + public AjaxResult delete(@PathVariable String[] flowIds) { + return toAjax(modelingVisualizationParamsService.deleteByIds(flowIds)); + } + + + /** + * save + */ + @PutMapping("/edit") + public AjaxResult edit(@RequestBody ModelingVisualizationParamsEntity modelingVisualizationParamsEntity) { + return toAjax(modelingVisualizationParamsService.update(modelingVisualizationParamsEntity)); + } + + @PostMapping("/generateChart/{paramsId}") + public AjaxResult generateChartByParamId(@PathVariable String paramsId) throws Exception { + ModelingVisualizationParamsEntity params = modelingVisualizationParamsService.getById(paramsId); + JSONObject paramsJson = JSONObject.parseObject(params.getParamsJson()); + + AjaxResult ajax = AjaxResult.success(); + ajax.put("chartData", modelingVisualizationParamsService.generateChart(paramsJson)); + return ajax; + } + + @PostMapping("/generateChart") + public AjaxResult generateChartByJson(@RequestBody JSONObject params) { + AjaxResult ajax = AjaxResult.success(); + try { + ajax.put("chartData", JSONObject.parse((modelingVisualizationParamsService.generateChart(params)))); + if (Objects.equals(params.getJSONObject("paramsData").getString("chartType"), "scatter")) { + //get geo config + ModelingVisualizationGeoFilesEntity geoFile = modelingVisualizationGeoFilesService.getById(params.getJSONObject("paramsData").getJSONObject("location").getLong("commonValue")); + JSONObject mapInfo = new JSONObject(); + mapInfo.put("name",geoFile.getRegisterName()); + mapInfo.put("data",JSONObject.parse(geoFile.getGeoJson())); + ajax.put("mapData", mapInfo); + } + } catch (Exception e) { + return AjaxResult.error("generate Chart error.Please check exception info : "+ e.getMessage()); + } + + return ajax; + } + + @PostMapping("/uploadGeo") + public AjaxResult upload(@RequestParam Long id, @RequestParam String name, @RequestParam("file") MultipartFile file) { + AjaxResult result = AjaxResult.success(); + ModelingVisualizationGeoFilesEntity newFile = new ModelingVisualizationGeoFilesEntity(); + newFile.setDataFlowId(id); + newFile.setName(file.getOriginalFilename()); + newFile.setRegisterName(name); + InputStreamReader isr = null; + InputStream in = null; + StringBuilder str = new StringBuilder(); + try { + in = file.getInputStream(); + isr = new InputStreamReader(in, StandardCharsets.UTF_8); + BufferedReader br = new BufferedReader(isr); + String s; + while ((s = br.readLine()) != null) { + str.append(s); + } + br.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + log.error("close input stream failed: " + e.getMessage()); + } + } + if (isr != null) { + try { + isr.close(); + } catch (IOException e) { + log.error("close input stream reader failed: " + e.getMessage()); + } + } + } + + newFile.setGeoJson(str.toString()); + + modelingVisualizationGeoFilesService.add(newFile); + return result; + } + + + @GetMapping("/getGeo/{dataFlowId}") + public AjaxResult getGeo(@PathVariable Long dataFlowId) { + AjaxResult result = AjaxResult.success(); + result.put("geoFiles",modelingVisualizationGeoFilesService.selectByDataFlowId(dataFlowId)); + return result; + } + + @DeleteMapping("/deleteGeo/{geoId}") + public AjaxResult deleteGeo(@PathVariable Long geoId) { + AjaxResult result = AjaxResult.success(); + modelingVisualizationGeoFilesService.removeById(geoId); + return result; + } + +} + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationReportController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationReportController.java new file mode 100644 index 000000000..78699142a --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationReportController.java @@ -0,0 +1,75 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.controller.modeling; + +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationReportsEntity; +import org.opengauss.admin.plugin.service.modeling.visualization.IModelingVisualizationReportsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + */ +@RestController +@RequestMapping("/modeling/dataflow/visualization/report") +public class ModelingVisualizationReportController extends BaseController { + + @Autowired + private IModelingVisualizationReportsService modelingVisualizationReportsService; + + @GetMapping("/getListByDataFlowId/{flowId}") + public AjaxResult getListByDataFlowId(@PathVariable Long flowId,@RequestParam(value = "name",required = false) String name) { + List list = modelingVisualizationReportsService.getByDataFlowIdAndName(flowId,name); + return AjaxResult.success(list); + } + + @GetMapping("/share/getByReportId/{reportId}") + public AjaxResult getByReportId(@PathVariable Long reportId) { + return AjaxResult.success(modelingVisualizationReportsService.getById(reportId)); + } + + /** + * add + */ + @RequestMapping("/add") + public AjaxResult add(@RequestBody ModelingVisualizationReportsEntity modelingVisualizationReports) { + return toAjax(modelingVisualizationReportsService.add(modelingVisualizationReports)); + } + + /** + * delete + */ + @DeleteMapping("/delete/{reportIds}") + public AjaxResult delete(@PathVariable String[] reportIds) { + return toAjax(modelingVisualizationReportsService.deleteByIds(reportIds)); + } + + + /** + * save + */ + @PutMapping("/update") + public AjaxResult edit(@RequestBody ModelingVisualizationReportsEntity modelingVisualizationReports) { + return toAjax(modelingVisualizationReportsService.update(modelingVisualizationReports)); + } + +} + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationShareController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationShareController.java new file mode 100644 index 000000000..61d6e48a8 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationShareController.java @@ -0,0 +1,52 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.controller.modeling; + +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.common.constant.Constants; +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.utils.http.HttpUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +/** + * @author LZW + * @date 2022/10/29 22:38 + */ +@RestController +@RequestMapping("/modeling/visualization/report/share") +public class ModelingVisualizationShareController extends BaseController { + + @GetMapping("/getMapGeo/{mapName}") + public AjaxResult getMapGeo(@PathVariable String mapName) { + AjaxResult ajax = AjaxResult.success(); + try { + String rspStr = HttpUtils.sendGet( "https://geojson.cn/api/data/china.json", Constants.GBK); + JSONObject mapInfo = new JSONObject(); + mapInfo.put("name",mapName); + mapInfo.put("data",JSONObject.parse(rspStr)); + ajax.put("mapData", mapInfo); + } catch (Exception e) { + return AjaxResult.error(e.getMessage()); + } + return ajax; + } + +} + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationSnapshotController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationSnapshotController.java new file mode 100644 index 000000000..2f93e9da8 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingVisualizationSnapshotController.java @@ -0,0 +1,69 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.controller.modeling; + +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationSnapshotsEntity; +import org.opengauss.admin.plugin.service.modeling.visualization.IModelingVisualizationSnapshotsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + */ +@RestController +@RequestMapping("/modeling/dataflow/visualization/snapshot") +public class ModelingVisualizationSnapshotController extends BaseController { + + @Autowired + private IModelingVisualizationSnapshotsService modelingVisualizationSnapshotsService; + + @GetMapping("/getListByDataFlowId/{flowId}") + public AjaxResult getListByDataFlowId(@PathVariable Long flowId) { + List list = modelingVisualizationSnapshotsService.getByDataFlowId(flowId); + return AjaxResult.success(list); + } + /** + * add + */ + @RequestMapping("/add") + public AjaxResult add(@RequestBody ModelingVisualizationSnapshotsEntity modelingVisualizationSnapshotsEntity) { + return toAjax(modelingVisualizationSnapshotsService.insertDataFlow(modelingVisualizationSnapshotsEntity)); + } + + /** + * delete + */ + @DeleteMapping("/delete/{snapshotIds}") + public AjaxResult delete(@PathVariable String[] snapshotIds) { + return toAjax(modelingVisualizationSnapshotsService.deleteByIds(snapshotIds)); + } + + + /** + * save + */ + @PutMapping("/edit") + public AjaxResult edit(@RequestBody ModelingVisualizationSnapshotsEntity modelingVisualizationSnapshotsEntity) { + return toAjax(modelingVisualizationSnapshotsService.updateDataFlow(modelingVisualizationSnapshotsEntity)); + } + +} + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingWorkFlowController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingWorkFlowController.java new file mode 100644 index 000000000..dc7dee2fd --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/modeling/ModelingWorkFlowController.java @@ -0,0 +1,41 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.controller.modeling; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.base.BaseController; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author LZW + * @date 2023/4/19 22:38 + */ +@RestController +@RequestMapping("/modeling/workflow") +public class ModelingWorkFlowController extends BaseController { + /** + * count for home page + */ + @GetMapping("/count") + public AjaxResult count() { + AjaxResult ajax = AjaxResult.success(); + ajax.put("count", 0); + return ajax; + } +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/BackupController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/BackupController.java new file mode 100644 index 000000000..becc796e6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/BackupController.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BackupController.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/BackupController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.controller.ops; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.service.ops.IOpsBackupService; +import org.opengauss.admin.plugin.vo.ops.BackupInputDto; +import org.opengauss.admin.plugin.vo.ops.BackupVO; +import org.opengauss.admin.plugin.vo.ops.RecoverInputDto; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * @author lhf + * @date 2022/11/5 09:49 + **/ +@RestController +@RequestMapping("/backup") +public class BackupController extends BaseController { + @Autowired + private IOpsBackupService backupService; + + @PostMapping("/backup") + public AjaxResult backup(@RequestBody @Validated BackupInputDto backup) { + backupService.backup(backup); + return AjaxResult.success(); + } + + @GetMapping("/page") + public AjaxResult pageBackup(@RequestParam(required = false, value = "clusterId") String clusterId) { + Page page = backupService.pageBackup(startPage(), clusterId); + return AjaxResult.success(page); + } + + @PostMapping("/recover/{id}") + public AjaxResult recover(@PathVariable("id") String id, @RequestBody RecoverInputDto recover) { + backupService.recover(id, recover); + return AjaxResult.success(); + } + + @DeleteMapping("/del/{id}") + public AjaxResult del(@PathVariable("id") String id) { + backupService.del(id); + return AjaxResult.success(); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/ClusterPackageManagerController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/ClusterPackageManagerController.java new file mode 100644 index 000000000..608e308b5 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/ClusterPackageManagerController.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterPackageManagerController.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/ClusterPackageManagerController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.controller.ops; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.dto.ops.OpsPackageDownloadDTO; +import org.opengauss.admin.common.core.dto.ops.OpsPackageUploadDTO; +import org.opengauss.admin.common.core.dto.ops.PackageDto; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.plugin.constant.OpsConstants; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackageManagerEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackagePathDictVO; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackageVO; +import org.opengauss.admin.plugin.service.ops.IOpsPackageManagerV2Service; +import org.opengauss.admin.plugin.service.ops.IOpsPackagePathDictService; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; +import java.util.Objects; + +/** + * ClusterPackageManagerController + * + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Slf4j +@RestController +@RequestMapping("/installPackageManager/v2") +public class ClusterPackageManagerController extends BaseController { + @Resource + private IOpsPackageManagerV2Service opsPackageManagerV2Service; + @Resource + private IOpsPackagePathDictService opsPackagePathDictService; + + @Value("${installPackage.urlPrefix}") + private String pkgUrlPrefix; + + @GetMapping("/check/online") + public AjaxResult checkOnline() { + return AjaxResult.success(opsPackagePathDictService.checkCurrentEnvironmentIsOnline()); + } + + @PostMapping("/page/package") + public TableDataInfo pagePackage(@RequestBody PackageDto dto) { + LambdaQueryWrapper queryWrapper = getOpsPackageQueryWrapper(dto); + IPage page = opsPackageManagerV2Service.page(startPage(), queryWrapper); + return getDataTable(page); + } + + @PostMapping("/list/package") + public List listPackage(@Valid @RequestBody PackageDto dto) { + return opsPackageManagerV2Service.list(getOpsPackageQueryWrapper(dto)); + } + + private LambdaQueryWrapper getOpsPackageQueryWrapper(PackageDto dto) { + LambdaQueryWrapper lambdaQuery = Wrappers.lambdaQuery(OpsPackageManagerEntity.class); + LambdaQueryWrapper queryWrapper = lambdaQuery + .like(StrUtil.isNotEmpty(dto.getName()), OpsPackageManagerEntity::getName, dto.getName()) + .eq(StrUtil.isNotEmpty(dto.getOs()), OpsPackageManagerEntity::getOs, dto.getOs()) + .eq(StrUtil.isNotEmpty(dto.getOsVersion()), OpsPackageManagerEntity::getOsVersion, dto.getOsVersion()) + .eq(StrUtil.isNotEmpty(dto.getCpuArch()), OpsPackageManagerEntity::getCpuArch, dto.getCpuArch()) + .eq(StrUtil.isNotEmpty(dto.getOpenGaussVersionNum()), OpsPackageManagerEntity::getPackageVersionNum, + dto.getOpenGaussVersionNum()) + .eq(Objects.nonNull(dto.getOpenGaussVersion()), OpsPackageManagerEntity::getPackageVersion, + dto.getOpenGaussVersion()) + .isNotNull(OpsPackageManagerEntity::getPackagePath) + .orderByDesc(OpsPackageManagerEntity::getUpdateTime); + return queryWrapper; + } + + @GetMapping("/list/version/number") + public AjaxResult listVersionNumber() { + return AjaxResult.success(opsPackageManagerV2Service.listVersionNumber()); + } + + /** + * check and return package version info + * + * @param packageDto packageDto + * @return AjaxResult + */ + @GetMapping("/check/version/number") + public AjaxResult checkVersionNumber(@ModelAttribute PackageDto packageDto) { + try { + Assert.isTrue(StrUtil.isNotEmpty(packageDto.getOs()), "os 不能为空"); + Assert.isTrue(StrUtil.isNotEmpty(packageDto.getCpuArch()), "cpuArch 不能为空"); + Assert.isTrue(StrUtil.isNotEmpty(packageDto.getOpenGaussVersionNum()), "packageVersionNum 不能为空"); + Assert.isTrue(Objects.nonNull(packageDto.getOpenGaussVersion()), "packageVersion 不能为空"); + Assert.isTrue(opsPackagePathDictService.checkOsExists(packageDto.getOs()), + "not support os param value " + packageDto.simple()); + Assert.isTrue(opsPackagePathDictService.checkCpuArchExists(packageDto.getCpuArch()), + "not support cpuArch param value " + packageDto.simple()); + + List list = opsPackageManagerV2Service.queryOpsPackageList(packageDto); + if (CollectionUtils.isNotEmpty(list)) { + return AjaxResult.success(list); + } else { + OpsPackagePathDictVO packageTmp = opsPackagePathDictService.queryPackagePathDict(packageDto); + if (Objects.nonNull(packageTmp)) { + OpsPackageVO opsPackageVO = opsPackagePathDictService.buildOpsPackage(pkgUrlPrefix, + packageDto.getOpenGaussVersionNum(), packageTmp); + return AjaxResult.success(opsPackageVO); + } else { + return AjaxResult.error("安装包字典不存当前版本 :" + packageDto.simple()); + } + } + } catch (IllegalArgumentException | OpsException e) { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * Delete package + * + * @param packageIds packageIds + * @return AjaxResult + */ + @PostMapping("/delete/package") + public AjaxResult del(@RequestBody List packageIds) { + opsPackageManagerV2Service.delPackage(packageIds); + return AjaxResult.success(); + } + + /** + * Check package list + * + * @param packageIds packageIds + * @return result + */ + @PostMapping("/check/package") + public AjaxResult checkingPackageList(@RequestBody List packageIds) { + try { + Assert.isTrue(CollectionUtils.isNotEmpty(packageIds), "packageIds 不能为空"); + opsPackageManagerV2Service.checkingPackageList(packageIds); + return AjaxResult.success(); + } catch (IllegalArgumentException | OpsException e) { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * check package + * + * @param packageId packageId + * @return boolean + */ + @PostMapping("/check/pkg") + public AjaxResult checkPackage(@RequestParam(name = "packageId") String packageId) { + try { + Assert.isTrue(StrUtil.isNotEmpty(packageId), "packageId 不能为空"); + return AjaxResult.success(opsPackageManagerV2Service.checkingPackage(packageId)); + } catch (IllegalArgumentException | OpsException e) { + return AjaxResult.error(e.getMessage()); + } + } + + @PostMapping("/save/online") + public AjaxResult online(@Valid @RequestBody OpsPackageDownloadDTO dto) { + Assert.isTrue(opsPackagePathDictService.checkCurrentEnvironmentIsOnline(), + "current environment is not online"); + Assert.isTrue(opsPackagePathDictService.checkOsExists(dto.getOs()), + "not support os param value " + dto.getOs()); + Assert.isTrue(opsPackagePathDictService.checkCpuArchExists(dto.getCpuArch()), + "not support cpuArch param value " + dto.getCpuArch()); + Assert.isTrue(!opsPackageManagerV2Service.hasName("", dto.getName()), + "package name [" + dto.getName() + "] already exists"); + OpsPackageManagerEntity entity = new OpsPackageManagerEntity(); + entity.setOs(dto.getOs()); + entity.setOsVersion(dto.getOsVersion()); + entity.setCpuArch(dto.getCpuArch()); + entity.setPackageVersion(dto.getOpenGaussVersion().name()); + entity.setPackageVersionNum(dto.getOpenGaussVersionNum()); + entity.setPackageUrl(dto.getDownloadUrl()); + entity.setName(dto.getName()); + entity.setType("openGauss"); + entity.setRemark(OpsConstants.PACKAGE_REMARK); + opsPackageManagerV2Service.saveOnlinePackage(entity, getUserId(), dto.getWsBusinessId()); + return AjaxResult.success(); + } + + @PostMapping("/update/online") + public AjaxResult online(@RequestParam("packageId") String packageId, @RequestParam("wsBusinessId") String wsBusinessId) { + Assert.isTrue(opsPackagePathDictService.checkCurrentEnvironmentIsOnline(), "current environment is not online"); + OpsPackageManagerEntity packageEntity = opsPackageManagerV2Service.getById(packageId); + Assert.isTrue(Objects.nonNull(packageEntity), "package [" + packageId + "] not exists"); + opsPackageManagerV2Service.updateOnlinePackage(packageEntity, getUserId(), wsBusinessId); + return AjaxResult.success(); + } + + + @PostMapping("/update/upload") + public AjaxResult upload(@RequestParam("packageId") String packageId, @RequestParam("uploadFile") MultipartFile uploadFile) { + Assert.isTrue(StrUtil.isNotEmpty(packageId), "packageId can not be empty"); + Assert.isTrue(Objects.nonNull(uploadFile), "uploadFile param can not be empty"); + OpsPackageManagerEntity pkg = opsPackageManagerV2Service.getById(packageId); + Assert.isTrue(Objects.nonNull(pkg), "package [" + packageId + "] not exists"); + pkg.setFile(uploadFile); + opsPackageManagerV2Service.updateUploadPackage(pkg, getUserId()); + return AjaxResult.success(); + } + + @PostMapping("/save/upload") + public AjaxResult upload(@ModelAttribute OpsPackageUploadDTO dto) { + Assert.isTrue(StrUtil.isNotEmpty(dto.getOs()), "os param can not be empty"); + Assert.isTrue(StrUtil.isNotEmpty(dto.getOsVersion()), "osVersion param can not be empty"); + Assert.isTrue(StrUtil.isNotEmpty(dto.getCpuArch()), "cpuArch param can not be empty"); + Assert.isTrue(StrUtil.isNotEmpty(dto.getName()), "package name param can not be empty"); + Assert.isTrue(Objects.nonNull(dto.getPackageVersion()), "package version param can not be empty"); + Assert.isTrue(StrUtil.isNotEmpty(dto.getPackageVersionNum()), "package version num param can not be empty"); + Assert.isTrue(opsPackagePathDictService.checkOsExists(dto.getOs()), + "not support os param value " + dto.getOs()); + Assert.isTrue(opsPackagePathDictService.checkCpuArchExists(dto.getCpuArch()), + "not support cpuArch param value " + dto.getCpuArch()); + OpsPackageManagerEntity pkg = new OpsPackageManagerEntity(); + BeanUtils.copyProperties(dto, pkg); + pkg.setType("openGauss"); + pkg.setPackageVersion(dto.getPackageVersion().name()); + pkg.setFile(dto.getUploadFile()); + pkg.setRemark(OpsConstants.PACKAGE_REMARK); + opsPackageManagerV2Service.saveUploadPackage(pkg, getUserId()); + return AjaxResult.success(); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/HostController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/HostController.java new file mode 100644 index 000000000..5b773f21b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/HostController.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostController.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/HostController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.controller.ops; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.plugin.service.ops.IHostService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author lhf + * @date 2023/2/14 13:58 + **/ +@RestController +@RequestMapping("/host") +public class HostController extends BaseController { + + @Autowired + private IHostService hostService; + + @GetMapping("/pathEmpty/{id}") + public AjaxResult pathEmpty(@PathVariable("id") String id,@RequestParam("path") String path,@RequestParam(value = "rootPassword",required = false) String rootPassword){ + return AjaxResult.success(hostService.pathEmpty(id,path,rootPassword)); + } + + @GetMapping("/portUsed/{id}") + public AjaxResult portUsed(@PathVariable("id") String id,@RequestParam("port") Integer port,@RequestParam(value = "rootPassword",required = false) String rootPassword){ + return AjaxResult.success(hostService.portUsed(id,port,rootPassword)); + } + + @GetMapping("/fileExist/{id}") + public AjaxResult fileExist(@PathVariable("id") String id,@RequestParam("file") String file,@RequestParam(value = "rootPassword",required = false) String rootPassword){ + return AjaxResult.success(hostService.fileExist(id,file,rootPassword)); + } + + /** + * query disk path + * + * @param id the host id + * @param rootPassword root user password + * @return list of disk path + */ + @GetMapping("/multiPathQuery/{id}") + public AjaxResult multiPathQuery(@PathVariable("id") String id, @RequestParam(value = "rootPassword", required = + false) String rootPassword) { + List lunPathList = hostService.multiPathQuery(id, rootPassword); + return AjaxResult.success(lunPathList); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/InstallPackageManagerController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/InstallPackageManagerController.java new file mode 100644 index 000000000..37d02cea1 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/InstallPackageManagerController.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * InstallPackageManagerController.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/InstallPackageManagerController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.controller.ops; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.UploadInfo; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackageManagerEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackageVO; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.service.ops.IOpsPackageManagerService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.beans.PropertyEditor; +import java.beans.PropertyEditorSupport; +import java.util.List; +import java.util.Objects; + +/** + * @author lhf + * @date 2022/12/11 16:04 + **/ +@Slf4j +@RestController +@RequestMapping("/installPackageManager") +public class InstallPackageManagerController extends BaseController { + + @Autowired + private IOpsPackageManagerService opsPackageManagerService; + @Value("${installPackage.urlPrefix}") + private String installPackageUrlPrefix; + + @PostMapping + public AjaxResult save(@ModelAttribute OpsPackageVO pkg) { + opsPackageManagerService.savePackage(pkg.toEntity(), getUserId()); + return AjaxResult.success(); + } + + @DeleteMapping("/{id}") + public AjaxResult del(@PathVariable("id") String id) { + opsPackageManagerService.delPackage(id); + return AjaxResult.success(); + } + + @PutMapping + public AjaxResult update(@ModelAttribute OpsPackageVO pkg) { + opsPackageManagerService.updatePackage(pkg.toEntity(), getUserId()); + return AjaxResult.success(); + } + + @GetMapping("/detail/{id}") + public AjaxResult detail(@PathVariable("id") String id) { + OpsPackageManagerEntity byId = opsPackageManagerService.getById(id); + return AjaxResult.success(byId); + } + + @GetMapping("/page") + public TableDataInfo page(@RequestParam(value = "name", required = false) String name, @RequestParam(value = "packageVersion", required = false) OpenGaussVersionEnum packageVersion) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsPackageManagerEntity.class) + .eq(Objects.nonNull(packageVersion), OpsPackageManagerEntity::getPackageVersion, packageVersion) + .orderByDesc(OpsPackageManagerEntity::getUpdateTime); + + if (StrUtil.isNotEmpty(name)) { + queryWrapper.and(orWrapper -> orWrapper + .or().like(OpsPackageManagerEntity::getName, name) + .or().like(OpsPackageManagerEntity::getOs, name) + .or().like(OpsPackageManagerEntity::getCpuArch, name) + .or().like(OpsPackageManagerEntity::getPackageVersionNum, name)); + } + + IPage page = opsPackageManagerService.page(startPage(), queryWrapper); + return getDataTable(page); + } + + @GetMapping("/list") + public AjaxResult list(@RequestParam(value = "os", required = false) String os, + @RequestParam(value = "osVersion", required = false) String osVersion, + @RequestParam(value = "cpuArch", required = false) String cpuArch, + @RequestParam(value = "packageVersion", required = false) OpenGaussVersionEnum packageVersion, + @RequestParam(value = "packageVersionNum", required = false) String packageVersionNum, + @RequestParam(value = "type", required = false) String type) { + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsPackageManagerEntity.class) + .eq(StrUtil.isNotEmpty(os), OpsPackageManagerEntity::getOs, os) + .eq(StrUtil.isNotEmpty(osVersion), OpsPackageManagerEntity::getOsVersion, osVersion) + .eq(StrUtil.isNotEmpty(cpuArch), OpsPackageManagerEntity::getCpuArch, cpuArch) + .eq(Objects.nonNull(packageVersion), OpsPackageManagerEntity::getPackageVersion, Objects.nonNull(packageVersion) ? packageVersion.name() : null) + .eq(StrUtil.isNotEmpty(packageVersionNum), OpsPackageManagerEntity::getPackageVersionNum, packageVersionNum) + .eq(StrUtil.isNotEmpty(type), OpsPackageManagerEntity::getType, type) + .isNotNull(OpsPackageManagerEntity::getPackageUrl) + .orderByDesc(OpsPackageManagerEntity::getPackageVersionNum); + List list = opsPackageManagerService.list(queryWrapper); + return AjaxResult.success(list); + } + + @GetMapping("/getCpuArch") + public AjaxResult getCpuArch(@RequestParam("installPackagePath") String installPackagePath, @RequestParam("version") OpenGaussVersionEnum version) { + String arch = opsPackageManagerService.getCpuArchByPackagePath(installPackagePath, version); + return AjaxResult.success(arch); + } + + @GetMapping("/analysisPkg") + public AjaxResult analysisPkg(@RequestParam String pkgName, @RequestParam String pkgType) { + OpsPackageVO result = opsPackageManagerService.analysisPkg(pkgName, pkgType); + return AjaxResult.success(result); + } + + @Deprecated + @PostMapping("/upload") + public AjaxResult upload(@RequestParam MultipartFile file) { + try { + UploadInfo info = opsPackageManagerService.upload(file, getUserId()); + return AjaxResult.success(info.toVO()); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } + + @DeleteMapping("/delPkgTar") + public AjaxResult deletePkgTar(@RequestParam String path, @RequestParam(required = false) String id) { + boolean result = opsPackageManagerService.deletePkgTar(path, id); + return result ? AjaxResult.success() : AjaxResult.error(); + } + + @GetMapping("/sysUploadPath") + public AjaxResult getSysUploadPath() { + String result = opsPackageManagerService.getSysUploadPath(getUserId()); + return AjaxResult.success("ok", result); + } + + @GetMapping("/hasName") + public AjaxResult hasName(@RequestParam("name") String name) { + return AjaxResult.success(opsPackageManagerService.hasName(name)); + } + + @InitBinder + public void initBinder(WebDataBinder binder) { + binder.registerCustomEditor(UploadInfo.class, new PropertyEditorSupport() { + @Override + public void setAsText(String text) throws IllegalArgumentException { + UploadInfo info = JSON.parseObject(text, UploadInfo.class); + setValue(info); + } + }); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpenLooKengController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpenLooKengController.java new file mode 100644 index 000000000..fb6f4487a --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpenLooKengController.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpenLooKengController.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpenLooKengController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.controller.ops; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.entity.ops.OpsAzEntity; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.plugin.domain.entity.ops.OpsOlkEntity; +import org.opengauss.admin.plugin.domain.model.ops.olk.InstallOlkBody; +import org.opengauss.admin.plugin.domain.model.ops.olk.OlkConfig; +import org.opengauss.admin.plugin.domain.model.ops.olk.OlkPageVO; +import org.opengauss.admin.plugin.domain.model.ops.olk.ShardingDatasourceConfig; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.ShardingRuleDto; +import org.opengauss.admin.plugin.service.ops.IOpsOlkService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/olk") +public class OpenLooKengController extends BaseController { + @Autowired + private IOpsOlkService opsOlkService; + + @PostMapping("/install") + public AjaxResult install(@RequestBody InstallOlkBody installBody) { + opsOlkService.install(installBody); + return AjaxResult.success(); + } + + @DeleteMapping("/remove/{id}") + public AjaxResult delete(@PathVariable("id") String id) { + opsOlkService.removeById(id); + return AjaxResult.success(); + } + + @DeleteMapping("/destroy/{id}") + public AjaxResult destroy(@PathVariable("id") String id, @RequestParam String bid) { + try { + opsOlkService.destroy(id, bid); + } catch (OpsException ex) { + return AjaxResult.error("Destroy OpenLooKeng Failed: " + ex.getMessage()); + } + return AjaxResult.success(); + } + + @PostMapping("/generateRuleYaml") + public AjaxResult generateRuleYaml(@RequestBody OlkConfig olkConfig) { + String result = opsOlkService.generateRuleYaml(olkConfig); + return AjaxResult.success("ok", result); + } + + @GetMapping("/start/{id}") + public AjaxResult start(@PathVariable("id") String id, @RequestParam String bid) { + try { + opsOlkService.start(id, bid); + } catch (OpsException ex) { + return AjaxResult.error("Start OpenLooKeng Failed: " + ex.getMessage()); + } + return AjaxResult.success(); + } + + @GetMapping("/stop/{id}") + public AjaxResult stop(@PathVariable("id") String id, @RequestParam String bid) { + try { + opsOlkService.stop(id, bid); + } catch (OpsException ex) { + return AjaxResult.error("Stop OpenLooKeng Failed: " + ex.getMessage()); + } + return AjaxResult.success(); + } + + @GetMapping("/page") + public TableDataInfo list(@RequestParam(required = false, value = "name") String name) { + IPage page = opsOlkService.pageOlk(startPage(), name); + return getDataTable(page); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterController.java new file mode 100644 index 000000000..6490dabbc --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterController.java @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterController.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.controller.ops; + +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO; +import org.opengauss.admin.common.core.domain.model.ops.check.CheckSummaryVO; +import org.opengauss.admin.common.enums.OsSupportMap; +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.plugin.domain.entity.ops.OpsImportEntity; +import org.opengauss.admin.plugin.domain.model.ops.*; +import org.opengauss.admin.plugin.domain.model.ops.env.HostEnv; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussSupportOSEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.service.ops.IOpsClusterService; +import org.opengauss.admin.plugin.service.ops.impl.BatchImportClusterService; +import org.opengauss.admin.plugin.service.ops.impl.ImportClusterService; +import org.opengauss.admin.plugin.vo.ops.*; +import org.opengauss.admin.plugin.vo.ops.SessionVO; +import org.opengauss.admin.plugin.vo.ops.SlowSqlVO; +import org.opengauss.admin.system.plugin.facade.OpsFacade; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Operation and maintenance cluster operations + * + * @author lhf + * @date 2022/8/6 17:38 + **/ +@RestController +@RequestMapping("/opsCluster") +public class OpsClusterController extends BaseController { + @Resource + private IOpsClusterService opsClusterService; + @Resource + private ImportClusterService importClusterService; + @Resource + private BatchImportClusterService batchImportClusterService; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private OpsFacade opsFacade; + + private List opsImportEntities = new ArrayList<>(); + + + @GetMapping("/hasName") + public AjaxResult hasName(@RequestParam("name") String name) { + boolean has = opsClusterService.hasName(name); + Map hasRes = new HashMap<>(); + hasRes.put("has", has ? "Y" : "N"); + return AjaxResult.success(hasRes); + } + + @PostMapping("/download") + public AjaxResult download(@RequestBody DownloadBody downloadBody) { + opsClusterService.download(downloadBody); + return AjaxResult.success(); + } + + @PostMapping("/install") + public AjaxResult install(@RequestBody InstallBody installBody) { + opsClusterService.install(installBody); + return AjaxResult.success(); + } + + @GetMapping("/upgradeOsCheck") + public AjaxResult upgradeOsCheck(@RequestParam String clusterId,@RequestParam(value = "rootPassword",required = false) String rootPassword) { + UpgradeOsCheckVO upgradeOsCheckVO = opsClusterService.upgradeOsCheck(clusterId,rootPassword); + return AjaxResult.success(upgradeOsCheckVO); + } + + @PostMapping("/upgrade") + public AjaxResult upgrade(@RequestBody UpgradeBody upgradeBody) { + opsClusterService.upgrade(upgradeBody); + return AjaxResult.success(); + } + + @PostMapping("/upgradeCommit") + public AjaxResult upgradeCommit(@RequestBody UpgradeBody upgradeBody) { + opsClusterService.upgradeCommit(upgradeBody); + return AjaxResult.success(); + } + + @PostMapping("/upgradeRollback") + public AjaxResult upgradeRollback(@RequestBody UpgradeBody upgradeBody) { + opsClusterService.upgradeRollback(upgradeBody); + return AjaxResult.success(); + } + + @PostMapping("/quickInstall") + public AjaxResult quickInstall(@RequestBody InstallBody installBody) { + opsClusterService.quickInstall(installBody); + return AjaxResult.success(); + } + + @PostMapping("/uninstall") + public AjaxResult uninstall(@RequestBody UnInstallBody unInstallBody) { + opsClusterService.uninstall(unInstallBody); + return AjaxResult.success(); + } + + @DeleteMapping("/remove/{clusterId}") + public AjaxResult remove(@PathVariable("clusterId") String clusterId) { + opsClusterService.removeCluster(clusterId); + return AjaxResult.success(); + } + + @PostMapping("/restart") + public AjaxResult restart(@RequestBody OpsClusterBody restartBody) { + opsClusterService.restart(restartBody); + return AjaxResult.success(); + } + + @PostMapping("/start") + public AjaxResult start(@RequestBody OpsClusterBody startBody) { + opsClusterService.start(startBody); + return AjaxResult.success(); + } + + @PostMapping("/stop") + public AjaxResult stop(@RequestBody OpsClusterBody stopBody) { + opsClusterService.stop(stopBody); + return AjaxResult.success(); + } + + @PostMapping("/ssh") + public AjaxResult ssh(@RequestBody SSHBody sshBody) { + opsClusterService.ssh(sshBody); + return AjaxResult.success(); + } + + @GetMapping("/ls") + public AjaxResult ls(@RequestParam String hostId, @RequestParam String path) { + List files = opsClusterService.ls(hostId, path); + return AjaxResult.success(files); + } + + @GetMapping("/logPath") + public AjaxResult logPath(@RequestParam String clusterId, @RequestParam String hostId) { + OpsNodeLogVO opsNodeLogVO = opsClusterService.logPath(clusterId, hostId); + return AjaxResult.success(opsNodeLogVO); + } + + @GetMapping("/auditLog") + public AjaxResult auditLog(@RequestParam String clusterId, @RequestParam String hostId, @RequestParam String start, @RequestParam String end) { + List auditLogVOList = opsClusterService.auditLog(startPage(), clusterId, hostId, start, end); + return AjaxResult.success(auditLogVOList); + } + + @GetMapping("/listSession") + public AjaxResult listSession(@RequestParam String clusterId, @RequestParam String hostId) { + List sessionVOList = opsClusterService.listSession(clusterId, hostId); + return AjaxResult.success(sessionVOList); + } + + @GetMapping("/slowSql") + public AjaxResult slowSql(@RequestParam String clusterId, @RequestParam String hostId, @RequestParam String start, @RequestParam String end) { + List slowSqlVOList = opsClusterService.slowSql(startPage(), clusterId, hostId, start, end); + return AjaxResult.success(slowSqlVOList); + } + + @GetMapping("/download") + public void download(@RequestParam String hostId, @RequestParam String path, @RequestParam String filename, HttpServletResponse response) { + opsClusterService.download(hostId, path, filename, response); + } + + @GetMapping("/listCluster") + public AjaxResult listCluster() { + return AjaxResult.success(opsFacade.listCluster()); + } + + @GetMapping("/summary") + public AjaxResult summary() { + ClusterSummaryVO clusterSummaryVO = opsClusterService.summary(); + return AjaxResult.success(clusterSummaryVO); + } + + @PostMapping("/import") + public AjaxResult importCluster(@RequestBody ImportClusterBody importClusterBody) { + importClusterService.importCluster(importClusterBody); + return AjaxResult.success(); + } + + /** + * download import cluster Template + * + * @param response downLoad Response + * @param currentLocale The current language version being used + */ + @GetMapping("/downloadImportFile/{currentLocale}") + public void downloadImportFile(@PathVariable String currentLocale, HttpServletResponse response) { + batchImportClusterService.downloadImportFile(response, currentLocale); + } + + @PostMapping("/uploadImportFile") + public AjaxResult uploadImportFile(MultipartFile file) { + opsImportEntities = batchImportClusterService.uploadImportFile(file); + return AjaxResult.success(); + } + + @GetMapping("/parseExcel") + public AjaxResult parseExcel() { + opsImportEntities = batchImportClusterService.parseExcel(opsImportEntities); + return AjaxResult.success(opsImportEntities.size()); + } + + @GetMapping("/importSuccessCount") + public AjaxResult importSuccessCount() { + return AjaxResult.success(batchImportClusterService.responseImportSuccessCount()); + } + + /** + * download import cluster Error Report + * + * @param response downLoad Response + * @param currentLocale The current language version being used + */ + @GetMapping("/downloadErrorFile/{currentLocale}") + public void downloadErrorFile(@PathVariable String currentLocale, HttpServletResponse response) { + batchImportClusterService.downloadErrorFile(response, opsImportEntities, currentLocale); + } + + @GetMapping("/monitor") + public AjaxResult monitor(@RequestParam String clusterId, @RequestParam String hostId, @RequestParam String businessId, @RequestParam(required = false, defaultValue = "MASTER")ClusterRoleEnum role) { + String tipMessage = opsClusterService.monitor(clusterId, hostId, businessId, role); + return AjaxResult.success(tipMessage); + } + + @GetMapping("/listHost") + public AjaxResult listHost(@RequestParam("clusterId") String clusterId) { + List hostEntities = opsClusterService.listClusterHost(clusterId); + return AjaxResult.success(hostEntities); + } + + @GetMapping("/check") + public AjaxResult check(@RequestParam String clusterId,@RequestParam(value = "rootPassword",required = false) String rootPassword) { + CheckSummaryVO checkSummaryVO = opsFacade.check(clusterId,rootPassword); + return AjaxResult.success(checkSummaryVO); + } + + @GetMapping("/generateconf") + public AjaxResult generateconf(@RequestParam String clusterId, @RequestParam String hostId, @RequestParam String businessId) { + opsClusterService.generateconf(clusterId, hostId, businessId); + return AjaxResult.success(); + } + + @GetMapping("/switchover") + public AjaxResult switchover(@RequestParam String clusterId, @RequestParam String hostId, @RequestParam String businessId) { + opsClusterService.switchover(clusterId, hostId, businessId); + return AjaxResult.success(); + } + + @GetMapping("/build") + public AjaxResult build(@RequestParam String clusterId, @RequestParam String hostId, @RequestParam String businessId) { + opsClusterService.build(clusterId, hostId, businessId); + return AjaxResult.success(); + } + + @GetMapping("/listInstallPackage") + public AjaxResult listInstallPackage(@RequestParam(value = "version", required = false) OpenGaussVersionEnum openGaussVersionEnum) { + ListDir listDir = opsClusterService.listInstallPackage(openGaussVersionEnum, getUserId()); + return AjaxResult.success(listDir); + } + + @GetMapping("/env/{hostId}") + public AjaxResult env(@PathVariable("hostId") String hostId, + @RequestParam(value = "expectedOs", defaultValue = "centos_X86_64") String expectedOsStr, + @RequestParam(required = false) String rootPassword) { + int tmpIndex = expectedOsStr.indexOf("_"); + OpenGaussSupportOSEnum expectedOs = OpenGaussSupportOSEnum.of( + expectedOsStr.substring(0, tmpIndex), + "", + expectedOsStr.substring(tmpIndex + 1) + ); + HostEnv hostEnv = opsClusterService.env(hostId, expectedOs, rootPassword); + return AjaxResult.success(hostEnv); + } + + @GetMapping("/listGucSetting") + public AjaxResult listGucSetting(@RequestParam("clusterId") String clusterId, @RequestParam("hostId") String hostId) { + List gucSettingList = opsClusterService.getGucSettingList(clusterId, hostId); + return AjaxResult.success(gucSettingList); + } + + @PostMapping("/batchConfigGucSetting") + public AjaxResult batchConfigGucSetting(@RequestBody GucSettingDto gucBody) { + opsClusterService.batchConfigGucSetting(gucBody); + return AjaxResult.success(); + } + + @GetMapping("/listEnterpriseCluster") + public AjaxResult listEnterpriseCluster() { + List result = opsClusterService.listEnterpriseCluster(); + return AjaxResult.success(result); + } + + @GetMapping + public AjaxResult threadPoolMonitor() { + Map res = opsClusterService.threadPoolMonitor(); + return AjaxResult.success(res); + } + + /** + * return supported os name list + */ + @GetMapping("/listSupportOsName") + public AjaxResult listSupportOsName() { + return AjaxResult.success(OsSupportMap.getSupportOsNameList()); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterLogController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterLogController.java new file mode 100644 index 000000000..4aebe61e9 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterLogController.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterLogController.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterLogController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.controller.ops; + +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskEntity; +import org.opengauss.admin.plugin.service.ops.IOpsClusterLogService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterTaskService; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * Cluster Log Controller + * + * @author wangchao + * @date 2024/6/22 9:41 + */ +@RestController +@RequestMapping("/opsClusterLog") +public class OpsClusterLogController extends BaseController { + private static final String OPERATE_LOG_FILE_TEMPLATE = "OPERATE_LOG_%s.log"; + @Resource + private IOpsClusterLogService opsClusterLogService; + @Resource + private IOpsClusterTaskService opsClusterTaskService; + + @GetMapping("/log") + public String queryClusterDetail(@RequestParam String clusterId) { + return opsClusterLogService.queryClusterOperateLog(clusterId); + } + + @GetMapping("/download/log") + public ResponseEntity downloadClusterDetail(@RequestParam String clusterId) { + OpsClusterTaskEntity taskEntity = opsClusterTaskService.getById(clusterId); + Assert.isTrue(Objects.nonNull(taskEntity), "task not exists"); + String context = opsClusterLogService.queryClusterOperateLog(clusterId); + String clusterName = taskEntity.getClusterName(); + String downloadFileName = String.format(OPERATE_LOG_FILE_TEMPLATE, clusterName); + return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, + "attachment;filename=\"" + downloadFileName + "\"") + .header("fileName", downloadFileName) + .body(context.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterTaskController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterTaskController.java new file mode 100644 index 000000000..089109728 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterTaskController.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskController.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterTaskController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.controller.ops; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.metadata.IPage; + +import org.apache.commons.collections4.CollectionUtils; +import org.opengauss.admin.common.annotation.Log; +import org.opengauss.admin.common.enums.BusinessType; +import org.opengauss.admin.common.utils.ip.IpUtils; +import org.opengauss.admin.plugin.base.BaseController; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskEntity; +import org.opengauss.admin.plugin.domain.model.ops.dto.OpsClusterTaskDTO; +import org.opengauss.admin.plugin.domain.model.ops.dto.OpsClusterTaskQueryParamDTO; +import org.opengauss.admin.plugin.service.ops.IOpsClusterTaskService; +import org.opengauss.admin.plugin.service.ops.impl.OpsHostRemoteService; +import org.springframework.util.Assert; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * cluster task operations + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@RestController +@RequestMapping("/clusterTask") +@Validated +public class OpsClusterTaskController extends BaseController { + @Resource + private IOpsClusterTaskService opsClusterTaskService; + @Resource + private OpsHostRemoteService opsHostRemoteService; + + /** + * query host info by ip + * + * @param hostIp host ip + * @return host info + */ + @GetMapping("/host/{hostIp}") + public AjaxResult host(@PathVariable String hostIp) { + Assert.isTrue(StrUtil.isNotEmpty(hostIp), "host ip cannot be empty"); + Assert.isTrue(IpUtils.mayBeIPAddress(hostIp), "ipaddress " + hostIp + "is invalid"); + return AjaxResult.success(opsClusterTaskService.getHostByIp(hostIp)); + } + + /** + * get az list + * + * @return az list + */ + @GetMapping("/list/az") + public AjaxResult listAz() { + return AjaxResult.success(opsHostRemoteService.listAllAz()); + } + + /** + * get az info by id + * + * @param azId azId + * @return az + */ + @GetMapping("/az/{azId}") + public AjaxResult getAzInfo(@PathVariable String azId) { + return AjaxResult.success(opsHostRemoteService.getAzById(azId)); + } + + /** + * query host info by os , osVersion, cpuArch + * + * @param os os + * @param osVersion osVersion + * @param cpuArch cpuArch + * @return host info + */ + @GetMapping("/host/list") + public AjaxResult hostList(@RequestParam(name = "os", required = false) String os, + @RequestParam(name = "osVersion", required = false) String osVersion, + @RequestParam(name = "cpuArch", required = false) String cpuArch) { + return AjaxResult.success(opsClusterTaskService.getHostList(os, osVersion, cpuArch)); + } + + /** + * query host user by host id + * + * @param hostId host id + * @return host user list + */ + @GetMapping("/hostUser/{hostId}") + public AjaxResult hostUser(@PathVariable String hostId) { + Assert.isTrue(StrUtil.isNotEmpty(hostId), "host id cannot be empty"); + Assert.isTrue(!StrUtil.equalsIgnoreCase(hostId, "null"), "host id cannot be empty"); + return AjaxResult.success(opsClusterTaskService.listHostUserByHostId(hostId)); + } + + /** + * check if the server port is occupied + * + * @param hostId host id + * @param hostPort host port + * @return check result + */ + @GetMapping("/hostPort/{hostId}/{hostPort}") + public AjaxResult hostPort(@RequestParam(name = "taskId") String taskId, @PathVariable String hostId, + @PathVariable Integer hostPort) { + Assert.isTrue(StrUtil.isNotEmpty(hostId), "host id cannot be empty"); + Assert.isTrue(hostPort >= 1024 && hostPort <= 65535, "server port must be between 1024 and 65535"); + return AjaxResult.success(opsClusterTaskService.checkHostPort(taskId, hostId, hostPort)); + } + + /** + * check cluster all port + * + * @param clusterId cluster id + * @return check result + */ + @GetMapping("/check/cluster/port") + public AjaxResult checkTaskHostPort(@RequestParam(name = "clusterId") String clusterId) { + Assert.isTrue(StrUtil.isNotEmpty(clusterId), "cluster id cannot be empty"); + return AjaxResult.success(opsClusterTaskService.checkTaskHostPort(clusterId)); + } + + /** + * check host path disk space + * + * @param hostId host + * @param paths paths + * @return check result + */ + @PostMapping("/check/host/disk/space/{hostId}") + public AjaxResult checkHostDiskSpace(@PathVariable String hostId, @RequestBody List paths) { + Assert.isTrue(StrUtil.isNotEmpty(hostId), "host id cannot be empty"); + Assert.isTrue(CollectionUtil.isNotEmpty(paths), "paths cannot be empty"); + return AjaxResult.success(opsClusterTaskService.checkHostDiskSpace(hostId, paths)); + } + + /** + * page query + * + * @param dto dto + * @return clusterTask/page + */ + @PostMapping("/page") + public TableDataInfo page(@RequestBody OpsClusterTaskQueryParamDTO dto) { + IPage page = opsClusterTaskService.pageByCondition(startPage(), dto); + if (Objects.isNull(page)) { + return getDataTable(); + } + return getDataTable(page); + } + + /** + * check clusterName is existed + * + * @param clusterName clusterName + * @return clusterNameExist + */ + @GetMapping("/check/name") + public AjaxResult checkClusterName(@RequestParam(name = "clusterName") String clusterName, + @RequestParam(name = "clusterId") String clusterId) { + Assert.isTrue(StrUtil.isNotEmpty(clusterName), "cluster name cannot be empty"); + return AjaxResult.success(opsClusterTaskService.checkClusterNameExist(clusterId, clusterName)); + } + + /** + * check if current host user has installed cluster + * + * @param hostIp host ip + * @param hostUsername host username + * @return check result + */ + @GetMapping("/check/host") + public AjaxResult checkHostUserCanInstallCluster(@RequestParam(name = "hostIp") String hostIp, + @RequestParam(name = "hostUsername") String hostUsername) { + Assert.isTrue(StrUtil.isNotEmpty(hostIp), "host ip cannot be empty"); + Assert.isTrue(StrUtil.isNotEmpty(hostUsername), "host username cannot be empty"); + Assert.isTrue(IpUtils.mayBeIPAddress(hostIp), "ip address " + hostIp + " is illegal"); + return AjaxResult.success(opsClusterTaskService.checkHostInstanceCanInstallClusterNode(hostIp, hostUsername)); + } + + /** + * query cluster task detail + * + * @param taskId taskId + * @return cluster task detail + */ + @GetMapping("/detail/{taskId}") + public AjaxResult queryClusterTaskDetail(@PathVariable("taskId") String taskId) { + Assert.isTrue(StrUtil.isNotEmpty(taskId), "task id cannot be empty"); + return AjaxResult.success(opsClusterTaskService.queryClusterTaskDetail(taskId)); + } + + /** + * create cluster task + * + * @param dto dto + * @return create result + */ + @Log(title = "cluster_task", businessType = BusinessType.INSERT) + @PostMapping("/create") + public AjaxResult createClusterTask(@RequestBody @Valid OpsClusterTaskDTO dto) { + try { + Assert.isTrue(Objects.nonNull(dto), "cluster task create param cannot be null"); + return AjaxResult.success(opsClusterTaskService.createClusterTask(dto)); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } + + /** + * check cluster task + * + * @param clusterId cluster id + * @return check result + */ + @PostMapping("/check") + public AjaxResult checkClusterTask(@RequestParam("clusterId") String clusterId) { + try { + Assert.isTrue(StrUtil.isNotEmpty(clusterId), "cluster id must be not empty"); + return AjaxResult.success(opsClusterTaskService.checkClusterTask(clusterId)); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } + + /** + * update cluster task + * + * @param dto dto + * @return update result + */ + @Log(title = "cluster_task", businessType = BusinessType.UPDATE) + @PostMapping("/update") + public AjaxResult updateClusterTask(@RequestBody @Valid OpsClusterTaskDTO dto) { + try { + Assert.isTrue(Objects.nonNull(dto), "cluster task update param cannot be null"); + Assert.isTrue(StrUtil.isNotEmpty(dto.getClusterId()), "cluster id cannot be empty"); + opsClusterTaskService.updateClusterTask(dto); + return AjaxResult.success(); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } + + /** + * batch delete cluster task + * + * @param taskIds taskIds + * @return task delete result + */ + @Log(title = "cluster_task", businessType = BusinessType.DELETE) + @PostMapping("/delete") + public AjaxResult deleteClusterTask(@RequestBody List taskIds) { + try { + Assert.isTrue(CollectionUtils.isNotEmpty(taskIds), "cluster task id list cannot be empty"); + Map delResult = opsClusterTaskService.deleteClusterTask(taskIds); + return AjaxResult.success(delResult); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } + + /** + * force delete task by task id + * + * @param taskId task id + * @return result + */ + @Log(title = "cluster_task", businessType = BusinessType.DELETE) + @PostMapping("/force/delete") + public AjaxResult deleteClusterTask(@RequestParam(name = "taskId") String taskId) { + try { + Assert.isTrue(StrUtil.isNotEmpty(taskId), "cluster task id cannot be empty"); + return AjaxResult.success(opsClusterTaskService.deleteClusterTaskForce(taskId)); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } + + /** + * confirm cluster task by task id + * + * @param taskId task id + * @return confirm result + */ + @Log(title = "cluster_task", businessType = BusinessType.CONFIRM) + @PostMapping("/confirm") + public AjaxResult confirmClusterTask(@RequestParam String taskId) { + try { + Assert.isTrue(StrUtil.isNotEmpty(taskId), "cluster task id cannot be empty"); + opsClusterTaskService.confirmClusterTask(taskId); + return AjaxResult.success(); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } + + /** + * cluster task of host environment check + * + * @param taskId cluster task id + * @return environment check result + */ + @Log(title = "cluster_task", businessType = BusinessType.CHECK_ENVIRONMENT) + @PostMapping("/check/environment") + public AjaxResult checkClusterEnvironment(@RequestParam String taskId) { + try { + Assert.isTrue(StrUtil.isNotEmpty(taskId), "cluster task id cannot be empty"); + opsClusterTaskService.checkClusterTask(taskId); + return AjaxResult.success(opsClusterTaskService.checkClusterInstallEnvironment(taskId)); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } + + /** + * cluster task of host environment check + * + * @param taskId cluster task id + * @return environment check result + */ + @PostMapping("/check/env/path") + public AjaxResult checkClusterEnvPath(@RequestParam String taskId) { + try { + Assert.isTrue(StrUtil.isNotEmpty(taskId), "cluster task id cannot be empty"); + return AjaxResult.success(opsClusterTaskService.checkClusterInstallEnvironment(taskId)); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } + + /** + * get cluster task of host environment check + * + * @param taskId taskId + * @return environment check result + */ + @GetMapping("/get/environment") + public AjaxResult getClusterEnvironment(@RequestParam String taskId) { + try { + Assert.isTrue(StrUtil.isNotEmpty(taskId), "cluster task id cannot be empty"); + return AjaxResult.success(opsClusterTaskService.getClusterInstallEnvironment(taskId)); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } + + /** + * batch install cluster task + * + * @param taskIds cluster task ids + * @return batch install result + */ + @Log(title = "cluster_task", businessType = BusinessType.BATCH_INSTALL) + @PostMapping("/batch/install") + public AjaxResult batchInstallCluster(@RequestBody List taskIds) { + try { + Assert.isTrue(CollectionUtils.isNotEmpty(taskIds), "cluster task ids cannot be empty"); + return AjaxResult.success(opsClusterTaskService.batchInstallCluster(taskIds)); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } + + @Log(title = "cluster_task", businessType = BusinessType.RE_INSTALL) + @PostMapping("/re/install") + public AjaxResult reInstallCluster(@RequestParam("taskId") String taskId) { + try { + Assert.isTrue(StrUtil.isNotEmpty(taskId), "cluster task id cannot be empty"); + opsClusterTaskService.reInstallCluster(taskId); + return AjaxResult.success(); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } + + /** + * get batch install cluster task status + * + * @param taskIds cluster task ids + * @return batch install status + */ + @PostMapping("/batch/install/status") + public AjaxResult getBatchInstallTaskStatus(@RequestBody List taskIds) { + try { + Assert.isTrue(CollectionUtils.isNotEmpty(taskIds), "cluster task ids cannot be empty"); + return AjaxResult.success(opsClusterTaskService.getBatchInstallTaskStatus(taskIds)); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } + + /** + * copy cluster task by old cluster task id + * + * @param taskId old cluster task id + * @return new cluster task id + */ + @Log(title = "cluster_task", businessType = BusinessType.COPY) + @PostMapping("/copy") + public AjaxResult copyClusterTask(@RequestParam String taskId) { + try { + Assert.isTrue(StrUtil.isNotEmpty(taskId), "cluster task id cannot be empty"); + return AjaxResult.success(opsClusterTaskService.copyClusterTask(taskId)); + } catch (OpsException ex) { + return AjaxResult.error(ex.getMessage()); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterTaskNodeController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterTaskNodeController.java new file mode 100644 index 000000000..d15037c85 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterTaskNodeController.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2024-2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskNodeController.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/OpsClusterTaskNodeController.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.plugin.controller.ops; + +import org.opengauss.admin.common.annotation.Log; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.enums.BusinessType; +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.plugin.domain.model.ops.dto.OpsClusterTaskNodeDTO; +import org.opengauss.admin.plugin.service.ops.impl.OpsClusterTaskNodeProviderService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.GetMapping; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; + +/** + * cluster task node operations + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@RestController +@RequestMapping("/cluster/task/node") +@Validated +public class OpsClusterTaskNodeController extends BaseController { + @Resource + private OpsClusterTaskNodeProviderService clusterTaskNodeProvider; + + + /** + * create cluster task node + * + * @param insertDto insertDto + * @return create result + */ + @Log(title = "cluster_task_node", businessType = BusinessType.INSERT) + @PostMapping("/create") + public AjaxResult createClusterTaskNode(@RequestBody @Valid OpsClusterTaskNodeDTO insertDto) { + return AjaxResult.success(clusterTaskNodeProvider.createClusterTaskNode(insertDto)); + } + + /** + * list cluster node list + * + * @param clusterId cluster id + * @return list + */ + @GetMapping("/list") + public AjaxResult listClusterTaskNode(@RequestParam("clusterId") String clusterId) { + return AjaxResult.success(clusterTaskNodeProvider.listClusterTaskNode(clusterId)); + } + + /** + * update cluster task + * + * @param dto dto + * @return update result + */ + @Log(title = "cluster_task_node", businessType = BusinessType.UPDATE) + @PostMapping("/update") + public AjaxResult updateClusterTaskNode(@RequestBody @Valid OpsClusterTaskNodeDTO dto) { + return AjaxResult.success(clusterTaskNodeProvider.updateClusterTaskNode(dto)); + } + + /** + * delete cluster task node + * + * @param clusterId clusterId + * @param nodeId nodeId + * @return task node delete result + */ + @Log(title = "cluster_task_node", businessType = BusinessType.DELETE) + @PostMapping("/delete") + public AjaxResult deleteClusterTaskNode(@RequestParam("clusterId") String clusterId, + @RequestParam("nodeId") String nodeId) { + return AjaxResult.success(clusterTaskNodeProvider.deleteClusterTaskNode(clusterId, nodeId)); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/WdrController.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/WdrController.java new file mode 100644 index 000000000..455367b64 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/WdrController.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WdrController.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/controller/ops/WdrController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.controller.ops; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.opengauss.admin.plugin.base.BaseController; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.domain.entity.ops.OpsWdrEntity; +import org.opengauss.admin.plugin.domain.model.ops.WdrGeneratorBody; +import org.opengauss.admin.plugin.enums.ops.WdrScopeEnum; +import org.opengauss.admin.plugin.enums.ops.WdrTypeEnum; +import org.opengauss.admin.plugin.service.ops.IOpsWdrService; +import org.opengauss.admin.plugin.vo.ops.DwrSnapshotVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.Date; +import java.util.List; + +/** + * @author lhf + * @date 2022/10/13 15:14 + **/ +@RestController +@RequestMapping("/wdr") +public class WdrController extends BaseController { + + @Autowired + private IOpsWdrService wdrService; + + @GetMapping("/listSnapshot") + public AjaxResult listSnapshot(@RequestParam String clusterId, @RequestParam String hostId) { + Page dwrSnapshotVOS = wdrService.listSnapshot(startPage(), clusterId, hostId); + return AjaxResult.success(dwrSnapshotVOS); + } + + @GetMapping("/createSnapshot") + public AjaxResult createSnapshot(@RequestParam String clusterId, @RequestParam String hostId) { + wdrService.createSnapshot(clusterId, hostId); + return AjaxResult.success(); + } + + @GetMapping("/list") + public AjaxResult list(@RequestParam String clusterId, + @RequestParam(required = false,value = "wdrScope") WdrScopeEnum wdrScope, + @RequestParam(required = false,value = "wdrType") WdrTypeEnum wdrType, + @RequestParam(required = false,value = "hostId") String hostId, + @RequestParam(required = false,value = "start") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date start, + @RequestParam(required = false,value = "end") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date end) { + List wdrList = wdrService.listWdr(clusterId, wdrScope, wdrType, hostId, start, end); + return AjaxResult.success(wdrList); + } + + @DeleteMapping("/del/{id}") + public AjaxResult del(@PathVariable("id") String id) { + wdrService.del(id); + return AjaxResult.success(); + } + + @PostMapping("/generate") + public AjaxResult generate(@RequestBody @Validated WdrGeneratorBody wdrGeneratorBody) { + wdrService.generate(wdrGeneratorBody); + return AjaxResult.success(); + } + + @GetMapping("/downloadWdr") + public void downloadWdr(@RequestParam String wdrId, HttpServletResponse response) { + wdrService.downloadWdr(wdrId,response); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/BaseEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/BaseEntity.java new file mode 100644 index 000000000..c9b6c138f --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/BaseEntity.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BaseEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/BaseEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Entity base class + * + * @author xielibo + */ +@Data +public class BaseEntity implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * search value + */ + @TableField(exist = false) + private String searchValue; + + /** + * creator + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * create time + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + /** + * updater + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * update time + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * remark + */ + private String remark; + + + @TableField(exist = false) + private Map params; + + public Map getParams() { + if (params == null) { + params = new HashMap<>(4); + } + return params; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowEntity.java new file mode 100644 index 000000000..f68109005 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowEntity.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.modeling; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.plugin.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author LZW + * @TableName modeling_data_flow + * @date 2022/10/29 22:38 + */ +@Data +@EqualsAndHashCode(callSuper=true) +@TableName(value ="modeling_data_flow") +public class ModelingDataFlowEntity extends BaseEntity { + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * data flow name + */ + private String name; + + /** + * query type (insert,delete,update,query) + */ + private String type; + + /** + * unique id + */ + private String uId; + + /** + * tag + */ + private String tags; + + /** + * remark + */ + private String remark; + + /** + * full operator config info + */ + private String operatorContent; + + /** + * default cluster id + */ + private String clusterId; + + /** + * default clusterNodeId + */ + private String clusterNodeId; + + /** + * default schema + */ + private String schema; + + /** + * default dbName + */ + private String dbName; + + private int queryCount; + + @TableField(exist = false) + private String orderBy; + + @TableField(exist = false) + private int count; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowOperatorEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowOperatorEntity.java new file mode 100644 index 000000000..b09c1ece6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowOperatorEntity.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowOperatorEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowOperatorEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.modeling; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.plugin.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author LZW + * @TableName modeling_data_flow_operator + * @date 2022/10/29 22:38 + */ +@Data +@EqualsAndHashCode(callSuper=true) +@TableName(value ="modeling_data_flow_operator") +public class ModelingDataFlowOperatorEntity extends BaseEntity { + + @TableId + private Object id; + + /** + * operator config name + */ + private String name; + + /** + * packagePath + */ + private String packagePath; + + /** + * enable in which type of query + */ + private Integer type; + + /** + * group in menu + */ + private Integer groupId; + + /** + * sort id + */ + private Integer sortId; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowOperatorGroupEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowOperatorGroupEntity.java new file mode 100644 index 000000000..ec195bc7f --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowOperatorGroupEntity.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowOperatorGroupEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowOperatorGroupEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.modeling; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.plugin.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author LZW + * @TableName modeling_data_flow_operator_group + * @date 2022/10/29 22:38 + */ +@Data +@EqualsAndHashCode(callSuper=true) +@TableName(value ="modeling_data_flow_operator_group") +public class ModelingDataFlowOperatorGroupEntity extends BaseEntity { + + @TableId + private Object id; + + /** + * group name in menu + */ + private String name; + + /** + * sort when display in menu + */ + private Object sortId; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowTypeEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowTypeEntity.java new file mode 100644 index 000000000..a2f333a72 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowTypeEntity.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowTypeEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingDataFlowTypeEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.modeling; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.plugin.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author LZW + * @date 2022/10/29 22:38 + * @TableName modeling_data_flow_type + */ +@Data +@EqualsAndHashCode(callSuper=true) +@TableName(value ="modeling_data_flow_type") +public class ModelingDataFlowTypeEntity extends BaseEntity { + + @TableId + private Object id; + + /** + * type name + */ + private String name; + + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationCustomDimensionsEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationCustomDimensionsEntity.java new file mode 100644 index 000000000..5ab611ced --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationCustomDimensionsEntity.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationCustomDimensionsEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationCustomDimensionsEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.modeling; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.plugin.domain.BaseEntity; +import org.opengauss.admin.plugin.domain.model.modeling.CustomDimension; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author LZW + * @TableName modeling_visualization_custom_dimensions + * @date 2022/10/29 22:38 + */ +@Data +@EqualsAndHashCode(callSuper=true) +@TableName(value ="modeling_visualization_custom_dimensions") +public class ModelingVisualizationCustomDimensionsEntity extends BaseEntity { + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * dimension name + */ + private String name; + + /** + * target field + */ + private String field; + + /** + * config json + */ + private String categoriesJson; + + /** + * belongs to which operator unique id + */ + private String operatorId; + + /** + * belongs to which data flow + */ + private Long dataFlowId; + + private Object sortId; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + public ModelingVisualizationCustomDimensionsEntity from(CustomDimension customDimension) { + + this.setId(customDimension.getId()); + this.setField(customDimension.getField()); + this.setName(customDimension.getName()); + this.setCategoriesJson(JSONObject.toJSONString(customDimension.getCategories())); + this.setOperatorId(customDimension.getOperatorId()); + this.setDataFlowId(customDimension.getDataFlowId()); + + return this; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationGeoFilesEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationGeoFilesEntity.java new file mode 100644 index 000000000..02750933e --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationGeoFilesEntity.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationGeoFilesEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationGeoFilesEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.modeling; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.plugin.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author LZW + * @TableName modeling_visualization_geo_files + * @date 2022/10/29 22:38 + */ +@Data +@EqualsAndHashCode(callSuper=true) +@TableName(value ="modeling_visualization_geo_files") +public class ModelingVisualizationGeoFilesEntity extends BaseEntity { + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * File name + */ + private String name; + + /** + * http url if exist + */ + private String filePath; + + /** + * geo full info json + */ + private String geoJson; + + /** + * register map name + */ + private String registerName; + + /** + * belongs to one dataflow + */ + private Long dataFlowId; + + private Object sortId; + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationParamsEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationParamsEntity.java new file mode 100644 index 000000000..ee256f9b9 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationParamsEntity.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationParamsEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationParamsEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.modeling; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.plugin.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import jakarta.validation.constraints.NotEmpty; +/** + * @author LZW + * @TableName modeling_visualization_params + * @date 2022/10/29 22:38 + */ +@Data +@EqualsAndHashCode(callSuper=true) +@TableName(value ="modeling_visualization_params") +public class ModelingVisualizationParamsEntity extends BaseEntity { + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * chart name + */ + private String name; + + /** + * chart type + */ + private String type; + + /** + * chart config params json + */ + private String paramsJson; + + /** + * belongs to one operator unique id + */ + @NotEmpty(message = "operator info not found") + private String operatorId; + + /** + * belongs to one dataflow + */ + @NotEmpty(message = "dataflow info not found") + private Long dataFlowId; + + private Object sortId; + + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationReportsEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationReportsEntity.java new file mode 100644 index 000000000..733d489a6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationReportsEntity.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationReportsEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationReportsEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.modeling; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.plugin.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author LZW + * @TableName modeling_visualization_reports + * @date 2022/10/29 22:38 + */ +@Data +@EqualsAndHashCode(callSuper=true) +@TableName(value ="modeling_visualization_reports") +public class ModelingVisualizationReportsEntity extends BaseEntity { + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * + */ + private String name; + + /** + * + */ + private String url; + + /** + * + */ + private String intro; + + /** + * + */ + private Long dataFlowId; + + /** + * + */ + private String paramsJson; + + /** + * + */ + private Long type; + + /** + * + */ + private String resourceUrl; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationSnapshotsEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationSnapshotsEntity.java new file mode 100644 index 000000000..2c60defa2 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationSnapshotsEntity.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationSnapshotsEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/modeling/ModelingVisualizationSnapshotsEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.modeling; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.plugin.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author LZW + * @TableName modeling_visualization_snapshots + * @date 2022/10/29 22:38 + */ +@Data +@EqualsAndHashCode(callSuper=true) +@TableName(value ="modeling_visualization_snapshots") +public class ModelingVisualizationSnapshotsEntity extends BaseEntity { + + @TableId(value = "id", type = IdType.AUTO) + private String id; + + /** + * + */ + private String name; + + /** + * + */ + private String imgBase64; + + /** + * + */ + private String chartDataJson; + + /** + * + */ + private Long dataFlowId; + + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsBackupEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsBackupEntity.java new file mode 100644 index 000000000..80d7ed47a --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsBackupEntity.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsBackupEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsBackupEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.plugin.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author lhf + * @date 2022/8/6 16:08 + **/ +@Data +@TableName("ops_backup") +@EqualsAndHashCode(callSuper = true) +public class OpsBackupEntity extends BaseEntity { + @TableId + private String backupId; + private String clusterId; + private String hostId; + private String clusterNodeId; + private String backupPath; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsCheckEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsCheckEntity.java new file mode 100644 index 000000000..5f805ba04 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsCheckEntity.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsCheckEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsCheckEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.plugin.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author lhf + * @date 2022/11/13 17:15 + **/ +@Data +@TableName("ops_check") +@EqualsAndHashCode(callSuper = true) +public class OpsCheckEntity extends BaseEntity { + private String checkId; + private String clusterId; + private String checkRes; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterEntity.java new file mode 100644 index 000000000..c80bed5ac --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterEntity.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.plugin.domain.BaseEntity; +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; +import org.opengauss.admin.plugin.enums.ops.InstallModeEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author lhf + * @date 2022/8/12 09:01 + **/ +@Data +@TableName("ops_cluster") +@EqualsAndHashCode(callSuper = true) +public class OpsClusterEntity extends BaseEntity { + @TableId + private String clusterId; + private OpenGaussVersionEnum version; + private String versionNum; + private InstallModeEnum installMode; + private DeployTypeEnum deployType; + private String clusterName; + private String installPackagePath; + private String databaseUsername; + private String databasePassword; + + private String installPath; + private String logPath; + private String tmpPath; + private String omToolsPath; + private String corePath; + private Integer port; + private Boolean enableDcf; + private Integer dcfPort; + private String envPath; + private String xmlConfigPath; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterNodeEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterNodeEntity.java new file mode 100644 index 000000000..08cbe2038 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterNodeEntity.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterNodeEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterNodeEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import org.opengauss.admin.plugin.domain.BaseEntity; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import lombok.Data; +import lombok.EqualsAndHashCode; + + +/** + * @author lhf + * @date 2022/8/18 09:12 + **/ +@Data +@TableName("ops_cluster_node") +@EqualsAndHashCode(callSuper = true) +public class OpsClusterNodeEntity extends BaseEntity { + @TableId + private String clusterNodeId; + private ClusterRoleEnum clusterRole; + private String hostId; + @TableField(exist = false) + private String rootPassword; + private String installUserId; + private String installPath; + private String dataPath; + private String pkgPath; + private Boolean installDemoDatabase; + private String clusterId; + /** + * Whether to install CM + */ + @TableField("is_install_cm") + private Boolean isInstallCM; + /** + * Whether it is the CM master node + */ + @TableField("is_cm_master") + private Boolean isCMMaster; + /** + * CM data path + */ + private String cmDataPath; + /** + * CM port + */ + private Integer cmPort; + /** + * data node xlog path + */ + private String xlogPath; + + private Boolean isEnableDss; + + private String dssDataLunLinkPath; + + private String xlogLunLinkPath; + + private String cmVotingLunLinkPath; + + private String cmSharingLunLinkPath; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterOperateLog.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterOperateLog.java new file mode 100644 index 000000000..f298db887 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterOperateLog.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterOperateLog.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterOperateLog.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.plugin.mapper.handler.LocalDateTimeTypeHandler; +import org.opengauss.admin.plugin.domain.BaseEntity; +import org.opengauss.admin.plugin.enums.ops.ClusterOperateTypeEnum; + +import java.time.LocalDateTime; + + +/** + * OpsClusterOperateLog + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Data +@TableName("ops_cluster_operate_log") +@EqualsAndHashCode(callSuper = true) +public class OpsClusterOperateLog extends BaseEntity { + @TableId + private String operateId; + private String clusterId; + private String clusterNodeId; + /** + * 创建,删除,修改,环境检查,确认,安装, + * 初始化,启动,停止,重启,升级,回滚,卸载 + */ + private ClusterOperateTypeEnum operateType; + /** + * 操作命令,执行记录 + */ + private String operateLog; + + @TableField(typeHandler = LocalDateTimeTypeHandler.class) + private LocalDateTime operateTime; + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterTaskEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterTaskEntity.java new file mode 100644 index 000000000..74ee8f420 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterTaskEntity.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskEntity.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterTaskEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.plugin.domain.BaseEntity; +import org.opengauss.admin.common.enums.ops.ClusterEnvCheckResultEnum; +import org.opengauss.admin.common.enums.ops.OpsClusterTaskStatusEnum; +import org.opengauss.admin.plugin.domain.model.ops.OpsClusterTaskVO; +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.springframework.beans.BeanUtils; + +/** + * OpsClusterTaskEntity + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Data +@TableName("ops_cluster_task") +@EqualsAndHashCode(callSuper = true) +public class OpsClusterTaskEntity extends BaseEntity { + + /** + * clusterId cluster task id ,and cluster id + */ + @TableId + @TableField(fill = FieldFill.INSERT) + private String clusterId; + + /** + * cluster task install servers host id + */ + private String hostId; + + @TableField(exist = false) + private String hostIp; + @TableField(exist = false) + private String displayHostIp; + /** + * cluster task install servers user id + */ + private String hostUserId; + + @TableField(exist = false) + private String hostUsername; + + /** + * server base info : os + */ + private String os; + /** + * server base info : cpu arch + */ + private String cpuArch; + + /** + * install package pre-filtering conditions : version + */ + private OpenGaussVersionEnum version; + /** + * install package pre-filtering conditions : version num + */ + private String versionNum; + /** + * install package pre-filtering conditions : package name + */ + private String packageName; + /** + * install package pre-filtering result : package id + */ + private String packageId; + + + /** + * cluster install base info : cluster name + */ + private String clusterName; + /** + * cluster install base info : database username + */ + private String databaseUsername; + /** + * cluster install base info : database password + */ + private String databasePassword; + /** + * cluster install base info : database port + */ + private Integer databasePort; + /** + * cluster install base info : install package path + */ + private String installPackagePath; + /** + * cluster install base info : install path + */ + private String installPath; + /** + * cluster install base info : log path + */ + private String logPath; + /** + * cluster install base info : tmp path + */ + private String tmpPath; + /** + * cluster install base info : om tools path + */ + private String omToolsPath; + /** + * cluster install base info : core path + */ + private String corePath; + + /** + * cluster install base info : env path + */ + private String envPath; + /** + * cluster install base info : enable install cm tool + */ + private Boolean enableCmTool; + private Boolean enableDcf; + /** + * cluster install base info : enable generate environment variable file + */ + private Boolean enableGenerateEnvironmentVariableFile; + /** + * cluster install base info : xml config path + */ + private String xmlConfigPath; + /** + * cluster install base info : cluster node num + */ + private Integer clusterNodeNum; + private DeployTypeEnum deployType; + private OpsClusterTaskStatusEnum status; + private ClusterEnvCheckResultEnum envCheckResult; + + public OpsClusterTaskVO toVo() { + OpsClusterTaskVO target = new OpsClusterTaskVO(); + BeanUtils.copyProperties(this, target); + return target; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterTaskNodeEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterTaskNodeEntity.java new file mode 100644 index 000000000..edf0a914b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterTaskNodeEntity.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskEntity.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsClusterTaskEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.plugin.domain.BaseEntity; +import org.opengauss.admin.common.enums.ops.ClusterEnvCheckResultEnum; +import org.opengauss.admin.plugin.domain.model.ops.OpsClusterTaskNodeVO; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.springframework.beans.BeanUtils; + + +/** + * OpsClusterTaskEntity + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Data +@TableName("ops_cluster_task_node") +@EqualsAndHashCode(callSuper = true) +public class OpsClusterTaskNodeEntity extends BaseEntity { + @TableId + @TableField(fill = FieldFill.INSERT) + private String clusterNodeId; + private String clusterId; + private String hostId; + private String hostUserId; + private ClusterRoleEnum nodeType; + private String dataPath; + private String azOwner; + private String azPriority; + private ClusterEnvCheckResultEnum envCheckResult; + private String envCheckDetail; + private Boolean isCmMaster; + private String cmDataPath; + private Integer cmPort; + public OpsClusterTaskNodeVO toVo() { + OpsClusterTaskNodeVO opsClusterNodeVO = new OpsClusterTaskNodeVO(); + BeanUtils.copyProperties(this, opsClusterNodeVO); + return opsClusterNodeVO; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsImportEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsImportEntity.java new file mode 100644 index 000000000..e9da3fc08 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsImportEntity.java @@ -0,0 +1,73 @@ +package org.opengauss.admin.plugin.domain.entity.ops; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.opengauss.admin.common.exception.ops.OpsException; + +import java.util.Objects; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@ColumnWidth(25) +public class OpsImportEntity { + @ExcelProperty(index = 0) + private String clusterName; + @ExcelProperty(index = 1) + private String privateIp; + @ExcelProperty(index = 2) + private String publicIp; + @ExcelProperty(index = 3) + private String installUsername; + @ExcelProperty(index = 4) + private String databaseUsername; + @ToString.Exclude + @ExcelProperty(index = 5) + private String databasePassword; + @ExcelProperty(index = 6) + private Integer port; + @ExcelProperty(index = 7) + private String envPath; + @ExcelProperty(index = 8) + private String importStatus; + @ExcelProperty(index = 9) + private String errorInfo; + + public void checkConfig() { + if (Objects.isNull(clusterName)) { + throw new OpsException("The clusterName is incorrect"); + } + + if (Objects.isNull(privateIp)) { + throw new OpsException("Incorrect privateIp"); + } + + if (Objects.isNull(publicIp)) { + throw new OpsException("Incorrect publicIp"); + } + + if (Objects.isNull(installUsername)) { + throw new OpsException("Incorrect installUsername"); + } + + if (Objects.isNull(databaseUsername)) { + throw new OpsException("Incorrect databaseUsername"); + } + + if (Objects.isNull(databasePassword)) { + throw new OpsException("Incorrect databasePassword"); + } + + if (Objects.isNull(port)) { + throw new OpsException("Incorrect port"); + } + + if (Objects.isNull(envPath)) { + throw new OpsException("Incorrect envPath"); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsImportSshEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsImportSshEntity.java new file mode 100644 index 000000000..c69f07f14 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsImportSshEntity.java @@ -0,0 +1,16 @@ +package org.opengauss.admin.plugin.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; + +@Data +public class OpsImportSshEntity { + @TableField(value = "host_id") + private Long hostId; + @TableField(value = "host_user_id") + private Long hostUserId; + @TableField(value = "port") + private Integer port; + @TableField(value = "password") + private String password; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsOlkEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsOlkEntity.java new file mode 100644 index 000000000..1736550f4 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsOlkEntity.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsOlkEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsOlkEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import org.opengauss.admin.plugin.domain.BaseEntity; +import org.opengauss.admin.plugin.domain.model.ops.olk.ShardingDatasourceConfig; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.OlkParamDto; + +import java.util.List; + +@Data +@TableName("ops_olk") +public class OpsOlkEntity extends BaseEntity { + @TableId + private String id; + private String name; + private String olkTarId; + private String ssTarId; + private String dadTarId; + private String dadPort; + private String dadInstallPath; + private String dadInstallHostId; + private String dadInstallUsername; + private String dadInstallPassword; + private String zkTarId; + private String ssPort; + private String olkPort; + private String zkPort; + private String ssInstallPath; + private String ssUploadPath; + private String olkInstallPath; + private String olkUploadPath; + private String ssInstallHostId; + private String olkInstallHostId; + private String ssInstallUsername; + private String olkInstallUsername; + @TableField(exist = false) + private String ssInstallPassword; + @TableField(exist = false) + private String olkInstallPassword; + private String tableName; + private String columns; + private String ruleYaml; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsPackageManagerEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsPackageManagerEntity.java new file mode 100644 index 000000000..26803d0f2 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsPackageManagerEntity.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsPackageManagerEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsPackageManagerEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.ops; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.common.core.domain.UploadInfo; +import org.opengauss.admin.plugin.domain.BaseEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackageVO; +import org.springframework.boot.jackson.JsonObjectDeserializer; +import org.springframework.boot.jackson.JsonObjectSerializer; +import org.springframework.web.multipart.MultipartFile; + +/** + * @author lhf + * @date 2022/12/11 16:11 + **/ +@Data +@TableName(value = "ops_package_manager", autoResultMap = true) +@EqualsAndHashCode(callSuper = true) +public class OpsPackageManagerEntity extends BaseEntity { + @TableId + private String packageId; + private String name; + @TableField(exist = false) + private String urlPrefix; + private String os; + private String osVersion; + private String cpuArch; + private String packageVersion; + private String packageVersionNum; + private String packageUrl; + @TableField(typeHandler = FastjsonTypeHandler.class) + private UploadInfo packagePath; + private String type; + private String source; + @TableField(exist = false) + @JsonIgnore + @JSONField(serialize = false, deserialize = false) + private MultipartFile file; + + public String getRealPath() { + if (ObjectUtil.isNull(packagePath)) { + return ""; + } + return packagePath.getRealPath(); + } + + public String getFileName() { + if (ObjectUtil.isNull(packagePath)) { + return ""; + } + return packagePath.getName(); + } + + public OpsPackageVO toVO() { + OpsPackageVO vo = new OpsPackageVO(); + vo.setName(name); + vo.setPackageId(packageId); + vo.setPackageVersion(packageVersion); + vo.setPackageVersionNum(packageVersionNum); + vo.setOs(os); + vo.setOsVersion(osVersion); + vo.setCpuArch(cpuArch); + vo.setRemark(getRemark()); + vo.setType(type); + vo.setSource(source); + vo.setPackageUrl(packageUrl); + vo.setPackagePath(packagePath); + vo.setUpdateTime(getUpdateTime()); + return vo; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsPackagePathDictEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsPackagePathDictEntity.java new file mode 100644 index 000000000..71fd0e18a --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsPackagePathDictEntity.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsPackagePathDictEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsPackagePathDictEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.plugin.domain.BaseEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackagePathDictVO; + +/** + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Data +@TableName(value = "ops_package_path_dict", autoResultMap = true) +@EqualsAndHashCode(callSuper = true) +public class OpsPackagePathDictEntity extends BaseEntity { + @TableId + private String id; + private String os; + private String osVersion; + private String cpuArch; + private String version; + private String urlPath; + private String pkgTmpUseVersion; + private String packageNameTmp; + + public OpsPackagePathDictVO toVO() { + OpsPackagePathDictVO vo = new OpsPackagePathDictVO(); + vo.setId(id); + vo.setVersion(version); + vo.setPackageNameTmp(packageNameTmp); + vo.setOs(os); + vo.setOsVersion(osVersion); + vo.setPkgTmpUseVersion(pkgTmpUseVersion); + vo.setCpuArch(cpuArch); + vo.setUrlPath(urlPath); + vo.setRemark(getRemark()); + return vo; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsParseExcelEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsParseExcelEntity.java new file mode 100644 index 000000000..f265a1e98 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsParseExcelEntity.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsParseExcelEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsParseExcelEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.ops; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import org.opengauss.admin.system.plugin.beans.SshLogin; + +/** + * ParseClusterExcel properties + * + * @author ybx + * @version 1.0 + * @date: 2024/8/5 21:13 PM + * @since JDK 11 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class OpsParseExcelEntity { + private String versionType; + private SshLogin sshLogin; + private OpsImportEntity opsImportEntity; + private OpsImportSshEntity hostAndUserId; + private String publicIp; + private int ipSequence; + private int ipNum; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsWdrEntity.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsWdrEntity.java new file mode 100644 index 000000000..67aedbe1c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsWdrEntity.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsWdrEntity.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/entity/ops/OpsWdrEntity.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.entity.ops; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.opengauss.admin.plugin.domain.BaseEntity; +import org.opengauss.admin.plugin.enums.ops.WdrScopeEnum; +import org.opengauss.admin.plugin.enums.ops.WdrTypeEnum; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * @author lhf + * @date 2022/10/13 15:03 + **/ +@Data +@TableName("ops_wdr") +@EqualsAndHashCode(callSuper = true) +public class OpsWdrEntity extends BaseEntity { + @TableId + private String wdrId; + private WdrScopeEnum scope; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date reportAt; + private WdrTypeEnum reportType; + private String reportName; + private String reportPath; + private String clusterId; + private String nodeId; + private String hostId; + private String userId; + private String startSnapshotId; + private String endSnapshotId; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/CustomDimension.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/CustomDimension.java new file mode 100644 index 000000000..5445ab68c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/CustomDimension.java @@ -0,0 +1,77 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling; + +import org.opengauss.admin.plugin.vo.modeling.Categories; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + */ +public class CustomDimension { + + private Long dataFlowId; + private String operatorId; + private String name; + private String field; + private List categories; + private Long id; + public void setDataFlowId(Long dataFlowId) { + this.dataFlowId = dataFlowId; + } + public Long getDataFlowId() { + return dataFlowId; + } + + public void setOperatorId(String operatorId) { + this.operatorId = operatorId; + } + public String getOperatorId() { + return operatorId; + } + + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + + public void setField(String field) { + this.field = field; + } + public String getField() { + return field; + } + + public void setCategories(List categories) { + this.categories = categories; + } + public List getCategories() { + return categories; + } + + public void setId(Long id) { + this.id = id; + } + public Long getId() { + return id; + } + + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/ModelingDataFlowLineParams.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/ModelingDataFlowLineParams.java new file mode 100644 index 000000000..87381508c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/ModelingDataFlowLineParams.java @@ -0,0 +1,37 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author LZW + */ +@Data +public class ModelingDataFlowLineParams implements Serializable { + + private String uid; + + private Integer level; + + private String source; + + private String target; + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/ModelingDataFlowOperatorParams.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/ModelingDataFlowOperatorParams.java new file mode 100644 index 000000000..b88cfa913 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/ModelingDataFlowOperatorParams.java @@ -0,0 +1,115 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling; + +import com.alibaba.fastjson.JSONObject; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; +/** + * @author LZW + * @date 2022/10/29 22:38 + */ +@Data +public class ModelingDataFlowOperatorParams implements Serializable { + + private String uid; + + private String cellType; + + private Integer level; + + private Boolean disable; + + private JSONObject data; + + private String belongsToMainOperatorId; + + private ModelingDataFlowOperatorParams dataSource; + + public void initDataSource(List operatorParamsList, List lineParamsList) { + ModelingDataFlowLineParams foreLine = null; + for (ModelingDataFlowLineParams line: lineParamsList) { + if (Objects.equals(line.getTarget(), this.getUid())) + { + line.setLevel(this.getLevel() + 1); + foreLine = line; + } + } + + if (foreLine != null) + { + for (ModelingDataFlowOperatorParams operator:operatorParamsList) { + if (Objects.equals(operator.getUid(), foreLine.getSource()) && Objects.equals(operator.getCellType(), "dataSource")) + { + this.setDataSource(operator); + } + } + } + + } + + public void sortFore(List operatorParamsList,List lineParamsList) + { + ModelingDataFlowLineParams foreLine = null; + for (ModelingDataFlowLineParams line: lineParamsList) { + if (Objects.equals(line.getTarget(), this.getUid())) + { + line.setLevel(this.getLevel() + 1); + foreLine = line; + } + } + + if (foreLine != null) + { + for (ModelingDataFlowOperatorParams operator:operatorParamsList) { + if (Objects.equals(operator.getUid(), foreLine.getSource())) + { + operator.setLevel(foreLine.getLevel() + 1); + operator.sortFore(operatorParamsList,lineParamsList); + } + } + } + } + + public void sortChildren(List operatorParamsList,List lineParamsList) + { + ModelingDataFlowLineParams nextLine = null; + for (ModelingDataFlowLineParams line: lineParamsList) { + if (Objects.equals(line.getSource(), this.getUid())) + { + line.setLevel(this.getLevel() + 1); + nextLine = line; + } + } + + if (nextLine != null) + { + for (ModelingDataFlowOperatorParams operator:operatorParamsList) { + if (Objects.equals(operator.getUid(), nextLine.getTarget())) + { + operator.setBelongsToMainOperatorId(this.getBelongsToMainOperatorId()); + operator.setLevel(nextLine.getLevel() + 1); + operator.sortChildren(operatorParamsList,lineParamsList); + } + } + } + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/ModelingDataFlowSqlObject.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/ModelingDataFlowSqlObject.java new file mode 100644 index 000000000..2fc8c6eed --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/ModelingDataFlowSqlObject.java @@ -0,0 +1,314 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling; + +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO; +import lombok.Data; + +import java.io.Serializable; +import java.sql.Date; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +/** + * @author LZW + * @date 2022/10/29 22:38 + */ +@Data +public class ModelingDataFlowSqlObject implements Serializable { + private String mainType; + + private String mainOperatorId; + + private String preparedSql; + + private String executeDbName; + + private String executeClusterId; + + private String executeClusterNodeId; + + private String executeSchema; + + private OpsClusterNodeVO executeClusterNode; + + public List preparedParams = new ArrayList<>(); + + public String addParam(String param) { + int index = preparedParams.size(); + preparedParams.add(param); + return "#" + index + "#"; + } + + public String addParamList(List paramList) { + StringBuilder sb = new StringBuilder(); + for (String param : paramList) { + sb.append(addParam(param)).append(","); + } + sb.deleteCharAt(sb.length() - 1); + return sb.toString(); + } + + private String finalSql; + + private String selectRegion = "select "; + + private String updateRegion = "update "; + + private String deleteRegion = "delete from "; + + private String insertRegion = "insert into "; + + private String insertFieldsRegion = " values "; + + private String updateFieldsRegion = "set "; + + private String fromRegion = ""; + + private String joinRegion = ""; + + private String whereRegion = ""; + + private String groupRegion = ""; + + private String orderByRegion = ""; + + private String limitRegion = ""; + + private String polymerizationRegion = ""; + + private List mappingRegion = new ArrayList<>(); + + + public ModelingDataFlowSqlObject initDataBase(OpsClusterNodeVO node, String defaultSchema) { + if (this.getExecuteSchema() == null) { + this.setExecuteSchema(defaultSchema); + } + if (node == null) { + throw new RuntimeException("cluster node info not found!"); + } + this.setExecuteClusterNode(node); + + return this; + } + + public String toSql() { + return getFinalSql(); + } + + public String getSqlWithType() { + String sql; + switch (this.getMainType()) { + case "query": + sql = toQuerySql(); + break; + case "update": + sql = toUpdateSql(); + break; + case "delete": + sql = toDeleteSql(); + break; + case "insert": + sql = toInsertSql(); + break; + default: + sql = ""; + } + return sql; + } + + public String toQuerySql() + { + sqlObjectPretreatment(); + return (this.getSelectRegion() + this.getFromRegion() + this.getJoinRegion() + this.getWhereRegion() + this.getGroupRegion() + this.getOrderByRegion() + this.getLimitRegion()).trim()+";"; + } + + public String toDeleteSql() + { + return ( this.getDeleteRegion() + this.getWhereRegion() + this.getLimitRegion() ).trim()+";"; + } + + public String toInsertSql() + { + return ( this.getInsertRegion() + this.getInsertFieldsRegion() ).trim()+";"; + } + + public String toUpdateSql() + { + return ( this.getUpdateRegion() + this.getUpdateFieldsRegion() + this.getWhereRegion() + this.getOrderByRegion() + this.getLimitRegion()).trim()+";"; + } + + /** + * sql pretreatment + */ + private void sqlObjectPretreatment() { + groupRegionHandle(); + polymerizationRegionHandle(); + mappingRegionHandle(); + joinRegionHandle(); + limitRegionHandle(); + } + + /** + * modify select region for group + */ + private void groupRegionHandle() { + if (this.getGroupRegion().length() > 0) { + this.setSelectRegion("select "+this.getGroupRegion().substring(9)); + } + } + + /** + * modify select region for polymerization + */ + private void polymerizationRegionHandle() { + if (this.getPolymerizationRegion().length()>0) { + if (this.getGroupRegion().length() > 0) { + this.setSelectRegion(this.getSelectRegion().trim()+","+this.getPolymerizationRegion()); + } else { + this.setSelectRegion("select "+this.getPolymerizationRegion()); + } + } + } + + /** + * if exist join region then modify select [If there are fields with the same name +(1/2/3...)] + */ + private void joinRegionHandle() { + if (this.getJoinRegion().length() > 0) { + String fieldsOldString = this.getSelectRegion().substring(7).trim(); + + String[] fieldsOldArr = fieldsOldString.split(",(?![^()]*\\))"); + if (fieldsOldArr.length < 2) { + return; + } + List fieldsNewList = new ArrayList<>(); + Map fieldsOldAllList = new HashMap<>(); + int fieldRepeat = 0; + for (String fieldOld : fieldsOldArr) { + String[] tableField = fieldOld.split("\\."); + if (tableField.length == 2) { + String tableName = tableField[0]; + String field = tableField[1]; + if (fieldsOldAllList.get(field) == null) { + fieldsOldAllList.put(field, 0); + fieldsNewList.add(fieldOld); + } else { + fieldRepeat = 1; + int repeatTimes = fieldsOldAllList.get(field) + 1; + fieldsOldAllList.put(field, repeatTimes); + fieldsNewList.add(tableName + "." + field + " as " + field + "_" + repeatTimes); + } + } + } + if (fieldRepeat == 1) { + // if repeat + String newFieldSql = String.join(",", fieldsNewList); + this.setSelectRegion("select " + newFieldSql + " "); + } + } + } + + + /** + * default limit 1000 + */ + private void limitRegionHandle() { + if (this.getLimitRegion().length()<1) { + this.setLimitRegion(" limit 1000"); + } + } + + /** + * if mapping region exist then modify select region + */ + private void mappingRegionHandle() { + List mappingRegionTemp = this.getMappingRegion(); + if (mappingRegionTemp.size()>0) { + //if mapping region exist + String fieldsOldString = this.getSelectRegion().substring(7).trim(); + + String fieldAll = "*"; + if (fieldsOldString.equals(fieldAll)) { + this.setSelectRegion(this.getSelectRegion().trim()+","+String.join(",",this.getMappingRegion())+" "); + } else { + String[] fieldsOldArr = fieldsOldString.split(","); + List fieldsOldList = Arrays.asList(fieldsOldArr); + List fieldsNewList = new ArrayList<>(fieldsOldList); + + for (String mappingItem : mappingRegionTemp) { + String mappingField = mappingItem.split(" ")[0]; + int indexSame = fieldsNewList.indexOf(mappingField); + if (indexSame != -1) { + // replace if include + fieldsNewList.set(indexSame, mappingItem); + } else { + // add if not include + fieldsNewList.add(mappingItem); + } + } + this.setSelectRegion("select "+String.join(",",fieldsNewList)+" "); + } + } + } + + public ModelingDataFlowSqlObject doPrepare() { + List sortParamsList = new ArrayList<>(preparedParams.size()); + String oriSql = this.getSqlWithType(); + //find #index# sort params + List result = regStrip("#\\d+#",oriSql); + result.forEach(index -> { + sortParamsList.add(preparedParams.get(Integer.parseInt(index))); + }); + preparedParams = sortParamsList; + //replace format #index# with ? + preparedSql = oriSql.replaceAll("#\\d+#","?"); + + finalSql = getRealSql(preparedSql,preparedParams); + return this; + } + + public static List regStrip(String patten,String textArea) { + Pattern compile = Pattern.compile(patten); + Matcher matcher = compile.matcher(textArea); + List targetList = new ArrayList(); + while (matcher.find()) { + String substring = textArea.substring(matcher.start()+1, matcher.end()-1); + targetList.add(substring); + } + return targetList; + } + + public static String getRealSql(String sql, List params) { + + if(params == null || params.size() == 0) { + return sql; + } + + Pattern pattern = Pattern.compile("\\?"); + Matcher matcher = pattern.matcher(sql); + StringBuffer sb = new StringBuffer(); + int index = 0; + while (matcher.find()) { + matcher.appendReplacement(sb, "'" + params.get(index++) + "'"); + } + matcher.appendTail(sb); + + return sb.toString(); + + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/OpenGaussConnectorBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/OpenGaussConnectorBody.java new file mode 100644 index 000000000..4a71b48da --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/OpenGaussConnectorBody.java @@ -0,0 +1,53 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling; + +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO; +import lombok.Data; + +/** + * + * @author LZW + */ +@Data +public class OpenGaussConnectorBody { + + private String id; + + private String ip; + + private Integer port; + + private String database; + + private String dbUser; + + private String dbPassword; + + private String schema; + + private static final long serialVersionUID = 1L; + + public OpenGaussConnectorBody(OpsClusterNodeVO clusterNodeVO) { + this.setIp(clusterNodeVO.getPublicIp()); + this.setPort(clusterNodeVO.getDbPort()); + this.setDatabase(clusterNodeVO.getDbName()); + this.setDbUser(clusterNodeVO.getDbUser()); + this.setDbPassword(clusterNodeVO.getDbUserPassword()); + + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/BarChartBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/BarChartBody.java new file mode 100644 index 000000000..01318c835 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/BarChartBody.java @@ -0,0 +1,95 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart; + +import org.opengauss.admin.plugin.vo.modeling.component.*; + +import java.util.List; + +/** + * + * @author LZW + */ + +public class BarChartBody { + + private Title title; + private Tooltip tooltip; + private Legend legend; + private Grid grid; + private List dataZoom; + private XAxis xAxis; + private YAxis yAxis; + private List series; + public void setTitle(Title title) { + this.title = title; + } + public Title getTitle() { + return title; + } + + public void setTooltip(Tooltip tooltip) { + this.tooltip = tooltip; + } + public Tooltip getTooltip() { + return tooltip; + } + + public void setLegend(Legend legend) { + this.legend = legend; + } + public Legend getLegend() { + return legend; + } + + public void setGrid(Grid grid) { + this.grid = grid; + } + public Grid getGrid() { + return grid; + } + + public void setXAxis(XAxis xAxis) { + this.xAxis = xAxis; + } + public XAxis getXAxis() { + return xAxis; + } + + public void setYAxis(YAxis yAxis) { + this.yAxis = yAxis; + } + public YAxis getYAxis() { + return yAxis; + } + + public void setSeries(List series) { + this.series = series; + } + public List getSeries() { + return series; + } + + public List getDataZoom() { + return dataZoom; + } + + public void setDataZoom(List dataZoom) { + this.dataZoom = dataZoom; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/GraphChartBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/GraphChartBody.java new file mode 100644 index 000000000..4ab79e2d0 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/GraphChartBody.java @@ -0,0 +1,51 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart; + +import lombok.Data; +import org.opengauss.admin.plugin.vo.modeling.component.GraphSeries; +import org.opengauss.admin.plugin.vo.modeling.component.Title; +import org.opengauss.admin.plugin.vo.modeling.component.Tooltip; + +import java.util.List; +/** + * + * @author LZW + */ +@Data +public class GraphChartBody { + + private Title title; + private Tooltip tooltip; + private List series; + + public void setTitle(Title title) { + this.title = title; + } + public Title getTitle() { + return title; + } + + public void setTooltip(Tooltip tooltip) { + this.tooltip = tooltip; + } + public Tooltip getTooltip() { + return tooltip; + } + + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/HeatmapChartBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/HeatmapChartBody.java new file mode 100644 index 000000000..54eff63cc --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/HeatmapChartBody.java @@ -0,0 +1,78 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart; + +import org.opengauss.admin.plugin.vo.modeling.component.*; + +import java.util.List; +/** + * + * @author LZW + */ +public class HeatmapChartBody { + + private Title title; + private Tooltip tooltip; + private List series; + private XAxis xAxis; + private XAxis yAxis; + private VisualMap visualMap; + + public void setTitle(Title title) { + this.title = title; + } + public Title getTitle() { + return title; + } + + public void setTooltip(Tooltip tooltip) { + this.tooltip = tooltip; + } + public Tooltip getTooltip() { + return tooltip; + } + + public void setXAxis(XAxis xAxis) { + this.xAxis = xAxis; + } + public XAxis getXAxis() { + return xAxis; + } + + public void setYAxis(XAxis yAxis) { + this.yAxis = yAxis; + } + public XAxis getYAxis() { + return yAxis; + } + + public void setVisualMap(VisualMap visualMap) { + this.visualMap = visualMap; + } + public VisualMap getVisualMap() { + return visualMap; + } + + public void setSeries(List series) { + this.series = series; + } + public List getSeries() { + return series; + } + + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/LineChartBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/LineChartBody.java new file mode 100644 index 000000000..7c24619ce --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/LineChartBody.java @@ -0,0 +1,113 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart; + +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +import org.opengauss.admin.plugin.vo.modeling.component.*; + +import java.util.List; + + +/** + * + * @author LZW + */ + +public class LineChartBody { + + private Title title; + private Tooltip tooltip; + private Legend legend; + private Grid grid; + private XAxis xAxis; + private YAxis yAxis; + private List series; + private List dataZoom; + + public void setTitle(Title title) { + this.title = title; + } + public Title getTitle() { + return title; + } + + public void setTooltip(Tooltip tooltip) { + this.tooltip = tooltip; + } + public Tooltip getTooltip() { + return tooltip; + } + + public void setLegend(Legend legend) { + this.legend = legend; + } + public Legend getLegend() { + return legend; + } + + public void setGrid(Grid grid) { + this.grid = grid; + } + public Grid getGrid() { + return grid; + } + + + public void setXAxis(XAxis xAxis) { + this.xAxis = xAxis; + } + public XAxis getXAxis() { + return xAxis; + } + + public void setYAxis(YAxis yAxis) { + this.yAxis = yAxis; + } + public YAxis getYAxis() { + return yAxis; + } + + public void setSeries(List series) { + this.series = series; + } + public List getSeries() { + return series; + } + + public List getDataZoom() { + return dataZoom; + } + + public void setDataZoom(List dataZoom) { + this.dataZoom = dataZoom; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/MixChartBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/MixChartBody.java new file mode 100644 index 000000000..c581eef3f --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/MixChartBody.java @@ -0,0 +1,112 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart; +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +import org.opengauss.admin.plugin.vo.modeling.component.*; + +import java.util.List; + + +/** + * + * @author LZW + */ + +public class MixChartBody { + + private Title title; + private Tooltip tooltip; + private Legend legend; + private Grid grid; + private XAxis xAxis; + private List yAxis; + private List series; + private List dataZoom; + + public void setTitle(Title title) { + this.title = title; + } + public Title getTitle() { + return title; + } + + public void setTooltip(Tooltip tooltip) { + this.tooltip = tooltip; + } + public Tooltip getTooltip() { + return tooltip; + } + + public void setLegend(Legend legend) { + this.legend = legend; + } + public Legend getLegend() { + return legend; + } + + public void setGrid(Grid grid) { + this.grid = grid; + } + public Grid getGrid() { + return grid; + } + + + public void setXAxis(XAxis xAxis) { + this.xAxis = xAxis; + } + public XAxis getXAxis() { + return xAxis; + } + + public void setYAxis(List yAxis) { + this.yAxis = yAxis; + } + public List getYAxis() { + return yAxis; + } + + public void setSeries(List series) { + this.series = series; + } + public List getSeries() { + return series; + } + + public List getDataZoom() { + return dataZoom; + } + + public void setDataZoom(List dataZoom) { + this.dataZoom = dataZoom; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/PieChartBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/PieChartBody.java new file mode 100644 index 000000000..d8002c3de --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/PieChartBody.java @@ -0,0 +1,62 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart; +import org.opengauss.admin.plugin.vo.modeling.component.Legend; +import org.opengauss.admin.plugin.vo.modeling.component.PieSeries; +import org.opengauss.admin.plugin.vo.modeling.component.Title; +import org.opengauss.admin.plugin.vo.modeling.component.Tooltip; + +import java.util.List; +/** + * + * @author LZW + */ +public class PieChartBody { + + private Title title; + private Tooltip tooltip; + private Legend legend; + private List series; + + public void setTooltip(Tooltip tooltip) { + this.tooltip = tooltip; + } + public Tooltip getTooltip() { + return tooltip; + } + + public void setLegend(Legend legend) { + this.legend = legend; + } + public Legend getLegend() { + return legend; + } + + public void setSeries(List series) { + this.series = series; + } + public List getSeries() { + return series; + } + + public void setTitle(Title title) { + this.title = title; + } + public Title getTitle() { + return title; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/ScatterChartBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/ScatterChartBody.java new file mode 100644 index 000000000..6fa3a3468 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/ScatterChartBody.java @@ -0,0 +1,68 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart; + +import org.opengauss.admin.plugin.vo.modeling.component.*; + +import java.util.List; +/** + * + * @author LZW + */ +public class ScatterChartBody { + + private Title title; + private Tooltip tooltip; + private VisualMap visualMap; + private List series; + private List geo; + + public void setTitle(Title title) { + this.title = title; + } + public Title getTitle() { + return title; + } + + public void setTooltip(Tooltip tooltip) { + this.tooltip = tooltip; + } + public Tooltip getTooltip() { + return tooltip; + } + + public void setVisualMap(VisualMap visualMap) { + this.visualMap = visualMap; + } + public VisualMap getVisualMap() { + return visualMap; + } + + public void setSeries(List series) { + this.series = series; + } + public List getSeries() { + return series; + } + + public void setGeo(List geo) { + this.geo = geo; + } + public List getGeo() { + return geo; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/BarSeriesConstructor.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/BarSeriesConstructor.java new file mode 100644 index 000000000..158c15af3 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/BarSeriesConstructor.java @@ -0,0 +1,169 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart.constructor; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.plugin.vo.modeling.CategoryBody; +import org.opengauss.admin.plugin.vo.modeling.Dimension; +import org.opengauss.admin.plugin.vo.modeling.Indicator; + +import java.util.*; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Data +@EqualsAndHashCode(callSuper=true) +public class BarSeriesConstructor extends BaseSeriesConstructor { + private final Dimension xDimension; + + private final List indicators; + private final List dimensions; + + private final List> queryData; + + private String summaryType; + private String yUnit; + + private List indicatorFieldList; + private Map categoryDimensionValueMapper; + + private Map>> seriesData; + public ArrayList seriesKey; + + public BarSeriesConstructor(Dimension xDimension, List indicators, List dimensions, List> queryData) { + this.indicators = indicators; + this.dimensions = dimensions; + this.xDimension = xDimension; + this.queryData = queryData; + } + + public void getDimensionValue() { + //Analyze the specific values that need to be counted in different dimensions, controlled by the limit number num + queryData.forEach(item->{ + dimensions.forEach(dim->{ + String dimField = dim.getField(); + String dimCategory = (String) item.get(dimField); + if( !dim.getCustomFlag() && !Objects.equals(dimField, xDimension.getField())) { + //Generate a new category object, here is a non-custom dimension, and the category name is consistent with the value + CategoryBody cb = new CategoryBody(dimField, dimCategory, 1); + List valueList = new ArrayList<>(); + valueList.add(dimCategory); + cb.setValueList(valueList); + dim.addCategoryBody(cb); + } + }); + }); + + //enumeration mapping relationship + categoryDimensionValueMapper = new HashMap<>(); + dimensions.forEach(dim -> { + dim.getCategoryBodyList().forEach(body -> { + body.getValueList().forEach(value->{ + categoryDimensionValueMapper.put( body.getField()+value,body.getCategoryName()); + }); + }); + }); + + indicatorFieldList = new ArrayList<>(); + //a single graph only supports a single indicator + indicatorFieldList.add(indicators.get(0).getField()); + + } + + public void getDescartes() { + seriesKey = new ArrayList<>(); + List> dimData = new ArrayList<>(); + dimensions.forEach(dim -> { + List categoryList = new ArrayList<>(); + dim.getCategoryBodyList().forEach(categoryBody -> { + categoryList.add(categoryBody.getCategoryName()); + }); + dimData.add(categoryList); + }); + + //computer the Cartesian results between various categories of different statistical dimensions + culDescartes(dimData, seriesKey, 0, ""); + + seriesData = new HashMap<>(seriesKey.size()); + + if (seriesKey.size() == 0 ) { + seriesKey.add("Total"); + } + + seriesKey.forEach(key->{ + List> dataArr = new ArrayList<>(); + for (int i = 0; i < xDimension.getCategoryName().size();i++) { + dataArr.add(new ArrayList<>()); + } + seriesData.put(key, dataArr); + }); + } + + public Map> getSeriesData() { + queryData.forEach(item->{ + String xValue = item.get(xDimension.getField()) == null ? "null" : item.get(xDimension.getField()).toString(); + List xIndexList = xDimension.indexListByValue(xValue); + if (xIndexList.size() > 0) { + //Temporarily only counting single indicators + Float yValue = toFloat(String.valueOf(item.get(indicatorFieldList.get(0)))); + + List categoryGroup = new ArrayList<>(); + //The field corresponding to the statistical dimension is evaluated + dimensions.forEach(dim->{ + String categoryValue = item.get(dim.getField()) == null ? "null" : item.get(dim.getField()) .toString(); + String mapperKey = dim.getField()+categoryValue; + //Find the mapped category name + if (categoryDimensionValueMapper.containsKey(mapperKey)) { + categoryGroup.add(categoryDimensionValueMapper.get(mapperKey)); + } + }); + + //Concat all found category names as key + String groupKey = String.join("", categoryGroup); + + if (dimensions.size() == 0) { + xIndexList.forEach(xIndex -> { + seriesData.get("Total").get(xIndex).add(yValue); + }); + } else if (seriesData.containsKey(groupKey)) + { + xIndexList.forEach(xIndex -> { + seriesData.get(groupKey).get(xIndex).add(yValue); + }); + } + } + }); + + Map> resultSeries = new HashMap<>(); + seriesData.forEach((k,v) -> { + List summaryValue = new ArrayList<>(); + v.forEach(value -> { + Float s = culSummaryValue(value,indicators.get(0).getType()); + summaryValue.add(s); + }); + resultSeries.put(k,summaryValue); + }); + return resultSeries; + } + + public String getYUnit() { + yUnit = indicators.get(0).getUnit(); + return "{value}" + yUnit; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/BaseSeriesConstructor.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/BaseSeriesConstructor.java new file mode 100644 index 000000000..d4b03dcf9 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/BaseSeriesConstructor.java @@ -0,0 +1,143 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart.constructor; + +import java.util.Collections; +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class BaseSeriesConstructor { + + public void culDescartes(List> dimvalue, List result, int layer, String curString) + { + if (layer < dimvalue.size() - 1) + { + if (dimvalue.get(layer).size() == 0) { + culDescartes(dimvalue, result, layer + 1, curString); + } + else + { + for (int i = 0; i < dimvalue.get(layer).size(); i++) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(curString); + stringBuilder.append(dimvalue.get(layer).get(i)); + culDescartes(dimvalue, result, layer + 1, stringBuilder.toString()); + } + } + } + else if (layer == dimvalue.size() - 1) + { + if (dimvalue.get(layer).size() == 0) { + result.add(curString); + } + else + { + for (int i = 0; i < dimvalue.get(layer).size(); i++) + { + if (curString.isEmpty()) { + result.add(curString + dimvalue.get(layer).get(i)); + } else { + result.add(curString + dimvalue.get(layer).get(i)); + } + } + } + } + } + + public Float culSummaryValue(List oriData, String summaryType) { + Float result = 0f; + if (oriData.size() == 0) { + return result; + } + + switch (summaryType) { + case "sum" : { + for (Float value : oriData ) { + result += value; + } + break; + } + case "min" : { + result = min(oriData); + break; + } + case "max" : { + result = max(oriData); + break; + } + case "avg" : { + result = avg(oriData); + break; + } + case "mid" : { + result = median(oriData); + break; + } + default: { + result = oriData.get(0); + break; + } + } + + return result; + } + + public Float max(List oriData) { + return Collections.max(oriData); + } + + public Float min(List oriData) { + return Collections.min(oriData); + } + + public Float avg(List oriData) { + Float sum = 0f; + for (int i = 0; i < oriData.size(); i++) { + sum += oriData.get(i); + } + return sum / oriData.size(); + } + + public Float median(List oriData) { + Collections.sort(oriData); + if (oriData.size() % 2 == 0) { + int index = oriData.size() / 2; + return (Float) ((oriData.get(index - 1) + oriData.get(index))) / 2; + } + return oriData.get(oriData.size() / 2); + } + + + public float toFloat(Object value) { + if (value == null) { + return 0.0f; + } else if (value instanceof Number) { + return ((Number) value).floatValue(); + } else if (value instanceof String) { + try { + return Float.parseFloat((String) value); + } catch (NumberFormatException e) { + return 0.0f; + } + } else { + return 0.0f; + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/GraphSeriesConstructor.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/GraphSeriesConstructor.java new file mode 100644 index 000000000..c53f73669 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/GraphSeriesConstructor.java @@ -0,0 +1,93 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart.constructor; + +import org.opengauss.admin.plugin.vo.modeling.component.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.plugin.vo.modeling.Indicator; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Data +@EqualsAndHashCode(callSuper=true) +public class GraphSeriesConstructor extends BaseSeriesConstructor { + + private final Indicator indicator; + private final Node nodeParams; + private final Relation relationParams; + + private final List> queryData; + + private String summaryType; + + private Map> seriesData; + public ArrayList seriesKey; + + + public GraphSeriesConstructor(Indicator indicator, Node nodeParams, Relation relationParams, List> queryData) { + this.indicator = indicator; + this.nodeParams = nodeParams; + this.relationParams = relationParams; + this.queryData = queryData; + } + + public List getSeriesData() { + GraphSeries mainSeries = new GraphSeries(); + mainSeries.setName(nodeParams.getName()); + mainSeries.setType("graph"); + mainSeries.setLayout("none"); + mainSeries.setRoam(true); + mainSeries.setLabel(new Label().setPosition("right")); + mainSeries.setLineStyle(new LineStyle().setCurveness(0.3f).setColor("source")); + + List nodeList = new ArrayList<>(); + List linkList = new ArrayList<>(); + queryData.forEach(item->{ + GraphNode node = new GraphNode(); + node.setId((String) item.get(nodeParams.getId())); + float xPosition = toFloat(item.get(nodeParams.getX())); + float yPosition = toFloat(item.get(nodeParams.getY())); + if (xPosition > 0 && yPosition > 0) { + node.setX(xPosition); + node.setY(yPosition); + } + node.setValue(toFloat(item.get(relationParams.getValue()))); + node.setCategory(1); + node.setName((String) item.get(nodeParams.getName())); + node.setSymbolSize(toFloat(item.get(nodeParams.getSize()))); + node.setLabel(new Label().setShow(true)); + nodeList.add(node); + + GraphLink link = new GraphLink(); + link.setTarget((String) item.get(relationParams.getOut())); + link.setSource((String) item.get(relationParams.getIn())); + linkList.add(link); + }); + + mainSeries.setData(nodeList); + mainSeries.setLinks(linkList); + + return List.of(mainSeries); + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/HeatmapSeriesConstructor.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/HeatmapSeriesConstructor.java new file mode 100644 index 000000000..45378a134 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/HeatmapSeriesConstructor.java @@ -0,0 +1,103 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart.constructor; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.plugin.vo.modeling.Dimension; +import org.opengauss.admin.plugin.vo.modeling.Indicator; +import org.opengauss.admin.plugin.vo.modeling.component.Emphasis; +import org.opengauss.admin.plugin.vo.modeling.component.HeatmapSeries; +import org.opengauss.admin.plugin.vo.modeling.component.ItemStyle; +import org.opengauss.admin.plugin.vo.modeling.component.Label; + +import java.util.*; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Data +@EqualsAndHashCode(callSuper=true) +public class HeatmapSeriesConstructor extends BaseSeriesConstructor { + + private final Indicator indicator; + private final Dimension xDimension; + private final Dimension yDimension; + + private final List> queryData; + + private String summaryType; + + private Map> seriesData; + public ArrayList seriesKey; + + public HeatmapSeriesConstructor(Indicator indicator, Dimension xDimension,Dimension yDimension, List> queryData) { + this.indicator = indicator; + this.xDimension = xDimension; + this.yDimension = yDimension; + this.queryData = queryData; + } + + public List getSeriesData() { + + HeatmapSeries heatmapSeries = new HeatmapSeries(); + heatmapSeries.setType("heatmap"); + heatmapSeries.setLabel(new Label().setShow(true)); + heatmapSeries.setEmphasis(new Emphasis().setItemStyle(new ItemStyle().setShadowBlur(10).setShadowColor("rgba(0, 0, 0, 0.5)"))); + + HashMap,Float> result = new HashMap<>(); + + queryData.forEach(item->{ + //X axis + String xValue = (String) item.get(xDimension.getField()); + List xIndexList = xDimension.indexListByValue(xValue); + //Y axis + String yValue = (String) item.get(yDimension.getField()); + List yIndexList = yDimension.indexListByValue(yValue); + + if (xIndexList.size() > 0 && yIndexList.size() > 0) + { + xIndexList.forEach(xIndex->{ + yIndexList.forEach(yIndex->{ + List target = List.of(xIndex,yIndex); + float heatValue = toFloat(String.valueOf(item.get(indicator.getField()))); + + if (result.containsKey(List.of(xIndex,yIndex))) { + result.replace(target,result.get(target) + heatValue); + } else { + result.put(target,heatValue); + } + }); + }); + } + + }); + + Iterator, Float>> entries = result.entrySet().iterator(); + List> finalResult = new ArrayList<>(); + + while (entries.hasNext()) { + Map.Entry, Float> entry = entries.next(); + finalResult.add(List.of(entry.getKey().get(0),entry.getKey().get(1),entry.getValue())); + } + + heatmapSeries.setData(finalResult); + + return List.of(heatmapSeries); + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/LineSeriesConstructor.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/LineSeriesConstructor.java new file mode 100644 index 000000000..c2ca995c3 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/LineSeriesConstructor.java @@ -0,0 +1,169 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart.constructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.plugin.vo.modeling.CategoryBody; +import org.opengauss.admin.plugin.vo.modeling.Dimension; +import org.opengauss.admin.plugin.vo.modeling.Indicator; + +import java.util.*; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Data +@EqualsAndHashCode(callSuper=true) +public class LineSeriesConstructor extends BaseSeriesConstructor { + + + private final Dimension xDimension; + + private final List indicators; + private final List dimensions; + + private final List> queryData; + + private String summaryType; + private String yUnit; + + private List indicatorFieldList; + private Map categoryDimensionValueMapper; + + private Map>> seriesData; + public ArrayList seriesKey; + + public LineSeriesConstructor(Dimension xDimension, List indicators, List dimensions, List> queryData) { + this.xDimension = xDimension; + this.indicators = indicators; + this.dimensions = dimensions; + this.queryData = queryData; + } + + public void getDimensionValue() { + //Analyze the specific values that need to be counted in different dimensions, controlled by the limit number num + queryData.forEach(item->{ + dimensions.forEach(dim->{ + String dimField = dim.getField(); + String dimCategory = (String) item.get(dimField); + if( !dim.getCustomFlag() && !Objects.equals(dimField, xDimension.getField())) { + //Generate a new category object, here is a non-custom dimension, and the category name is consistent with the value + CategoryBody cb = new CategoryBody(dimField, dimCategory, 1); + List valueList = new ArrayList<>(); + valueList.add(dimCategory); + cb.setValueList(valueList); + dim.addCategoryBody(cb); + } + }); + }); + + //enumeration mapping relationship + categoryDimensionValueMapper = new HashMap<>(); + dimensions.forEach(dim -> { + dim.getCategoryBodyList().forEach(body -> { + body.getValueList().forEach(value->{ + categoryDimensionValueMapper.put( body.getField()+value,body.getCategoryName()); + }); + }); + }); + + indicatorFieldList = new ArrayList<>(); + //a single graph only supports a single indicator + indicatorFieldList.add(indicators.get(0).getField()); + + } + + public void getDescartes() { + seriesKey = new ArrayList<>(); + List> dimData = new ArrayList<>(); + dimensions.forEach(dim -> { + List categoryList = new ArrayList<>(); + dim.getCategoryBodyList().forEach(categoryBody -> { + categoryList.add(categoryBody.getCategoryName()); + }); + dimData.add(categoryList); + }); + + //computer the Cartesian results between various categories of different statistical dimensions + culDescartes(dimData, seriesKey, 0, ""); + + if (seriesKey.size() == 0 ) { + seriesKey.add("Total"); + } + + seriesData = new HashMap<>(seriesKey.size()); + seriesKey.forEach(key->{ + List> dataArr = new ArrayList<>(); + for (int i = 0; i < xDimension.getCategoryName().size();i++) { + dataArr.add(new ArrayList<>()); + } + seriesData.put(key, dataArr); + }); + } + + public Map> getSeriesData() { + queryData.forEach(item->{ + String xValue = item.get(xDimension.getField()) == null ? "null" : item.get(xDimension.getField()).toString(); + List xIndexList = xDimension.indexListByValue(xValue); + if (xIndexList.size() > 0) { + //Temporarily only counting single indicators + Float yValue = toFloat(String.valueOf(item.get(indicatorFieldList.get(0)))); + + List categoryGroup = new ArrayList<>(); + //The field corresponding to the statistical dimension is evaluated + dimensions.forEach(dim->{ + String categoryValue = item.get(dim.getField()) == null ? "null" : item.get(dim.getField()) .toString(); + String mapperKey = dim.getField()+categoryValue; + //Find the mapped category name + if (categoryDimensionValueMapper.containsKey(mapperKey)) { + categoryGroup.add(categoryDimensionValueMapper.get(mapperKey)); + } + }); + + //Concat all found category names as key + String groupKey = String.join("", categoryGroup); + + if (dimensions.size() == 0) { + xIndexList.forEach(xIndex -> { + seriesData.get("Total").get(xIndex).add(yValue); + }); + } else if (seriesData.containsKey(groupKey)) + { + xIndexList.forEach(xIndex -> { + seriesData.get(groupKey).get(xIndex).add(yValue); + }); + } + } + }); + + Map> resultSeries = new HashMap<>(); + seriesData.forEach((k,v) -> { + List summaryValue = new ArrayList<>(); + v.forEach(value -> { + Float s = culSummaryValue(value,indicators.get(0).getType()); + summaryValue.add(s); + }); + resultSeries.put(k,summaryValue); + }); + return resultSeries; + } + + public String getYUnit() { + yUnit = indicators.get(0).getUnit(); + return "{value}" + yUnit; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/PieSeriesConstructor.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/PieSeriesConstructor.java new file mode 100644 index 000000000..595a5d8e8 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/PieSeriesConstructor.java @@ -0,0 +1,141 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart.constructor; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.plugin.vo.modeling.Dimension; +import org.opengauss.admin.plugin.vo.modeling.Indicator; +import org.opengauss.admin.plugin.vo.modeling.component.Label; +import org.opengauss.admin.plugin.vo.modeling.component.LabelLine; +import org.opengauss.admin.plugin.vo.modeling.component.PieData; +import org.opengauss.admin.plugin.vo.modeling.component.PieSeries; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Data +@EqualsAndHashCode(callSuper=true) +public class PieSeriesConstructor extends BaseSeriesConstructor { + + private final Indicator indicator; + private final Dimension dimension; + private final Dimension subDimension; + + private final List> queryData; + + private String summaryType; + private Boolean displayPercentage; + + private Map> seriesData; + public ArrayList seriesKey; + + public PieSeriesConstructor(Indicator indicator, Dimension dimension, Dimension subDimension, List> queryData, Boolean displayPercentage) { + this.indicator = indicator; + this.dimension = dimension; + this.subDimension = subDimension; + this.displayPercentage = displayPercentage; + + this.queryData = queryData; + } + + public List getSeriesData() { + + PieSeries mainPieSeries = new PieSeries(); + + //Initialize outer circle parameters + mainPieSeries.setRadius(List.of("0%","60%")); + mainPieSeries.setSelectedMode("single"); + mainPieSeries.setType("pie"); + mainPieSeries.setLabelLine(new LabelLine().setLength(30).setShow(true)); + if (displayPercentage != null && displayPercentage) { + mainPieSeries.setLabel(new Label().setFormatter("{b}\n\n占比{d}%").setFontSize(12).setShow(true)); + } + List pieDataList = new ArrayList<>(); + List subPieDataList = new ArrayList<>(); + + setSeriesKey(new ArrayList<>()); + + dimension.getCategoryBodyList().forEach(categoryBody -> { + PieData pieData = new PieData(); + pieData.setName(categoryBody.getCategoryName()); + pieData.setValue(0f); + pieDataList.add(dimension.getCategoryBodyList().indexOf(categoryBody),pieData); + seriesKey.add(categoryBody.getCategoryName()); + }); + + subDimension.getCategoryBodyList().forEach(categoryBody -> { + PieData pieData = new PieData(); + pieData.setName(categoryBody.getCategoryName()); + pieData.setValue(0f); + subPieDataList.add(subDimension.getCategoryBodyList().indexOf(categoryBody),pieData); + seriesKey.add(categoryBody.getCategoryName()); + }); + + queryData.forEach(item->{ + //Outer circle data + String xValue = (String) item.get(dimension.getField()); + List xIndexList = dimension.indexListByValue(xValue); + float yValue = toFloat(String.valueOf(item.get(indicator.getField()))); + + if (xIndexList.size() > 0) + { + //If the custom dimension contains multiple categories that overlap, the total will be greater than 100%, which needs to be considered + xIndexList.forEach(xIndex -> { + pieDataList.get(xIndex).addValue(yValue); + }); + } + //Inner circle data + String subValue = (String) item.get(subDimension.getField()); + List subIndexList = subDimension.indexListByValue(subValue); + if (subIndexList.size() > 0) + { + subIndexList.forEach(subIndex -> { + subPieDataList.get(subIndex).addValue(yValue); + }); + } + }); + mainPieSeries.setData(pieDataList); + + List result = new ArrayList<>(); + + if (subPieDataList.size() > 0) { + mainPieSeries.setRadius(List.of("45%","60%")); + PieSeries subPieSeries = new PieSeries(); + + Label nl = new Label().setPosition("inner").setFontSize(11).setShow(true); + if (displayPercentage != null && displayPercentage) { + nl.setFormatter("{b}\n\n占比{d}%"); + } + subPieSeries.setLabel(nl); + subPieSeries.setRadius(List.of("0","30%")); + subPieSeries.setSelectedMode("single"); + subPieSeries.setType("pie"); + subPieSeries.setLabelLine(new LabelLine().setShow(false)); + subPieSeries.setData(subPieDataList); + result.add(subPieSeries); + } + result.add(mainPieSeries); + + return result; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/ScatterSeriesConstructor.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/ScatterSeriesConstructor.java new file mode 100644 index 000000000..348013e8c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/modeling/echart/constructor/ScatterSeriesConstructor.java @@ -0,0 +1,108 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.domain.model.modeling.echart.constructor; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.opengauss.admin.plugin.vo.modeling.Indicator; +import org.opengauss.admin.plugin.vo.modeling.component.Encode; +import org.opengauss.admin.plugin.vo.modeling.component.Location; +import org.opengauss.admin.plugin.vo.modeling.component.ScatterSeries; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Data +@EqualsAndHashCode(callSuper=true) +public class ScatterSeriesConstructor extends BaseSeriesConstructor { + + private final Indicator indicator; + private final Location location; + private final int highLight; + + private final List> queryData; + + private String summaryType; + + private Map> seriesData; + public ArrayList seriesKey; + public int maxValue; + + public ScatterSeriesConstructor(Indicator indicator, Location location, List> queryData, int highLight) { + this.indicator = indicator; + this.location = location; + this.queryData = queryData; + this.highLight = highLight; + } + + public List getSeriesData() { + + Map seriesTemp = new HashMap<>(); + queryData.forEach(item->{ + String loc = ""; + Object field = item.get(location.getField()); + if (field != null) { + loc = ((String) field).trim(); + } + float yValue = toFloat(String.valueOf(item.get(indicator.getField()))); + if (seriesTemp.containsKey(loc)) { + seriesTemp.replace(loc,seriesTemp.get(loc) + yValue); + } else { + seriesTemp.put(loc,yValue); + } + }); + + List> list = new ArrayList<>(seriesTemp.entrySet()); + + list.sort(Map.Entry.comparingByValue(Comparator.reverseOrder())); + + List topLoc = list.stream() + .limit(highLight) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + maxValue = (int) Math.ceil(list.get(0).getValue()); + + //Format + List> seriesResult = new ArrayList<>(); + seriesTemp.forEach( (key,var) -> { + if (key != null) { + if (topLoc.contains(key)) { + seriesResult.add(Map.of("name", key, "value", var.toString(),"selected","true")); + } else { + seriesResult.add(Map.of("name", key, "value", var.toString())); + } + } + }); + + ScatterSeries scatterSeries = new ScatterSeries(); + scatterSeries.setMap("china"); + scatterSeries.setType("map"); + scatterSeries.setRoam(true); + scatterSeries.setData(seriesResult); + scatterSeries.setEncode(new Encode().setValue(2)); + scatterSeries.setGeoIndex(0); + scatterSeries.setSelectedMode("multiple"); + + return List.of(scatterSeries); + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ClusterInstallConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ClusterInstallConfig.java new file mode 100644 index 000000000..5fc226858 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ClusterInstallConfig.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterInstallConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ClusterInstallConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +/** + * @author lhf + * @date 2022/8/4 23:15 + **/ +public interface ClusterInstallConfig { + /** + * Check equipment + */ + void checkConfig(); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ClusterSummaryVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ClusterSummaryVO.java new file mode 100644 index 000000000..d912ab543 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ClusterSummaryVO.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterSummaryVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ClusterSummaryVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/23 23:21 + **/ +@Data +public class ClusterSummaryVO { + private Long clusterNum; + private Long hostNum; + private Long nodeNum; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/DisasterQueryResult.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/DisasterQueryResult.java new file mode 100644 index 000000000..b76cc8292 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/DisasterQueryResult.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022-2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; + +/** + * dorado query result + * + * @author shenzheng + * @since 2024/3/7 + **/ +@Data +public class DisasterQueryResult { + private String runningStatus; + private String replicationProgress; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/DownloadBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/DownloadBody.java new file mode 100644 index 000000000..dcccd57fc --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/DownloadBody.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DownloadBody.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/DownloadBody.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; + +/** + * @author lhf + * @date 2022/8/15 22:20 + **/ +@Data +public class DownloadBody { + @NotBlank(message = "The resource path cannot be empty") + private String resourceUrl; + @NotBlank(message = "The target path cannot be empty") + private String targetPath; + @NotBlank(message = "The file name cannot be empty") + private String fileName; + @NotBlank(message = "The service ID cannot be empty") + private String businessId; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/DwrSnapshotVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/DwrSnapshotVO.java new file mode 100644 index 000000000..140c787c9 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/DwrSnapshotVO.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DwrSnapshotVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/DwrSnapshotVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * @author lhf + * @date 2022/10/24 13:55 + **/ +@Data +public class DwrSnapshotVO { + private String snapshot_id; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date start_ts; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date end_ts; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/EnterpriseInstallConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/EnterpriseInstallConfig.java new file mode 100644 index 000000000..7cc492e02 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/EnterpriseInstallConfig.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * EnterpriseInstallConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/EnterpriseInstallConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.node.EnterpriseInstallNodeConfig; +import lombok.Data; +import org.opengauss.admin.plugin.enums.ops.DatabaseKernelArch; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Enterprise Edition installation configuration + * + * @author lhf + * @date 2022/8/4 23:16 + **/ +@Data +public class EnterpriseInstallConfig implements ClusterInstallConfig { + private String installPackagePath; + + private String installPath; + + private String logPath; + + private String tmpPath; + + private String omToolsPath; + + private String corePath; + + private Integer port; + + private Boolean enableDCF; + + private Integer dcfPort; + + private String databaseUsername; + + private String databasePassword; + + private DatabaseKernelArch databaseKernelArch = DatabaseKernelArch.MASTER_SLAVE; + + private Boolean isInstallCM; + + private SharingStorageInstallConfig sharingStorageInstallConfig; + + private List nodeConfigList; + + @Override + public void checkConfig() { + if (StrUtil.isEmpty(installPath)) { + throw new OpsException("wrong installation path"); + } + + if (StrUtil.isEmpty(logPath)) { + throw new OpsException("Log path is incorrect"); + } + + if (StrUtil.isEmpty(tmpPath)) { + throw new OpsException("The temporary file path is incorrect"); + } + + if (StrUtil.isEmpty(omToolsPath)) { + throw new OpsException("om Path error"); + } + + if (StrUtil.isEmpty(corePath)) { + throw new OpsException("Database core item error"); + } + + if (Objects.isNull(port)) { + throw new OpsException("Port error"); + } + + if (Objects.isNull(enableDCF)) { + enableDCF = false; + } + + if (Objects.isNull(databaseKernelArch)) { + throw new OpsException("database Kernel Arch error"); + } + + if (Objects.isNull(isInstallCM)) { + throw new OpsException("Is it wrong to install CM"); + } + + if (CollUtil.isEmpty(nodeConfigList)) { + throw new OpsException("The cluster node is incorrectly configured"); + } + + if (databaseKernelArch == DatabaseKernelArch.SHARING_STORAGE) { + isInstallCM = true; + if (Objects.isNull(sharingStorageInstallConfig)) { + throw new OpsException("sharing storage configuration is null"); + } + sharingStorageInstallConfig.setEnableDss(true); + sharingStorageInstallConfig.checkConfig(); + } + + for (EnterpriseInstallNodeConfig enterpriseInstallNodeConfig : nodeConfigList) { + enterpriseInstallNodeConfig.checkConfig(isInstallCM); + } + } + + public List toOpsClusterNodeEntityList() { + List list = new ArrayList<>(); + + for (EnterpriseInstallNodeConfig enterpriseInstallNodeConfig : nodeConfigList) { + OpsClusterNodeEntity opsClusterNodeEntity = enterpriseInstallNodeConfig.toOpsClusterNodeEntity(); + if (Objects.equals(databaseKernelArch, DatabaseKernelArch.SHARING_STORAGE)) { + sharingStorageInstallConfig.appendtoOpsClusterNodeEntity(opsClusterNodeEntity); + } + list.add(opsClusterNodeEntity); + } + return list; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/Host.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/Host.java new file mode 100644 index 000000000..94fbbf18b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/Host.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * Host.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/Host.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; + +import java.util.List; + +/** + * Host Information + * + * @author lhf + * @date 2022/8/4 22:12 + **/ +@Data +public class Host { + private String id; + /** + * The host address + */ + private String host; + /** + * Host SSH Port + */ + private Integer port; + /** + * Host authentication Certificate + */ + private List hostCredentials; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostBody.java new file mode 100644 index 000000000..73132b0b4 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostBody.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostBody.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostBody.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import jakarta.validation.constraints.NotEmpty; + +/** + * @author lhf + * @date 2022/8/11 20:07 + **/ +@Slf4j +@Data +public class HostBody { + @NotEmpty(message = "The IP address cannot be empty") + private String publicIp; + @NotEmpty(message = "The Intranet IP address cannot be empty") + private String privateIp; + @NotEmpty(message = "The password cannot be empty") + private String password; + @NotEmpty(message = "Please select AZ") + private String azId; + private String remark; + private Integer port; + + public OpsHostUserEntity toRootUser(String hostId) { + OpsHostUserEntity hostUserEntity = new OpsHostUserEntity(); + hostUserEntity.setUsername("root"); + hostUserEntity.setPassword(password); + hostUserEntity.setHostId(hostId); + return hostUserEntity; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostCredentials.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostCredentials.java new file mode 100644 index 000000000..2cfea964c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostCredentials.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostCredentials.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostCredentials.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import org.opengauss.admin.plugin.enums.ops.HostAuthenticationTypeEnum; +import lombok.Data; + +/** + * Host credential + * + * @author lhf + * @date 2022/8/4 22:17 + **/ +@Data +public class HostCredentials { + + private String principal; + + private String credentials; + + private HostAuthenticationTypeEnum authenticationType; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostFile.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostFile.java new file mode 100644 index 000000000..3f8fbe80c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostFile.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostFile.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostFile.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.mysql.cj.jdbc.exceptions.CommunicationsException; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.utils.uuid.UUID; +import org.opengauss.admin.plugin.enums.ops.HostFileTypeEnum; +import lombok.Data; + +import java.io.File; +import java.io.IOException; +import java.util.Date; + +/** + * @author lhf + * @date 2022/8/7 22:37 + **/ +@Slf4j +@Data +public class HostFile implements Comparable { + private String id; + private String name; + private HostFileTypeEnum type; + /** + * size(byte) + */ + private Long size; + // file path + private String path; + // package manage name if exist + private String pkgManagedName; + private String openGaussVersionNum; + private String openGaussVersion; + private String cpuArch; + private String os; + private String remark; + private Date updateTime; + + public static HostFile build(File file, OpsPackageVO vo) { + HostFile hostFile = new HostFile(); + String fileName = file.getName(); + HostFileTypeEnum fileType = file.isFile() ? HostFileTypeEnum.FILE : HostFileTypeEnum.DIRECTORY; + long length = file.length(); + if (StrUtil.isNotEmpty(vo.getName())) { + hostFile.setUpdateTime(vo.getUpdateTime()); + hostFile.setOpenGaussVersionNum(vo.getPackageVersionNum()); + hostFile.setRemark(vo.getRemark()); + } else { + hostFile.setUpdateTime(new Date(file.lastModified())); + hostFile.setOpenGaussVersionNum(parseVersionNum(fileName)); + } + hostFile.setId(UUID.fastUuid().toString()); + hostFile.setName(fileName); + hostFile.setType(fileType); + hostFile.setSize(length); + try { + hostFile.setPath(file.getCanonicalPath()); + } catch (IOException ex) { + log.error("set host file path error: " + ex.getMessage()); + } + + hostFile.setCpuArch(vo.getCpuArch()); + hostFile.setOs(vo.getOs()); + hostFile.setOpenGaussVersion(vo.getPackageVersion()); + hostFile.setPkgManagedName(vo.getName()); + return hostFile; + } + + public static HostFile of(String filename, HostFileTypeEnum hostFileTypeEnum, long size) { + HostFile hostFile = new HostFile(); + hostFile.setName(filename); + hostFile.setType(hostFileTypeEnum); + hostFile.setSize(size); + return hostFile; + } + + private static String parseVersionNum(String fileName) { + if (StrUtil.isNotEmpty(fileName)) { + if (fileName.startsWith("openGauss-Lite-")) { + String[] split = fileName.split("-"); + if (split.length >= 3) { + return split[2]; + } else { + return null; + } + } else if (fileName.startsWith("openGauss-")) { + String[] split = fileName.split("-"); + if (split.length >= 2) { + return split[1]; + } else { + return null; + } + } else { + //uuid + return parseVersionNum(fileName.substring(fileName.indexOf("openGauss"))); + } + } + return null; + } + + @Override + public int compareTo(HostFile o) { + return DateUtil.compare(o.getUpdateTime(), updateTime); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostInfoHolder.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostInfoHolder.java new file mode 100644 index 000000000..41dbf31c1 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostInfoHolder.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostInfoHolder.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostInfoHolder.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author lhf + * @date 2022/8/13 11:50 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class HostInfoHolder { + private OpsHostEntity hostEntity; + private List hostUserEntities; +} + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostUserBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostUserBody.java new file mode 100644 index 000000000..eb3db95f8 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostUserBody.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostUserBody.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/HostUserBody.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; + +/** + * @author lhf + * @date 2022/8/17 18:01 + **/ +@Data +public class HostUserBody { + @NotBlank(message = "The host ID cannot be empty") + private String hostId; + @NotBlank(message = "The user name cannot be empty") + private String username; + @NotBlank(message = "The password cannot be empty") + private String password; + + public OpsHostUserEntity toEntity() { + OpsHostUserEntity hostUserEntity = new OpsHostUserEntity(); + hostUserEntity.setHostId(hostId); + hostUserEntity.setUsername(username); + hostUserEntity.setPassword(password); + return hostUserEntity; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ImportClusterBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ImportClusterBody.java new file mode 100644 index 000000000..f0a60da86 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ImportClusterBody.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ImportClusterBody.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ImportClusterBody.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; +import org.opengauss.admin.plugin.enums.ops.InstallModeEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import lombok.Data; + +import java.util.Date; +import java.util.Objects; + +/** + * @author lhf + * @date 2022/10/8 15:47 + **/ +@Data +public class ImportClusterBody { + + private OpenGaussVersionEnum openGaussVersion; + + private String openGaussVersionNum; + + private InstallModeEnum installMode; + + private DeployTypeEnum deployType; + + private EnterpriseInstallConfig enterpriseInstallConfig; + + private MinimalistInstallConfig minimalistInstallConfig; + + private LiteInstallConfig liteInstallConfig; + + private String envPath; + + private String xmlConfigPath; + + private String clusterId; + + private String clusterName; + + + public void checkConfig() { + if (Objects.isNull(openGaussVersion)) { + throw new OpsException("The OpenGauss version is incorrect"); + } + + if (Objects.isNull(installMode)) { + throw new OpsException("Incorrect installation mode"); + } + + if (Objects.isNull(deployType)) { + throw new OpsException("Incorrect deployment mode"); + } + + if (StrUtil.isEmpty(clusterId)) { + throw new OpsException("Cluster ID error"); + } + + boolean clusterDeploy = deployType == DeployTypeEnum.CLUSTER; + + if (openGaussVersion == OpenGaussVersionEnum.ENTERPRISE) { + if (Objects.isNull(enterpriseInstallConfig)) { + throw new OpsException("The enterprise version is incorrectly installed and configured"); + } + + int nodeSize = CollUtil.isEmpty(enterpriseInstallConfig.getNodeConfigList()) ? 0 : + enterpriseInstallConfig.getNodeConfigList().size(); + if (clusterDeploy && enterpriseInstallConfig.getIsInstallCM() && nodeSize < 2) { + throw new OpsException("In cluster mode, a maximum of two nodes can be installed"); + } + + if (StrUtil.isEmpty(enterpriseInstallConfig.getDatabaseUsername())) { + throw new OpsException("The user name cannot be empty"); + } + + enterpriseInstallConfig.checkConfig(); + } else if (openGaussVersion == OpenGaussVersionEnum.MINIMAL_LIST) { + if (Objects.isNull(minimalistInstallConfig)) { + throw new OpsException("The minimalist version was incorrectly installed and configured"); + } + + int nodeSize = CollUtil.isEmpty(minimalistInstallConfig.getNodeConfigList()) ? 0 : + minimalistInstallConfig.getNodeConfigList().size(); + if (nodeSize > 1) { + throw new OpsException("The minimalist version can only be installed on a single host"); + } + + if (StrUtil.isEmpty(minimalistInstallConfig.getDatabaseUsername())) { + throw new OpsException("The user name cannot be empty"); + } + + minimalistInstallConfig.checkConfig(); + } else if (openGaussVersion == OpenGaussVersionEnum.LITE) { + if (Objects.isNull(liteInstallConfig)) { + throw new OpsException("The lightweight version was incorrectly installed and configured"); + } + + int nodeSize = CollUtil.isEmpty(liteInstallConfig.getNodeConfigList()) ? 0 : + liteInstallConfig.getNodeConfigList().size(); + if (clusterDeploy && nodeSize < 2) { + throw new OpsException("In cluster mode, a maximum of two nodes can be installed"); + } + + if (StrUtil.isEmpty(liteInstallConfig.getDatabaseUsername())) { + throw new OpsException("The user name cannot be empty"); + } + + liteInstallConfig.checkConfig(); + } + } + + public OpsClusterEntity toOpsClusterEntity() { + OpsClusterEntity opsClusterEntity = new OpsClusterEntity(); + opsClusterEntity.setCreateTime(new Date()); + opsClusterEntity.setClusterId(clusterId); + opsClusterEntity.setVersion(openGaussVersion); + opsClusterEntity.setVersionNum(openGaussVersionNum); + opsClusterEntity.setInstallMode(installMode); + opsClusterEntity.setDeployType(deployType); + opsClusterEntity.setClusterName(clusterName); + if (!StrUtil.isEmpty(envPath)) { + opsClusterEntity.setEnvPath(envPath); + } + if (openGaussVersion == OpenGaussVersionEnum.ENTERPRISE) { + opsClusterEntity.setDatabaseUsername(enterpriseInstallConfig.getDatabaseUsername()); + opsClusterEntity.setDatabasePassword(enterpriseInstallConfig.getDatabasePassword()); + opsClusterEntity.setInstallPath(enterpriseInstallConfig.getInstallPath()); + opsClusterEntity.setLogPath(enterpriseInstallConfig.getLogPath()); + opsClusterEntity.setTmpPath(enterpriseInstallConfig.getTmpPath()); + opsClusterEntity.setOmToolsPath(enterpriseInstallConfig.getOmToolsPath()); + opsClusterEntity.setCorePath(enterpriseInstallConfig.getCorePath()); + opsClusterEntity.setPort(enterpriseInstallConfig.getPort()); + boolean isDcfEnable = enterpriseInstallConfig.getEnableDCF(); + opsClusterEntity.setEnableDcf(isDcfEnable); + if (isDcfEnable) { + opsClusterEntity.setDcfPort(enterpriseInstallConfig.getDcfPort()); + } + opsClusterEntity.setInstallPackagePath(enterpriseInstallConfig.getInstallPackagePath()); + if (StrUtil.isEmpty(xmlConfigPath)) { + opsClusterEntity.setXmlConfigPath(enterpriseInstallConfig.getInstallPackagePath() + + "/cluster_config.xml"); + } else { + opsClusterEntity.setXmlConfigPath(xmlConfigPath); + } + } else if (openGaussVersion == OpenGaussVersionEnum.LITE) { + opsClusterEntity.setPort(liteInstallConfig.getPort()); + opsClusterEntity.setDatabaseUsername(liteInstallConfig.getDatabaseUsername()); + opsClusterEntity.setDatabasePassword(liteInstallConfig.getDatabasePassword()); + opsClusterEntity.setInstallPackagePath(liteInstallConfig.getInstallPackagePath()); + } else if (openGaussVersion == OpenGaussVersionEnum.MINIMAL_LIST) { + opsClusterEntity.setPort(minimalistInstallConfig.getPort()); + opsClusterEntity.setDatabaseUsername(minimalistInstallConfig.getDatabaseUsername()); + opsClusterEntity.setDatabasePassword(minimalistInstallConfig.getDatabasePassword()); + opsClusterEntity.setInstallPackagePath(minimalistInstallConfig.getInstallPackagePath()); + } + return opsClusterEntity; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/InstallBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/InstallBody.java new file mode 100644 index 000000000..1b400c8b1 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/InstallBody.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * InstallBody.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/InstallBody.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/8/5 09:05 + **/ +@Data +public class InstallBody { + private InstallContext installContext; + + private Boolean quickInstall; + + private String quickInstallResourceUrl; + + private String businessId; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/InstallContext.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/InstallContext.java new file mode 100644 index 000000000..1640ecf8d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/InstallContext.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * InstallContext.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/InstallContext.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; +import org.opengauss.admin.plugin.enums.ops.InstallModeEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussSupportOSEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.enums.ops.DatabaseKernelArch; +import org.springframework.util.ObjectUtils; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author lhf + * @date 2022/8/4 22:56 + **/ +@Slf4j +@Data +public class InstallContext implements Cloneable { + private String installUsername; + private OpenGaussVersionEnum openGaussVersion; + + private String openGaussVersionNum; + + private OpenGaussSupportOSEnum os; + + private InstallModeEnum installMode; + + private DeployTypeEnum deployType; + + private String installPackagePath; + private String installPackageLocalPath; + + private EnterpriseInstallConfig enterpriseInstallConfig; + + private MinimalistInstallConfig minimalistInstallConfig; + + private LiteInstallConfig liteInstallConfig; + + private String clusterId; + + private WsSession retSession; + private RetBuffer retBuffer; + + private String envPath; + private String envAbsolutePath; + + private List hostInfoHolders; + + public void checkConfig() { + checkRetSessionConfig(); + checkTaskConfig(); + } + + public void checkTaskConfig() { + if (Objects.isNull(openGaussVersion)) { + throw new OpsException("The OpenGauss version is incorrect"); + } + + if (StrUtil.isEmpty(openGaussVersionNum)) { + throw new OpsException("The OpenGauss version number is incorrect"); + } + + if (Objects.isNull(installMode)) { + throw new OpsException("Incorrect installation mode"); + } + + if (Objects.isNull(deployType)) { + throw new OpsException("Incorrect deployment mode"); + } + + if (StrUtil.isEmpty(installPackagePath)) { + throw new OpsException("The installation package path is incorrect"); + } + + if (StrUtil.isEmpty(clusterId)) { + throw new OpsException("Cluster ID error"); + } + + boolean clusterDeploy = deployType == DeployTypeEnum.CLUSTER; + + if (openGaussVersion == OpenGaussVersionEnum.ENTERPRISE) { + if (Objects.isNull(enterpriseInstallConfig)) { + throw new OpsException("The enterprise version is incorrectly installed and configured"); + } + + int nodeSize = CollUtil.isEmpty(enterpriseInstallConfig.getNodeConfigList()) ? 0 : + enterpriseInstallConfig.getNodeConfigList().size(); + if (enterpriseInstallConfig.getDatabaseKernelArch() == DatabaseKernelArch.MASTER_SLAVE) { + try { + String[] splitVersionNums = openGaussVersionNum.split("[_-]"); + int ogVerNum = Integer.parseInt(splitVersionNums[0].replaceAll("\\.", "")); + int minNodeSize = ogVerNum > 311 ? 2 : 3; + if (clusterDeploy && enterpriseInstallConfig.getIsInstallCM() && nodeSize < minNodeSize) { + throw new OpsException("In traditional master_slave cluster mode, at least " + + minNodeSize + " nodes needed to be installed"); + } + } catch (NumberFormatException | ArrayIndexOutOfBoundsException | NullPointerException e) { + log.error("Parsing the openGauss version number failed."); + } + } + + enterpriseInstallConfig.checkConfig(); + } else if (openGaussVersion == OpenGaussVersionEnum.MINIMAL_LIST) { + if (Objects.isNull(minimalistInstallConfig)) { + throw new OpsException("The minimalist version was incorrectly installed and configured"); + } + + int nodeSize = CollUtil.isEmpty(minimalistInstallConfig.getNodeConfigList()) ? 0 : + minimalistInstallConfig.getNodeConfigList().size(); + if (nodeSize > 1) { + throw new OpsException("The minimalist version can only be installed on a single host"); + } + + minimalistInstallConfig.checkConfig(); + } else if (openGaussVersion == OpenGaussVersionEnum.LITE) { + if (Objects.isNull(liteInstallConfig)) { + throw new OpsException("The lightweight version was incorrectly installed and configured"); + } + + int nodeSize = CollUtil.isEmpty(liteInstallConfig.getNodeConfigList()) ? 0 : + liteInstallConfig.getNodeConfigList().size(); + if (clusterDeploy && nodeSize < 2) { + throw new OpsException("In cluster mode, a maximum of two nodes can be installed"); + } + + liteInstallConfig.checkConfig(); + } + } + + private void checkRetSessionConfig() { + if (Objects.isNull(retSession)) { + throw new OpsException("Response connection error"); + } + } + + public OpsClusterEntity toOpsClusterEntity() { + OpsClusterEntity opsClusterEntity = new OpsClusterEntity(); + opsClusterEntity.setCreateTime(new Date()); + opsClusterEntity.setClusterId(clusterId); + opsClusterEntity.setVersion(openGaussVersion); + opsClusterEntity.setVersionNum(openGaussVersionNum); + opsClusterEntity.setInstallMode(installMode); + opsClusterEntity.setDeployType(deployType); + opsClusterEntity.setDatabaseUsername("gaussdb"); + if (!StrUtil.isEmpty(envPath)) { + opsClusterEntity.setEnvPath(envPath); + } + if (openGaussVersion == OpenGaussVersionEnum.ENTERPRISE) { + opsClusterEntity.setInstallPackagePath(enterpriseInstallConfig.getInstallPackagePath()); + opsClusterEntity.setDatabasePassword(enterpriseInstallConfig.getDatabasePassword()); + opsClusterEntity.setInstallPath(enterpriseInstallConfig.getInstallPath()); + opsClusterEntity.setLogPath(enterpriseInstallConfig.getLogPath()); + opsClusterEntity.setTmpPath(enterpriseInstallConfig.getTmpPath()); + opsClusterEntity.setOmToolsPath(enterpriseInstallConfig.getOmToolsPath()); + opsClusterEntity.setCorePath(enterpriseInstallConfig.getCorePath()); + opsClusterEntity.setPort(enterpriseInstallConfig.getPort()); + boolean isDcfEnable = enterpriseInstallConfig.getEnableDCF(); + opsClusterEntity.setEnableDcf(isDcfEnable); + if (isDcfEnable) { + opsClusterEntity.setDcfPort(enterpriseInstallConfig.getDcfPort()); + } + opsClusterEntity.setXmlConfigPath(enterpriseInstallConfig.getInstallPackagePath() + "/cluster_config.xml"); + } else if (openGaussVersion == OpenGaussVersionEnum.LITE) { + opsClusterEntity.setInstallPackagePath(liteInstallConfig.getInstallPackagePath()); + opsClusterEntity.setPort(liteInstallConfig.getPort()); + opsClusterEntity.setDatabasePassword(liteInstallConfig.getDatabasePassword()); + } else if (openGaussVersion == OpenGaussVersionEnum.MINIMAL_LIST) { + opsClusterEntity.setInstallPackagePath(minimalistInstallConfig.getInstallPackagePath()); + opsClusterEntity.setPort(minimalistInstallConfig.getPort()); + opsClusterEntity.setDatabasePassword(minimalistInstallConfig.getDatabasePassword()); + } + + return opsClusterEntity; + } + + /** + * Get host info holder map + * + * @return map + */ + public Map getHostInfoHolderMap() { + if (ObjectUtils.isEmpty(hostInfoHolders)) { + return new HashMap<>(); + } + return hostInfoHolders + .stream() + .collect(Collectors.toMap(val -> val.getHostEntity().getHostId(), Function.identity())); + } + + /** + * Legal Cloning Methods + * + * @return InstallContext + */ + @Override + public InstallContext clone() { + try { + return (InstallContext) super.clone(); + } catch (CloneNotSupportedException e) { + throw new OpsException("Failed to clone InstallContext" + e); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/JschResult.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/JschResult.java new file mode 100644 index 000000000..59542607c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/JschResult.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JschResult.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/JschResult.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; +import lombok.ToString; + +/** + * @author lhf + * @date 2022/8/10 14:08 + **/ +@Data +@ToString +public class JschResult { + private Integer exitCode; + private String result; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ListDir.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ListDir.java new file mode 100644 index 000000000..b81c6b634 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ListDir.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ListDir.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/ListDir.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; + +import java.util.List; + +/** + * @author lhf + * @date 2022/8/7 22:22 + **/ +@Data +public class ListDir { + private String path; + private List files; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/LiteInstallConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/LiteInstallConfig.java new file mode 100644 index 000000000..6641156a0 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/LiteInstallConfig.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * LiteInstallConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/LiteInstallConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.node.LiteInstallNodeConfig; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Lightweight edition installation configuration + * + * @author lhf + * @date 2022/8/4 23:15 + **/ +@Data +public class LiteInstallConfig implements ClusterInstallConfig { + private String installPackagePath; + private Integer port; + private String databaseUsername; + private String databasePassword; + + private List nodeConfigList; + + @Override + public void checkConfig() { + if (Objects.isNull(port)) { + throw new OpsException("Incorrect port number"); + } + if (StrUtil.isEmpty(databasePassword)) { + throw new OpsException("The database password format is incorrect"); + } + + if (CollUtil.isEmpty(nodeConfigList)) { + throw new OpsException("The cluster node is incorrectly configured"); + } + + for (LiteInstallNodeConfig liteInstallNodeConfig : nodeConfigList) { + liteInstallNodeConfig.checkConfig(); + } + } + + public List toOpsClusterNodeEntityList() { + ArrayList opsClusterNodeEntities = new ArrayList<>(); + + for (LiteInstallNodeConfig nodeConfig : nodeConfigList) { + opsClusterNodeEntities.add(nodeConfig.toOpsClusterNodeEntity()); + } + + return opsClusterNodeEntities; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/LunPathManager.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/LunPathManager.java new file mode 100644 index 000000000..c3f269b6f --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/LunPathManager.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * LunPathManager.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/LunPathManager.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.enums.ops.DiskQueryMethodEnum; + +/** + * Lun path manager + * + * @author shenzheng + * @since 2023.11.27 + **/ +public class LunPathManager { + /** + * lun query method + */ + public static DiskQueryMethodEnum lunQueryMethod; + + /** + * wwn length of lun path + */ + public static final int WWN_LEN_IN_HEX = 32; + + /** + * len of wwn in softlink + */ + public static final int SOFT_LINK_WWN_LEN = 6; + + private String disk; + + private String wwn; + + private String capacity; + + private String name; + + /** + * lunResult should be like : + * 1. SCSI "/dev/sdn 36eca1d110035234bd392b4cf00000035 322GB" + * 2. UPADMIN "uptrapathfz 6eca1d110035234bd392b4cf00000036 322GB MY_LUN" + * 3. UPADMIN_PLUS "uptrapathfz 6eca1d110035234bd392b4cf00000036 322GB MY_LUN" + * + * @param lunResult lun path result + */ + public LunPathManager(String lunResult) { + String[] parts = lunResult.trim().split(" "); + if (parts.length < 3) { + throw new OpsException("Invalid lun path result"); + } + this.disk = parts[0].contains("/dev") ? parts[0] : ("/dev/" + parts[0]); + this.wwn = parts[1]; + this.capacity = parts[2]; + this.name = (parts.length == 4) ? parts[3] : ""; + } + + public String getDisk() { + return this.disk; + } + + public String getName() { + return this.name; + } + + public String getWwn() { + return this.wwn; + } + + public String getCapacity() { + return this.capacity; + } + + /** + * get lun disk name + * + * @param path LUN path result + * @return disk name of LUN + */ + public static String getLunDiskName(String path) { + if (LunPathManager.lunQueryMethod == DiskQueryMethodEnum.UPADMIN) { + return "/dev/" + path; + } else { + return path; + } + } + + /** + * get lun link path + * + * @param path lun path Manager + * @param prefix prefix of link path + * @param isDisasterNeed this parameter function has been deprecated + * @param installUser install user + * @return soft link path of LUN + */ + public static String getLunLinkPath(LunPathManager path, String prefix, boolean isDisasterNeed, + String installUser) { + if (isDisasterNeed) { + return "/dev/" + installUser + "_dss_" + prefix; + } + return "/dev/ln_" + prefix + "_" + path.getWwn() + .substring(path.getWwn().length() - LunPathManager.SOFT_LINK_WWN_LEN); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/MinimalistInstallConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/MinimalistInstallConfig.java new file mode 100644 index 000000000..c998c6485 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/MinimalistInstallConfig.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MinimalistInstallConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/MinimalistInstallConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.node.MinimalistInstallNodeConfig; +import lombok.Data; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; + +import java.util.ArrayList; +import java.util.List; + +/** + * Minimalist installation configuration + * + * @author lhf + * @date 2022/8/4 23:14 + **/ +@Data +public class MinimalistInstallConfig implements ClusterInstallConfig { + private String installPackagePath; + + private Integer port; + + private String databaseUsername; + + private String databasePassword; + + private List nodeConfigList; + + @Override + public void checkConfig() { + if (StrUtil.isEmpty(databasePassword)) { + throw new OpsException("The database password format is incorrect"); + } + + if (CollUtil.isEmpty(nodeConfigList)) { + throw new OpsException("The cluster node is incorrectly configured"); + } + + for (MinimalistInstallNodeConfig minimalistInstallNodeConfig : nodeConfigList) { + minimalistInstallNodeConfig.checkConfig(); + } + } + + /** + * toOpsClusterNodeEntityList + * + * @param opsClusterEntity OpsClusterEntity + * @return List + */ + public List toOpsClusterNodeEntityList(OpsClusterEntity opsClusterEntity) { + ArrayList opsClusterNodeEntities = new ArrayList<>(); + for (MinimalistInstallNodeConfig nodeConfig : nodeConfigList) { + String completeDataPath = nodeConfig.getInstallPath() + "/data"; + if (StrUtil.isEmpty(nodeConfig.getDataPath())) { + if (opsClusterEntity.getDeployType() == DeployTypeEnum.CLUSTER) { + String masterDataPath = completeDataPath + "/master"; + nodeConfig.setDataPath(masterDataPath); + nodeConfig.setClusterRole(ClusterRoleEnum.MASTER); + opsClusterNodeEntities.add(nodeConfig.toOpsClusterNodeEntity()); + String slaveDataPath = completeDataPath + "/slave"; + nodeConfig.setDataPath(slaveDataPath); + nodeConfig.setClusterRole(ClusterRoleEnum.SLAVE); + opsClusterNodeEntities.add(nodeConfig.toOpsClusterNodeEntity()); + } else { + completeDataPath = completeDataPath + "/single_node"; + nodeConfig.setDataPath(completeDataPath); + opsClusterNodeEntities.add(nodeConfig.toOpsClusterNodeEntity()); + } + } else { + opsClusterNodeEntities.add(nodeConfig.toOpsClusterNodeEntity()); + } + } + return opsClusterNodeEntities; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/NodeMonitorVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/NodeMonitorVO.java new file mode 100644 index 000000000..65f630b23 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/NodeMonitorVO.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * NodeMonitorVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/NodeMonitorVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * @author lhf + * @date 2022/10/9 22:40 + **/ +@Data +public class NodeMonitorVO { + private String cpu; + private String memory; + private List net; + private String lock; + private String session; + private String connectNum; + private String kernel; + private String memorySize; + private List> sessionMemoryTop10; + private String state; + private Long time; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/NodeNetMonitor.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/NodeNetMonitor.java new file mode 100644 index 000000000..fe7a01f2d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/NodeNetMonitor.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * NodeNetMonitor.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/NodeNetMonitor.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/9 22:43 + **/ +@Data +public class NodeNetMonitor { + private String face; + private String receive; + private String transmit; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OmStatusModel.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OmStatusModel.java new file mode 100644 index 000000000..5ac4a0b66 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OmStatusModel.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OmStatusModel.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OmStatusModel.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; + +import java.util.Map; + +/** + * @author lhf + * @date 2023/2/20 17:22 + **/ +@Data +public class OmStatusModel { + private boolean installCm; + private Map nodeIdMapHostname; + private Map hostnameMapNodeId; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterBody.java new file mode 100644 index 000000000..744bb16ec --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterBody.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterBody.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterBody.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; + +import java.util.List; + +/** + * @author lhf + * @date 2022/9/29 17:18 + **/ +@Data +public class OpsClusterBody { + private String clusterId; + + private List nodeIds; + + private String businessId; + + private Boolean sync; + + private ClusterRoleEnum role; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterContext.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterContext.java new file mode 100644 index 000000000..a4beec0ad --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterContext.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterContext.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterContext.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import lombok.Data; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussSupportOSEnum; + +import java.util.List; + +/** + * @author lhf + * @date 2022/9/29 17:31 + **/ +@Data +public class OpsClusterContext implements Cloneable { + private WsSession retSession; + private RetBuffer retBuffer; + private List opNodeIds; + + private List hostInfoHolders; + + private OpsClusterEntity opsClusterEntity; + + private OpenGaussSupportOSEnum os; + + private List opsClusterNodeEntityList; + + private ClusterRoleEnum role; + + /** + * Legal Cloning Methods + * + * @return OpsClusterContext + */ + @Override + public OpsClusterContext clone() { + try { + return (OpsClusterContext) super.clone(); + } catch (CloneNotSupportedException e) { + throw new OpsException("Failed to clone OpsClusterContext" + e); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterNodeVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterNodeVO.java new file mode 100644 index 000000000..43a290509 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterNodeVO.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterNodeVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterNodeVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import lombok.Data; + +/** + * @author lhf + * @date 2022/9/26 17:39 + **/ +@Data +public class OpsClusterNodeVO { + private String nodeId; + private String clusterRole; + private String publicIp; + private String privateIp; + private String hostname; + private String azName; + private String azAddress; + private String hostId; + private Integer dbPort; + private String dbName; + private String dbUser; + private String dbUserPassword; + private Integer hostPort; + //private String rootPassword; + private Boolean isRemember; + private String installUserName; + + public static OpsClusterNodeVO of(OpsClusterNodeEntity opsClusterNodeEntity) { + OpsClusterNodeVO opsClusterNodeVO = new OpsClusterNodeVO(); + opsClusterNodeVO.setNodeId(opsClusterNodeEntity.getClusterNodeId()); + opsClusterNodeVO.setClusterRole(opsClusterNodeEntity.getClusterRole().name()); + return opsClusterNodeVO; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterTaskNodeVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterTaskNodeVO.java new file mode 100644 index 000000000..b7966e0ae --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterTaskNodeVO.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskNodeVO.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterTaskNodeVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; + +/** + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Data +public class OpsClusterTaskNodeVO { + private String clusterNodeId; + private String clusterId; + private String hostId; + private String hostIp; + private String displayHostIp; + private String hostUserId; + private String hostUsername; + private ClusterRoleEnum nodeType; + private String dataPath; + private String azOwner; + private String azPriority; + private Boolean isCmMaster; + private String cmDataPath; + private Integer cmPort; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterTaskVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterTaskVO.java new file mode 100644 index 000000000..d79514c60 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterTaskVO.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskVO.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterTaskVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; +import org.opengauss.admin.common.enums.ops.OpsClusterTaskStatusEnum; +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; + +import java.util.List; + +/** + * OpsClusterTaskVO + * + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Data +public class OpsClusterTaskVO { + private String clusterId; + private String hostIp; + private String hostId; + private String displayHostIp; + private String hostUserId; + private String hostUsername; + private String os; + private String cpuArch; + private OpenGaussVersionEnum version; + private String versionNum; + private String packageName; + private String packageId; + private String clusterName; + private String databaseUsername; + private String databasePassword; + private Integer databasePort; + private String installPackagePath; + private String installPath; + private String logPath; + private String tmpPath; + private String omToolsPath; + private String corePath; + private String envPath; + private Boolean enableCmTool; + private Boolean enableDcf; + private Boolean enableGenerateEnvironmentVariableFile; + private String xmlConfigPath; + private DeployTypeEnum deployType; + private Integer clusterNodeNum; + private OpsClusterTaskStatusEnum status; + private List clusterNodes; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterVO.java new file mode 100644 index 000000000..7d9a28e71 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterVO.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsClusterVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import com.fasterxml.jackson.annotation.JsonFormat; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; +import lombok.Data; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * @author lhf + * @date 2022/9/26 17:34 + **/ +@Data +public class OpsClusterVO { + + private String clusterId; + private String clusterName; + private String version; + private String versionNum; + private String databasePassword; + private DeployTypeEnum deployType; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date lastCheckAt; + + private Map checkSummary; + + private List clusterNodes; + + public static OpsClusterVO of(OpsClusterEntity opsClusterEntity) { + OpsClusterVO opsClusterVO = new OpsClusterVO(); + opsClusterVO.setClusterId(opsClusterEntity.getClusterId()); + opsClusterVO.setClusterName(opsClusterEntity.getClusterName()); + opsClusterVO.setVersion(opsClusterEntity.getVersion().name()); + opsClusterVO.setVersionNum(opsClusterEntity.getVersionNum()); + opsClusterVO.setDatabasePassword(opsClusterEntity.getDatabasePassword()); + opsClusterVO.setDeployType(opsClusterEntity.getDeployType()); + return opsClusterVO; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsPackagePathDictVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsPackagePathDictVO.java new file mode 100644 index 000000000..7f79d6ccc --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsPackagePathDictVO.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsPackagePathDictVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsPackagePathDictVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackagePathDictEntity; + +/** + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Data +public class OpsPackagePathDictVO { + private String id; + private String os; + private String osVersion; + private String cpuArch; + private String version; + private String urlPath; + private String pkgTmpUseVersion; + private String packageNameTmp; + private String remark; + + public OpsPackagePathDictEntity toEntity() { + OpsPackagePathDictEntity entity = new OpsPackagePathDictEntity(); + entity.setId(id); + entity.setOs(os); + entity.setOsVersion(osVersion); + entity.setCpuArch(cpuArch); + entity.setVersion(version); + entity.setUrlPath(urlPath); + entity.setPkgTmpUseVersion(pkgTmpUseVersion); + entity.setPackageNameTmp(packageNameTmp); + entity.setRemark(remark); + return entity; + } + + /** + * Build full package url. + * + * @param pkgUrlPrefix pkgUrlPrefix + * @param packageVersionNum packageVersionNum + * @param isLatest isLatest + * @return String + */ + public String buildFullPackageUrl(String pkgUrlPrefix, String packageVersionNum, boolean isLatest) { + if (isLatest) { + return pkgUrlPrefix + "/latest/" + urlPath + + "/" + String.format(packageNameTmp, packageVersionNum); + } else { + return pkgUrlPrefix + "/" + packageVersionNum + "/" + urlPath + + "/" + String.format(packageNameTmp, packageVersionNum); + } + + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsPackageVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsPackageVO.java new file mode 100644 index 000000000..8c104f0c9 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsPackageVO.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsPackageVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/OpsPackageVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; +import org.opengauss.admin.common.core.domain.UploadInfo; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackageManagerEntity; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Date; + +/** + * @author wangyl + * @date 203/03/08 13:40 + **/ +@SuperBuilder +@Accessors(chain = true) +@NoArgsConstructor +@Data +public class OpsPackageVO { + private String packageId; + private String name; + private String os; + private String osVersion; + private String cpuArch; + private String packageVersion; + private String packageVersionNum; + private String packageUrl; + private UploadInfo packagePath; + private String type; + private String source; + @JsonIgnore + @JSONField(serialize = false, deserialize = false) + private MultipartFile file; + private String remark; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + public OpsPackageManagerEntity toEntity() { + OpsPackageManagerEntity entity = new OpsPackageManagerEntity(); + entity.setPackageId(packageId); + entity.setName(name); + entity.setOs(os); + entity.setOsVersion(osVersion); + entity.setCpuArch(cpuArch); + entity.setPackageVersion(packageVersion); + entity.setPackageVersionNum(packageVersionNum); + entity.setPackageUrl(packageUrl); + entity.setPackagePath(packagePath); + entity.setType(type); + entity.setSource(source); + entity.setFile(file); + entity.setRemark(remark); + return entity; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/RetBuffer.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/RetBuffer.java new file mode 100644 index 000000000..f850f0644 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/RetBuffer.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * RetBuffer.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/RetBuffer.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +/** + * RetBuffer + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Slf4j +public class RetBuffer { + private StringBuffer buffer; + @Getter + private String clusterId; + + public RetBuffer(String clusterId) { + this.clusterId = clusterId; + buffer = new StringBuffer(); + } + + public String getRetBuffer() { + return buffer.toString(); + } + + public void sendText(String text) { + buffer.append(text).append(System.lineSeparator()); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SSHBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SSHBody.java new file mode 100644 index 000000000..e7fdca56c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SSHBody.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SSHBody.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SSHBody.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; + +/** + * SSHBody + * + * @author lhf + * @date 2022/8/8 13:57 + **/ +@Data +public class SSHBody { + private String hostId; + private String sshUsername; + private String rootPassword; + private String businessId; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SessionVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SessionVO.java new file mode 100644 index 000000000..74805bee1 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SessionVO.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SessionVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SessionVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 15:00 + **/ +@Data +public class SessionVO { + private String datid; + private String datname; + private String pid; + private String sessionid; + private String usesysid; + private String usename; + private String application_name; + private String client_addr; + private String client_hostname; + private String client_port; + private String backend_start; + private String xact_start; + private String query_start; + private String state_change; + private String waiting; + private String enqueue; + private String state; + private String resource_pool; + private String query_id; + private String query; + private String connection_info; + private String unique_sql_id; + private String trace_id; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SharingStorageInstallConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SharingStorageInstallConfig.java new file mode 100644 index 000000000..e5b4b2df2 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SharingStorageInstallConfig.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SharingStorageInstallConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SharingStorageInstallConfig.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.plugin.domain.model.ops; + +import cn.hutool.core.util.StrUtil; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.enums.ops.ConnectTypeEnum; + +import java.util.List; +import java.util.Objects; + +/** + * sharing storage install config + * + * @author shenzheng + * @since 2023.10.19 + **/ +@Slf4j +@Data +public class SharingStorageInstallConfig { + /** + * whether dss is on + */ + private boolean isEnableDss = false; + + private String clusterType = "single-inst"; + + /** + * dss instance home + */ + private String dssHome; + + /** + * dss volume name + */ + private String dssVgName = "data"; + + /** + * Lun for data, all nodes sharing one Lun + */ + private String dssDataLunPath; + + /** + * Lun for data, using ln -s + */ + private String dssDataLunLinkPath; + + /** + * Vg name of xlog LUN + */ + private String xlogVgName = "xlog"; + + /** + * Lun for xlog + */ + private List xlogLunPath; + + /** + * Lun for xlog, using ln -s + */ + private List xlogLunLinkPath; + + /** + * CM sharing Lun + */ + private String cmSharingLunPath; + + /** + * CM sharing Lun, using ln -s + */ + private String cmSharingLunLinkPath; + + /** + * CM voting Lun + */ + private String cmVotingLunPath; + + /** + * CM voting Lun, using ln -s + */ + private String cmVotingLunLinkPath; + + /** + * whether enable ssl when dss is on + */ + private boolean enableSsl = true; + + /** + * inter mes connect type + */ + private ConnectTypeEnum interconnectType = ConnectTypeEnum.TCP; + + /** + * RDMA config + */ + private String rdamConfig; + + /** + * RDMA config + */ + private String rdamLogPath; + + private String getLinkPath(String lunPath) { + LunPathManager lunPathManager = new LunPathManager(lunPath); + String[] s = lunPath.split(" "); + if (s.length < 3) { + throw new OpsException("invalid lun path :" + lunPath); + } + + if (lunPathManager.getWwn().length() < LunPathManager.WWN_LEN_IN_HEX) { + throw new OpsException("invalid wwn :" + lunPathManager.getWwn()); + } + + String lastPart = lunPathManager.getWwn().substring(lunPathManager.getWwn().length() + - LunPathManager.SOFT_LINK_WWN_LEN); + return "/dev/ln_" + lastPart; + } + + /** + * check configuration of sharing storage + */ + public void checkConfig() { + log.info("Start checking sharing storage configuration."); + if (!isEnableDss) { + throw new OpsException("isEnableDss is off when sharing storage arch is chose."); + } + + if (StrUtil.isEmpty(dssHome)) { + throw new OpsException("Dss home is empty."); + } + + if (StrUtil.isEmpty(dssVgName)) { + throw new OpsException("Dss vg name is empty."); + } + + if (StrUtil.isEmpty(dssDataLunPath)) { + throw new OpsException("Dss data lun is empty."); + } + + if (StrUtil.isEmpty(xlogVgName)) { + throw new OpsException("Dss xlog vg name is empty."); + } + + if (xlogLunPath.isEmpty()) { + throw new OpsException("Dss xlog lun is empty."); + } + + if (StrUtil.isEmpty(cmSharingLunPath)) { + throw new OpsException("CM sharing lun is empty."); + } + + if (StrUtil.isEmpty(cmVotingLunPath)) { + throw new OpsException("CM voting lun is empty."); + } + + if (Objects.isNull(interconnectType)) { + throw new OpsException("inter connect type is empty."); + } + + if (interconnectType == ConnectTypeEnum.RDMA) { + if (StrUtil.isEmpty(rdamConfig)) { + throw new OpsException("when interconnectType = RDMA, rdmaConfig can not be empty."); + } + + if (StrUtil.isEmpty(rdamLogPath)) { + throw new OpsException("when interconnectType = RDMA, rdmaLogPath can not be empty."); + } + } + log.info("Checking sharing storage configuration success."); + } + + /** + * append sharing storage to cluster node entity + * + * @param opsClusterNodeEntity OpsClusterNodeEntity + */ + public void appendtoOpsClusterNodeEntity(OpsClusterNodeEntity opsClusterNodeEntity) { + if (isEnableDss) { + opsClusterNodeEntity.setIsEnableDss(true); + opsClusterNodeEntity.setDssDataLunLinkPath(dssDataLunLinkPath); + String output = StringUtils.join(xlogLunLinkPath, ","); + opsClusterNodeEntity.setXlogLunLinkPath(output); + opsClusterNodeEntity.setCmVotingLunLinkPath(cmVotingLunLinkPath); + opsClusterNodeEntity.setCmSharingLunLinkPath(cmSharingLunLinkPath); + } else { + opsClusterNodeEntity.setIsEnableDss(false); + opsClusterNodeEntity.setDssDataLunLinkPath(""); + opsClusterNodeEntity.setXlogLunLinkPath(""); + opsClusterNodeEntity.setCmVotingLunLinkPath(""); + opsClusterNodeEntity.setCmSharingLunLinkPath(""); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SlowSqlVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SlowSqlVO.java new file mode 100644 index 000000000..d1def96ba --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SlowSqlVO.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SlowSqlVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SlowSqlVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/12 18:11 + **/ +@Data +public class SlowSqlVO { + private String start_time; + private String finish_time; + private String slow_sql_threshold; + private String query; + private String query_plan; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SshCommandConstants.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SshCommandConstants.java new file mode 100644 index 000000000..66ee0f859 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SshCommandConstants.java @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SshCommandConstants.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/SshCommandConstants.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +/** + * @author lhf + * @since 2022/8/10 14:51 + **/ +public interface SshCommandConstants { + /** + * default bashrc + */ + String DEFAULT_ENV_BASHRC = "~/.bashrc"; + + /** + * free hard disk space dir + */ + String DIR_FREE_HARD_DISK = "df -BG {0} | awk -F ' ' '{print $4}' | sort"; + /** + * install dependencies + */ + String DEPENDENCY = "yum list installed | egrep 'libaio-devel|flex|bison|ncurses-devel|glibc-devel|patch|redhat" + + "-lsb-core|readline-devel|lsscsi'"; + /** + * monitor dependencies + */ + String MONITOR_DEPENDENCY = "yum list installed | egrep 'coreutils|procps-ng|grep'"; + + /** + * Install user + */ + String OMM_USER = "cat /etc/passwd | awk -F \":\" \"{print $1}\"|grep omm | wc -l"; + /** + * Create OMM user + */ + String CREATE_OMM_USER = "useradd omm"; + + /** + * Modify OMM password + */ + String CHANGE_OMM_PASSWORD = "passwd omm"; + + /** + * limit check + */ + String LIMITS_CHECK = " cat /etc/security/limits.conf | grep 1048576 "; + + /** + * limit + */ + String LIMITS = "echo -e \"* hard nofile 1048576\\n* soft nofile 1048576\\n* hard nproc 1048576\\n* soft nproc 1048576\" >> /etc/security/limits.conf"; + + /** + * check hostname + */ + String HOSTNAME = "cat /etc/hostname | sed 's/\\\"//g'"; + /** + * decompress + */ + String DECOMPRESS = "tar {0} {1} -C {2}"; + /** + * SEMMNI + */ + String SEM = "echo 'kernel.sem = 250 6400000 1000 25600' | sudo tee -a /etc/sysctl.conf && sysctl -p"; + /** + * SEM MNI + */ + String SEM_VALUE = "250 6400000 1000 25600"; + /** + * check sem + */ + String CHECK_SEM = "sysctl kernel.sem | awk '{for(i=3;i<=NF;i++) printf $i (i rootPasswords; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/UnInstallContext.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/UnInstallContext.java new file mode 100644 index 000000000..734e9d4f5 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/UnInstallContext.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UnInstallContext.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/UnInstallContext.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import lombok.Data; +import org.opengauss.admin.plugin.enums.ops.OpenGaussSupportOSEnum; + +import java.util.List; + +/** + * @author lhf + * @date 2022/8/22 21:01 + **/ +@Data +public class UnInstallContext implements Cloneable { + private WsSession retSession; + + private List hostInfoHolders; + + private OpsClusterEntity opsClusterEntity; + + private OpenGaussSupportOSEnum os; + + private List opsClusterNodeEntityList; + + /** + * Legal Cloning Methods + * + * @return UnInstallContext + */ + @Override + public UnInstallContext clone() { + try { + return (UnInstallContext) super.clone(); + } catch (CloneNotSupportedException e) { + throw new OpsException("Failed to clone UnInstallContext" + e); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/UpgradeBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/UpgradeBody.java new file mode 100644 index 000000000..2e7de8e17 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/UpgradeBody.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UpgradeBody.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/UpgradeBody.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; +import org.opengauss.admin.plugin.enums.ops.UpgradeTypeEnum; + +/** + * @author lhf + * @date 2022/12/9 10:19 + **/ +@Data +public class UpgradeBody { + private String clusterId; + private UpgradeTypeEnum upgradeType; + private String upgradePackagePath; + private String versionNum; + private String businessId; + private String hostRootPassword; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/UpgradeContext.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/UpgradeContext.java new file mode 100644 index 000000000..db4fe0fac --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/UpgradeContext.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UpgradeContext.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/UpgradeContext.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.Data; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.enums.ops.OpenGaussSupportOSEnum; +import org.opengauss.admin.plugin.enums.ops.UpgradeTypeEnum; + +/** + * @author lhf + * @date 2022/12/9 11:09 + **/ +@Data +public class UpgradeContext { + private String hostPublicIp; + private String rootPassword; + private Integer hostPort; + private WsSession retSession; + private String clusterConfigXmlPath; + private UpgradeTypeEnum upgradeType; + private String versionNum; + private String upgradePackagePath; + private String sepEnvFile; + private String installUsername; + private String installUserPassword; + private OpsClusterEntity clusterEntity; + private OpsClusterNodeEntity opsClusterNodeEntity; + private OpenGaussSupportOSEnum os; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/WdrGeneratorBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/WdrGeneratorBody.java new file mode 100644 index 000000000..8ffcae195 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/WdrGeneratorBody.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WdrGeneratorBody.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/WdrGeneratorBody.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import org.opengauss.admin.plugin.enums.ops.WdrScopeEnum; +import org.opengauss.admin.plugin.enums.ops.WdrTypeEnum; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +/** + * @author lhf + * @date 2022/10/13 15:22 + **/ +@Data +public class WdrGeneratorBody { + @NotBlank(message = "The cluster ID cannot be empty") + private String clusterId; + @NotNull(message = "The report scope cannot be empty") + private WdrScopeEnum scope; + private String hostId; + @NotNull(message = "The report type cannot be empty") + private WdrTypeEnum type; + @NotNull(message = "start Snapshot ID cannot be empty") + private String startId; + @NotNull(message = "end Snapshot ID cannot be empty") + private String endId; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/WsSession.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/WsSession.java new file mode 100644 index 000000000..0f67a808c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/WsSession.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WsSession.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/WsSession.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import jakarta.websocket.Session; +import java.io.IOException; + +/** + * @author lhf + * @date 2022/8/9 10:00 + **/ +@Slf4j +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WsSession { + private Session session; + private String sessionId; + + public void close() { + try { + session.close(); + } catch (IOException e) { + log.error("Failed to close the ws connection. Procedure", e); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/cache/SSHChannelManager.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/cache/SSHChannelManager.java new file mode 100644 index 000000000..8508049f5 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/cache/SSHChannelManager.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SSHChannelManager.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/cache/SSHChannelManager.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.cache; + +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.model.ops.SSHBody; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import com.jcraft.jsch.ChannelShell; +import com.jcraft.jsch.Session; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author lhf + * @date 2022/8/11 20:37 + **/ +@Component +public class SSHChannelManager { + @Autowired + private JschUtil jschUtil; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + + private static final ConcurrentHashMap CHANNEL_CONTEXT = new ConcurrentHashMap<>(); + + + public Optional initChannelShell(SSHBody sshBody, OpsHostEntity opsHostEntity, OpsHostUserEntity rootUserEntity) { + String key = sshBody.getBusinessId(); + + Session session = jschUtil.getSession(opsHostEntity.getPublicIp(), opsHostEntity.getPort(), rootUserEntity.getUsername(), encryptionUtils.decrypt(rootUserEntity.getPassword())) + .orElseThrow(() -> new OpsException("Failed to establish session with host")); + + CHANNEL_CONTEXT.remove(key); + return Optional.ofNullable(CHANNEL_CONTEXT.computeIfAbsent(key, p -> jschUtil.openChannelShell(session))); + } + + public Optional getChannelShell(String businessId) { + return Optional.ofNullable(CHANNEL_CONTEXT.get(businessId)); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/cache/TaskManager.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/cache/TaskManager.java new file mode 100644 index 000000000..cede886cc --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/cache/TaskManager.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * TaskManager.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/cache/TaskManager.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.cache; + +import lombok.extern.slf4j.Slf4j; + +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * @author lhf + * @date 2022/8/11 20:37 + **/ +@Slf4j +public class TaskManager { + private static final ConcurrentHashMap> TASK_CONTEXT = new ConcurrentHashMap<>(); + + public static void registry(String sessionId, Future future) { + TASK_CONTEXT.put(sessionId, future); + } + + public static int executeCount(String businessPrefix) { + return (int) TASK_CONTEXT.keySet().stream().filter(s -> s.startsWith(businessPrefix)).count(); + } + + /** + * cancel task + * + * @param sessionId session id + * @return task future + */ + public static Optional> remove(String sessionId) { + Future future = TASK_CONTEXT.get(sessionId); + while (Objects.nonNull(future) && !future.isCancelled()) { + future.cancel(true); + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + log.warn("remove task {} interrupted", sessionId); + } + } + return Optional.ofNullable(TASK_CONTEXT.remove(sessionId)); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/cache/WsConnectorManager.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/cache/WsConnectorManager.java new file mode 100644 index 000000000..d4ee5c605 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/cache/WsConnectorManager.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WsConnectorManager.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/cache/WsConnectorManager.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.cache; + + +import org.opengauss.admin.plugin.domain.model.ops.WsSession; +import org.springframework.stereotype.Component; + +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author lhf + * @date 2022/8/11 20:37 + **/ +@Component +public class WsConnectorManager { + private static final ConcurrentHashMap CONNECTOR_CONTEXT = new ConcurrentHashMap<>(); + + /** + * register session + * + * @param sessionId business ID + * @param wsSession websocket session + */ + public void register(String sessionId, WsSession wsSession) { + CONNECTOR_CONTEXT.put(sessionId, wsSession); + } + + /** + * remove session + * + * @param sessionId business ID + */ + public void remove(String sessionId) { + CONNECTOR_CONTEXT.remove(sessionId); + } + + /** + * get session + * + * @param sessionId business ID + * @return websocket session + */ + public Optional getSession(String sessionId) { + return Optional.ofNullable(CONNECTOR_CONTEXT.get(sessionId)); + } + + public void removeByVal(WsSession wsSession) { + CONNECTOR_CONTEXT.forEach((k,v)->{ + if (v.equals(wsSession)){ + CONNECTOR_CONTEXT.remove(k); + } + }); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/dto/OpsClusterTaskDTO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/dto/OpsClusterTaskDTO.java new file mode 100644 index 000000000..11b0b11b1 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/dto/OpsClusterTaskDTO.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskDTO.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/dto/OpsClusterTaskDTO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.dto; + +import lombok.Data; +import org.hibernate.validator.constraints.Range; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskEntity; +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.springframework.beans.BeanUtils; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.util.Optional; + +/** + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Data +public class OpsClusterTaskDTO { + private String clusterId; + @NotBlank(message = "hostId can not be empty") + private String hostId; + @NotBlank(message = "hostUserId can not be empty") + private String hostUserId; + @NotBlank(message = "os can not be empty") + private String os; + @NotBlank(message = "cpuArch can not be empty") + private String cpuArch; + @NotNull(message = "version can not be empty") + private OpenGaussVersionEnum version; + @NotBlank(message = "versionNum can not be empty") + private String versionNum; + @NotBlank(message = "packageName can not be empty") + private String packageName; + @NotBlank(message = "packageId can not be empty") + private String packageId; + @NotBlank(message = "clusterName can not be empty") + private String clusterName; + @NotBlank(message = "databaseUsername can not be empty") + private String databaseUsername; + @NotBlank(message = "databasePassword can not be empty") + private String databasePassword; + @Range(min = 1024, max = 65535, message = "host port should be between 1024 and 65535") + private long port = 5432; + private String installPackagePath; + private String installPath; + private String logPath; + private String tmpPath; + private String omToolsPath; + private String corePath; + private String envPath; + private Boolean enableCmTool; + private Boolean enableGenerateEnvironmentVariableFile; + private String xmlConfigPath; + @NotNull(message = "deployType can not be empty") + private DeployTypeEnum deployType; + + /** + * to entity + * + * @return OpsClusterTaskEntity + */ + public OpsClusterTaskEntity toEntity() { + OpsClusterTaskEntity target = new OpsClusterTaskEntity(); + BeanUtils.copyProperties(this, target); + target.setDatabasePort(getDatabasePort()); + return target; + } + + /** + * get database port + * + * @return port + */ + private Integer getDatabasePort() { + return Optional.of(getPort()).orElse(5432L).intValue(); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/dto/OpsClusterTaskNodeDTO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/dto/OpsClusterTaskNodeDTO.java new file mode 100644 index 000000000..6d841623d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/dto/OpsClusterTaskNodeDTO.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024-2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskNodeDTO.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/dto/OpsClusterTaskNodeDTO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.dto; + +import lombok.Data; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskNodeEntity; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.springframework.beans.BeanUtils; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +/** + * OpsClusterTaskNodeDTO + * + * @author wangchao + * @since 2024/06/15 09:26 + */ +@Data +public class OpsClusterTaskNodeDTO { + private String clusterNodeId; + @NotBlank(message = "clusterId cannot be empty") + private String clusterId; + @NotBlank(message = "hostIp cannot be empty") + private String hostId; + @NotBlank(message = "hostUserId cannot be empty") + private String hostUserId; + @NotNull(message = "nodeType cannot be empty") + private ClusterRoleEnum nodeType; + @NotBlank(message = "dataPath cannot be empty") + private String dataPath; + private String azOwner; + private String azPriority; + private Boolean isCMMaster; + private String cmDataPath; + private Integer cmPort = 5433; + + /** + * Convert to entity. + * + * @return the entity + */ + public OpsClusterTaskNodeEntity toEntity() { + OpsClusterTaskNodeEntity opsClusterTaskNodeEntity = new OpsClusterTaskNodeEntity(); + BeanUtils.copyProperties(this, opsClusterTaskNodeEntity); + opsClusterTaskNodeEntity.setIsCmMaster(isCMMaster); + return opsClusterTaskNodeEntity; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/dto/OpsClusterTaskQueryParamDTO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/dto/OpsClusterTaskQueryParamDTO.java new file mode 100644 index 000000000..0c70df0c3 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/dto/OpsClusterTaskQueryParamDTO.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskQueryParamDTO.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/dto/OpsClusterTaskQueryParamDTO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.dto; + +import lombok.Data; +import org.opengauss.admin.common.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.common.enums.ops.OpsClusterTaskStatusEnum; + +/** + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Data +public class OpsClusterTaskQueryParamDTO { + private String taskId; + private String clusterName; + private OpsClusterTaskStatusEnum status; + private String hostId; + private String hostUserId; + private String os; + private String cpuArch; + private OpenGaussVersionEnum openGaussVersion; + private String openGaussVersionNum; + private int nodeNum = 0; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/EnvProperty.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/EnvProperty.java new file mode 100644 index 000000000..9360cc994 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/EnvProperty.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * EnvProperty.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/EnvProperty.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.env; + +import org.opengauss.admin.plugin.enums.ops.HostEnvStatusEnum; +import lombok.Data; + +/** + * Environment properties + * + * @author lhf + * @date 2022/8/10 10:33 + **/ +@Data +public class EnvProperty { + /** + * queue number + */ + private Integer sortNum; + /** + * property name + */ + private String name; + /** + * property value + */ + private String value; + /** + * property status + */ + private HostEnvStatusEnum status; + /** + * property status message + */ + private String statusMessage; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/HardwareEnv.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/HardwareEnv.java new file mode 100644 index 000000000..9e551757e --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/HardwareEnv.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HardwareEnv.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/HardwareEnv.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.env; + +import lombok.Data; + +import java.util.List; + +/** + * hardware environment + * + * @author lhf + * @date 2022/8/10 10:32 + **/ +@Data +public class HardwareEnv { + private List envProperties; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/HostEnv.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/HostEnv.java new file mode 100644 index 000000000..60dfefe5c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/HostEnv.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostEnv.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/HostEnv.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.env; + +import lombok.Data; + +/** + * Host environment monitoring + * + * @author lhf + * @date 2022/8/10 10:31 + **/ +@Data +public class HostEnv { + /** + * hardware environment + */ + private HardwareEnv hardwareEnv; + /** + * Software Environment + */ + private SoftwareEnv softwareEnv; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/SoftwareEnv.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/SoftwareEnv.java new file mode 100644 index 000000000..a8c098ecf --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/SoftwareEnv.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SoftwareEnv.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/env/SoftwareEnv.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.env; + +import lombok.Data; + +import java.util.List; + +/** + * Software Environment + * + * @author lhf + * @date 2022/8/10 10:32 + **/ +@Data +public class SoftwareEnv { + private List envProperties; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/node/EnterpriseInstallNodeConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/node/EnterpriseInstallNodeConfig.java new file mode 100644 index 000000000..73d5e591f --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/node/EnterpriseInstallNodeConfig.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * EnterpriseInstallNodeConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/node/EnterpriseInstallNodeConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.node; + +import cn.hutool.core.util.StrUtil; + +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; + +import lombok.Data; + +import java.util.Objects; + +/** + * Enterprise Edition Installation Node Configuration + * + * @author lhf + * @date 2022/8/4 23:16 + **/ +@Data +public class EnterpriseInstallNodeConfig { + /** + * Cluster role + */ + private ClusterRoleEnum clusterRole; + /** + * host ID + */ + private String hostId; + + private String rootPassword; + /** + * Public IP + */ + private String publicIp; + /** + * Intranet IP + */ + private String privateIp; + /** + * hostname + */ + private String hostname; + + private String installUserId; + + private String installUsername; + + private Boolean isCMMaster; + + private String cmDataPath; + + private Integer cmPort; + + private Integer dcfPort; + + private String dataPath; + + private String azName; + + private String azPriority; + + /** + * Check configuration + * + * @param isInstallCM isInstallCM + */ + public void checkConfig(Boolean isInstallCM) { + if (Objects.isNull(clusterRole)) { + throw new OpsException("Cluster role error"); + } + if (StrUtil.isEmpty(hostId)) { + throw new OpsException("wrong host id"); + } + if (StrUtil.isEmpty(publicIp)) { + throw new OpsException("public IP error"); + } + if (StrUtil.isEmpty(privateIp)) { + throw new OpsException("Intranet IP error"); + } + if (StrUtil.isEmpty(hostname)) { + throw new OpsException("wrong hostname"); + } + if (StrUtil.isEmpty(installUserId)) { + throw new OpsException("install user error"); + } + if (isInstallCM && StrUtil.isEmpty(cmDataPath)) { + throw new OpsException("cm data path error"); + } + if (StrUtil.isEmpty(dataPath)) { + throw new OpsException("[" + hostname + "]data path error"); + } + } + + public OpsClusterNodeEntity toOpsClusterNodeEntity() { + OpsClusterNodeEntity opsClusterNodeEntity = new OpsClusterNodeEntity(); + opsClusterNodeEntity.setClusterNodeId(StrUtil.uuid()); + opsClusterNodeEntity.setClusterRole(clusterRole); + opsClusterNodeEntity.setHostId(hostId); + opsClusterNodeEntity.setInstallUserId(installUserId); + opsClusterNodeEntity.setDataPath(dataPath); + opsClusterNodeEntity.setIsCMMaster(isCMMaster); + opsClusterNodeEntity.setCmDataPath(cmDataPath); + opsClusterNodeEntity.setCmPort(cmPort); + return opsClusterNodeEntity; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/node/LiteInstallNodeConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/node/LiteInstallNodeConfig.java new file mode 100644 index 000000000..1c7493631 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/node/LiteInstallNodeConfig.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * LiteInstallNodeConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/node/LiteInstallNodeConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.node; + +import cn.hutool.core.util.StrUtil; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import lombok.Data; + +import java.util.Objects; + +/** + * Lite install node configuration + * + * @author lhf + * @date 2022/8/4 23:17 + **/ +@Data +public class LiteInstallNodeConfig { + + private ClusterRoleEnum clusterRole; + + private String hostId; + + private String rootPassword; + + private String installUserId; + + private String installPath; + + private String dataPath; + + public OpsClusterNodeEntity toOpsClusterNodeEntity() { + OpsClusterNodeEntity opsClusterNodeEntity = new OpsClusterNodeEntity(); + opsClusterNodeEntity.setClusterNodeId(StrUtil.uuid()); + opsClusterNodeEntity.setClusterRole(clusterRole); + opsClusterNodeEntity.setHostId(hostId); + opsClusterNodeEntity.setInstallUserId(installUserId); + opsClusterNodeEntity.setInstallPath(installPath); + opsClusterNodeEntity.setDataPath(dataPath); + opsClusterNodeEntity.setPkgPath(installPath.substring(0, installPath.lastIndexOf("/"))); + return opsClusterNodeEntity; + } + + public void checkConfig() { + if (Objects.isNull(clusterRole)) { + throw new OpsException("Node role error"); + } + + if (StrUtil.isEmpty(hostId)) { + throw new OpsException("host information error"); + } + + if (StrUtil.isEmpty(installPath)) { + throw new OpsException("installation directory error"); + } + + if (StrUtil.isEmpty(dataPath)) { + throw new OpsException("data directory error"); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/node/MinimalistInstallNodeConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/node/MinimalistInstallNodeConfig.java new file mode 100644 index 000000000..81f78993f --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/node/MinimalistInstallNodeConfig.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MinimalistInstallNodeConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/node/MinimalistInstallNodeConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.node; + +import cn.hutool.core.util.StrUtil; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import lombok.Data; + +import java.util.Objects; + +/** + * Minimalist installation node configuration + * + * @author lhf + * @date 2022/8/4 23:17 + **/ +@Data +public class MinimalistInstallNodeConfig { + + private ClusterRoleEnum clusterRole; + + private String hostId; + + private String rootPassword; + + private String installUserId; + + private String installPath; + + private String dataPath; + + private Boolean isInstallDemoDatabase; + + public void checkConfig() { + if (Objects.isNull(clusterRole)) { + throw new OpsException("Node role error"); + } + + if (StrUtil.isEmpty(hostId)) { + throw new OpsException("host information error"); + } + + if (StrUtil.isEmpty(installPath)) { + throw new OpsException("installation directory error"); + } + + if (Objects.isNull(isInstallDemoDatabase)) { + isInstallDemoDatabase = false; + } + } + + public OpsClusterNodeEntity toOpsClusterNodeEntity() { + OpsClusterNodeEntity opsClusterNodeEntity = new OpsClusterNodeEntity(); + opsClusterNodeEntity.setClusterNodeId(StrUtil.uuid()); + opsClusterNodeEntity.setClusterRole(clusterRole); + opsClusterNodeEntity.setHostId(hostId); + opsClusterNodeEntity.setInstallUserId(installUserId); + opsClusterNodeEntity.setInstallPath(installPath); + opsClusterNodeEntity.setDataPath(dataPath); + opsClusterNodeEntity.setPkgPath(installPath.substring(0, installPath.lastIndexOf("/"))); + opsClusterNodeEntity.setInstallDemoDatabase(isInstallDemoDatabase); + return opsClusterNodeEntity; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/DistributeConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/DistributeConfig.java new file mode 100644 index 000000000..784f40789 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/DistributeConfig.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DistributeConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/DistributeConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk; + +import lombok.Data; + +@Data +public class DistributeConfig { + private String tableName; + private String column; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/InstallOlkBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/InstallOlkBody.java new file mode 100644 index 000000000..a1ea1d929 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/InstallOlkBody.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * InstallOlkBody.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/InstallOlkBody.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk; + +import lombok.Data; + +@Data +public class InstallOlkBody { + private String businessId; + private InstallOlkContext installContext; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/InstallOlkContext.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/InstallOlkContext.java new file mode 100644 index 000000000..c7102e901 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/InstallOlkContext.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * InstallOlkContext.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/InstallOlkContext.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk; + +import cn.hutool.core.util.StrUtil; +import lombok.Data; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.model.ops.WsSession; + +@Data +public class InstallOlkContext implements Cloneable { + private WsSession retSession; + private OlkConfig olkConfig; + + public void checkConfig() throws OpsException { + if (StrUtil.isEmpty(olkConfig.getRuleYaml())) { + throw new OpsException("Sharding rule yaml config content is empty, please generate rule yaml again"); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/OlkConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/OlkConfig.java new file mode 100644 index 000000000..6edaa16fd --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/OlkConfig.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OlkConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/OlkConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk; + +import lombok.Data; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsOlkEntity; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.DadReqPath; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.OlkParamDto; +import org.springframework.beans.BeanUtils; + +import java.nio.file.Path; +import java.util.List; + +@Data +public class OlkConfig { + private String id; + private String name; + private String olkTarId; + private String ssTarId; + private String dadTarId; + private String dadPort; + private String dadInstallPath; + private String dadInstallHostId; + private String dadInstallUsername; + private String dadInstallPassword; + private String zkTarId; + private String ssPort; + private String olkPort; + private String zkPort; + private String ssInstallPath; + private String ssUploadPath; + private String olkInstallPath; + private String olkUploadPath; + private String ssInstallHostId; + private String olkInstallHostId; + private String ssInstallUsername; + private String olkInstallUsername; + private String ssInstallPassword; + private String olkInstallPassword; + private OlkParamDto olkParamConfig; + private String remark; + // sharding datasource config + private List dsConfig; + private String tableName; + private String columns; + private String ruleYaml; + + public String getDadLogFileName () { + // return Path.of(dadInstallPath, DadReqPath.LOG_FILE_NAME + id).toString(); + return Path.of(dadInstallPath, DadReqPath.OUTPUT_LOG).toString().replace("\\", "/"); + } + + public OpsOlkEntity toEntity () { + OpsOlkEntity entity = new OpsOlkEntity(); + BeanUtils.copyProperties(this, entity); + return entity; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/OlkPageVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/OlkPageVO.java new file mode 100644 index 000000000..e15618311 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/OlkPageVO.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OlkPageVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/OlkPageVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk; + +import lombok.Data; + +@Data +public class OlkPageVO { + private String id; + private String name; + private String dadPkgName; + private String dadInstallIp; + private String dadInstallUsername; + private String dadInstallPath; + private String dadPort; + private String zkPkgName; + private String zkPort; + private String ssPkgName; + private String ssInstallIp; + private String ssInstallUsername; + private String ssInstallPath; + private String ssUploadPath; + private String ssPort; + private String olkPkgName; + private String olkInstallIp; + private String olkInstallUsername; + private String olkInstallPath; + private String olkUploadPath; + private String olkPort; + private String tableName; + private String columns; + private String ruleYaml; + private String remark; + private String updateTime; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/OlkProcessFlagStr.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/OlkProcessFlagStr.java new file mode 100644 index 000000000..06b7e1f45 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/OlkProcessFlagStr.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OlkProcessFlagStr.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/OlkProcessFlagStr.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk; + +public class OlkProcessFlagStr { + /** + * Datakit will use this + */ + public static final String START_DEPLOY_PROCESS = "Start OpenLooKeng Deploy Process, This may take 5~10 minutes..."; + public static final String END_DEPLOY_PROCESS = "OpenLooKeng Deploy Successfully Completed"; + public static final String START_UPLOAD = "Start Uploading All Packages..."; + public static final String START_UPLOAD_SHARDING_PROXY = "Start Uploading ShardingProxy Package To %s, By User %s"; + public static final String START_UPLOAD_ZOOKEEPER = "Start Uploading Zookeeper Package To %s, By User %s"; + public static final String START_UPLOAD_OLK = "Start Uploading OpenLooKeng Package To %s, By User %s"; + public static final String START_UPLOAD_DAD = "Start Uploading Distributed Deployment Component To %s, By User %s"; + public static final String START_UPLOAD_RULE_YAML = "Start Uploading rule.yaml To %s"; + public static final String END_UPLOAD_RULE_YAML = "Start Uploading rule.yaml To %s Complete"; + public static final String END_UPLOAD_SHARDING_PROXY = "Upload ShardingProxy Package To %s Complete"; + public static final String END_UPLOAD_ZOOKEEPER = "Upload Zookeeper Package To %s Complete"; + public static final String END_UPLOAD_OLK = "Upload OpenLooKeng Package To %s Complete"; + public static final String END_UPLOAD_DAD = "Upload Distributed Deployment Component Package To %s Complete"; + public static final String END_UPLOAD = "Uploading Packages Successfully Completed"; + public static final String SEND_CMD_TO_DAD_SERVICE = "Send Command To Deployment Service"; + public static final String SEND_CMD_TO_SERVICE_COMPLETE = "Send Command To Deployment Service Successfully Completed"; + + /** + * From Dad service, main chapter + */ + public static final String START_DEPLOY = "Start OpenLooKeng Deploy Process..."; + public static final String END_DEPLOY_IN_ERROR = "OpenLooKeng Deploy Process Abort With Error"; + public static final String END_DEPLOY = "OpenLooKeng Deploy Process Successfully Completed"; + public static final String START_DESTROY = "Start Destroy OpenLooKeng Service..."; + public static final String END_DESTROY_IN_ERROR = "Destroy OpenLooKeng Service Process Abort With Error"; + public static final String END_DESTROY = "OpenLooKeng Deploy Process Successfully Completed"; + public static final String START_SERVICE = "Start OpenLooKeng Service..."; + public static final String END_START_SERVICE_IN_ERROR = "Start OpenLooKeng Service Process Abort With Error"; + public static final String END_START_SERVICE = "OpenLooKeng Service Successfully Started"; + public static final String START_STOP_SERVICE = "Stopping OpenLooKeng Service..."; + public static final String END_STOP_SERVICE_IN_ERROR = "Stop OpenLooKeng Service Process Abort With Error"; + public static final String END_STOP_SERVICE = "OpenLooKeng Service Successfully Stopped"; + + /** + * From Dad service, sub chapter + */ + public static final String START_INSTALL = "Start Install All Packages..."; + public static final String START_INSTALL_SHARDING_PROXY = "Start Install ShardingProxy On %s"; + public static final String START_INSTALL_ZOOKEEPER = "Start Install Zookeeper On %s"; + public static final String START_INSTALL_OLK = "Start Install OpenLooKeng On %s"; + public static final String END_INSTALL_SHARDING_PROXY = "Install ShardingProxy On %s Complete"; + public static final String END_INSTALL_ZOOKEEPER = "Install Zookeeper On %s Complete"; + public static final String END_INSTALL_OLK = "Install OpenLooKeng On %s Complete"; + public static final String END_INSTALL = "Install All Packages Complete"; + public static final String START_SHARDING_PROXY_SERVICE = "Start Sharding Proxy Service On %s"; + public static final String START_ZOOKEEPER_SERVICE = "Start Zookeeper Service On %s"; + public static final String START_OLK_SERVICE = "Start OpenLooKeng Service On %s"; + public static final String END_SHARDING_PROXY_SERVICE = "Sharding Proxy Service On %s Started"; + public static final String END_ZOOKEEPER_SERVICE = "Zookeeper Service On %s Started"; + public static final String END_OLK_SERVICE = "OpenLooKeng Service On %s Started"; + public static final String END_SERVICE = "Start All Service Complete"; + public static final String DAD_PKG_NAME = "Distributed-Automatic-Deployment"; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/ShardingDatasourceConfig.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/ShardingDatasourceConfig.java new file mode 100644 index 000000000..e04caff83 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/ShardingDatasourceConfig.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ShardingDatasourceConfig.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/ShardingDatasourceConfig.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk; + +import lombok.Data; + +@Data +public class ShardingDatasourceConfig { + private String host; + private String port; + private String dbName; + private String username; + private String password; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/DadInstance.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/DadInstance.java new file mode 100644 index 000000000..18a5af2cc --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/DadInstance.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DadInstance.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/DadInstance.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk.dadReq; + +import lombok.Data; + +/** + * for concurrent in future + */ +@Data +public class DadInstance { + private String ip; + private String port; + private DadInstanceStatus status; + public static enum DadInstanceStatus { + RUNNING, + STOPPED + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/DadReqPath.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/DadReqPath.java new file mode 100644 index 000000000..0113a3bc9 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/DadReqPath.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DadReqPath.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/DadReqPath.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk.dadReq; + +public class DadReqPath { + public static final String DEPLOY = "/distributed/deployment"; + public static final String DESTROY = "/distributed/destroy"; + public static final String START = "/distributed/start"; + public static final String STOP = "/distributed/stop"; + public static final String OUTPUT_LOG = "output.log"; + public static final String REQ_ID_KEY = "reqId"; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/DadResult.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/DadResult.java new file mode 100644 index 000000000..fca50ea32 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/DadResult.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DadResult.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/DadResult.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk.dadReq; + +import cn.hutool.core.util.ObjectUtil; + +import java.util.HashMap; + +public class DadResult extends HashMap { + /** + * code + */ + public static final String CODE_TAG = "code"; + /** + * message + */ + public static final String MSG_TAG = "msg"; + /** + * data + */ + public static final String DATA_TAG = "data"; + public static final int SUCCESS = 200; + public static final int ERROR = 500; + private static final long serialVersionUID = 1L; + + public DadResult() { + } + + public DadResult(int code, String msg) { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + public DadResult(int code, String msg, Object data) { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (data != null) { + super.put(DATA_TAG, data); + } + } + + public static DadResult success() { + return DadResult.success("success"); + } + + public static DadResult success(Object data) { + return DadResult.success("success", data); + } + + public static DadResult success(String msg) { + return DadResult.success(msg, null); + } + + public static DadResult success(String msg, Object data) { + return new DadResult(SUCCESS, msg, data); + } + + public static DadResult error() { + return DadResult.error("fail"); + } + + public static DadResult error(String msg) { + return DadResult.error(msg, null); + } + + public static DadResult error(String msg, Object data) { + return new DadResult(ERROR, msg, data); + } + + public static DadResult error(int code, String msg) { + return new DadResult(code, msg, null); + } + + public boolean isSuccess() { + if (ObjectUtil.isNull(get(CODE_TAG))) { + return false; + } + return get(CODE_TAG).equals(SUCCESS); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/OlkCommandDto.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/OlkCommandDto.java new file mode 100644 index 000000000..099e01b4f --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/OlkCommandDto.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OlkCommandDto.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/OlkCommandDto.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk.dadReq; + +import lombok.Data; + +@Data +public class OlkCommandDto { + private ShardingsDto shardingsDto; + private OpenLooKengDto openLooKengDto; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/OlkParamDto.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/OlkParamDto.java new file mode 100644 index 000000000..2180cf5dd --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/OlkParamDto.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OlkParamDto.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/OlkParamDto.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk.dadReq; + +import lombok.Data; + +@Data +public class OlkParamDto { + private String xmx = "16G"; + private String maxMemory = "50GB"; + private String maxTotalMemory = "50GB"; + private String maxMemoryPer = "10GB"; + private String maxTotalMemoryPer = "10GB"; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/OpenLooKengDto.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/OpenLooKengDto.java new file mode 100644 index 000000000..250d25056 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/OpenLooKengDto.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpenLooKengDto.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/OpenLooKengDto.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk.dadReq; + +import lombok.Data; + +@Data +public class OpenLooKengDto { + private ServerDto serverDto; + private String installPath; + private String sourcePath; + private int port; + private OlkParamDto olkConfigDto; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ServerDto.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ServerDto.java new file mode 100644 index 000000000..4e38ea1b3 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ServerDto.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ServerDto.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ServerDto.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk.dadReq; + +import lombok.Data; + +@Data +public class ServerDto { + private String ip; + private int port; + private String username; + private String password; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ShardingDataSourceDto.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ShardingDataSourceDto.java new file mode 100644 index 000000000..740975fc2 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ShardingDataSourceDto.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ShardingDataSourceDto.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ShardingDataSourceDto.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk.dadReq; + +import lombok.Data; + +@Data +public class ShardingDataSourceDto { + private String url; + private String username; + private String password; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ShardingRuleDto.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ShardingRuleDto.java new file mode 100644 index 000000000..27b93f89e --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ShardingRuleDto.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ShardingRuleDto.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ShardingRuleDto.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk.dadReq; + +import lombok.Data; + +@Data +public class ShardingRuleDto { + private ShardingDataSourceDto dataSourceDto; + private String tableName; + private String column; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ShardingsDto.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ShardingsDto.java new file mode 100644 index 000000000..3486afb9d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ShardingsDto.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ShardingsDto.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ShardingsDto.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk.dadReq; + +import lombok.Data; + +@Data +public class ShardingsDto { + private ShardingRuleDto shardingRuleDto; + private ZookeeperDto zookeeperDto; + private String installPath; + private String sourcePath; + private ServerDto serverDto; + private int port; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ZookeeperDto.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ZookeeperDto.java new file mode 100644 index 000000000..e2e7dc77e --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ZookeeperDto.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ZookeeperDto.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/olk/dadReq/ZookeeperDto.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.domain.model.ops.olk.dadReq; + +import lombok.Data; + +@Data +public class ZookeeperDto { + private int port; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/DbDataLocationEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/DbDataLocationEnum.java new file mode 100644 index 000000000..057828b68 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/DbDataLocationEnum.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) GBA-NCTI-ISDC. 2022-2024. + * + * openGauss DataKit is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DbDataLocationEnum.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/DbDataLocationEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +/** + * DbDataLocationEnum + * + * @since 2024/1/15 + */ +@AllArgsConstructor +@Getter +public enum DbDataLocationEnum { + INTARKDB("org.intarkdb.Driver", Arrays.asList("classpath:intarkdb-schema.sql")), + OPENGAUSS("org.opengauss.Driver", Arrays.asList("classpath:openGauss-schema.sql")); + + private String driverClass; + private List locations; + + /** + * get DbDataLocationEnum by driverClass + * + * @param driverClass String + * @return Optional + */ + public static Optional of(String driverClass) { + if (StrUtil.isEmpty(driverClass)) { + return Optional.empty(); + } + for (DbDataLocationEnum enumConstant : DbDataLocationEnum.class.getEnumConstants()) { + if (enumConstant.getDriverClass().equalsIgnoreCase(driverClass)) { + return Optional.of(enumConstant); + } + } + return Optional.empty(); + } +} + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/ClusterOperateTypeEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/ClusterOperateTypeEnum.java new file mode 100644 index 000000000..eb44561a7 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/ClusterOperateTypeEnum.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterOperateTypeEnum.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/ClusterOperateTypeEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * ClusterOperateTypeEnum + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Getter +@AllArgsConstructor +public enum ClusterOperateTypeEnum { + CREATE("CREATE"), + UPDATE("UPDATE"), + CONFIRM("CONFIRM"), + DELETE("DELETE"), + COPY("COPY"), + CHECK_ENVIRONMENT("CHECK_ENVIRONMENT"), + INSTALL("INSTALL"); + + private final String operateType; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/ClusterRoleEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/ClusterRoleEnum.java new file mode 100644 index 000000000..0f1841949 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/ClusterRoleEnum.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterRoleEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/ClusterRoleEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +/** + * @author lhf + * @date 2022/8/6 17:10 + **/ +public enum ClusterRoleEnum { + /** + * master + */ + MASTER, + /** + * slave + */ + SLAVE, + /** + * cascade slave + */ + CASCADE; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/ConnectTypeEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/ConnectTypeEnum.java new file mode 100644 index 000000000..dfb6f4b3f --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/ConnectTypeEnum.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ConnectTypeEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/ConnectTypeEnum.java + * + * ------------------------------------------------------------------------- + */ +package org.opengauss.admin.plugin.enums.ops; + +/** + * sharing storage interconnect type + * + * @author shenzheng + * @since 2023.10.19 + **/ +public enum ConnectTypeEnum { + /** + * connect by TCP + */ + TCP, + + /** + * connect by RDMA + */ + RDMA; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/DatabaseKernelArch.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/DatabaseKernelArch.java new file mode 100644 index 000000000..c963a45e4 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/DatabaseKernelArch.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DatabaseKernelArch.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/DatabaseKernelArch.java + * + * ------------------------------------------------------------------------- + */ +package org.opengauss.admin.plugin.enums.ops; + +/** + * Database Kernel Architecture + * + * @author shenzheng + * @since 2023.10.24 + **/ +public enum DatabaseKernelArch { + MASTER_SLAVE, + + SHARING_STORAGE; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/DeployTypeEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/DeployTypeEnum.java new file mode 100644 index 000000000..70ce27184 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/DeployTypeEnum.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DeployTypeEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/DeployTypeEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +/** + * deploy type + * + * @author lhf + * @date 2022/8/6 17:08 + **/ +public enum DeployTypeEnum { + /** + * cluster + */ + CLUSTER, + /** + * single node + */ + SINGLE_NODE; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/DiskQueryMethodEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/DiskQueryMethodEnum.java new file mode 100644 index 000000000..fe3e4b36b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/DiskQueryMethodEnum.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DiskQueryMethodEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/domain/model/ops/DiskQueryMethodEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.opengauss.admin.plugin.domain.model.ops.SshCommandConstants; + +/** + * Disk path query method + * + * @author shenzheng + * @since 2023.12.9 + **/ + +@Getter +@AllArgsConstructor +public enum DiskQueryMethodEnum { + /** + * LS SCSI + */ + SCSI(SshCommandConstants.LS_SCSI, SshCommandConstants.FIND_DISK_BY_SCSI), + /** + * UPADMIN show vlun + */ + UPADMIN(SshCommandConstants.UPADMIN, SshCommandConstants.FIND_DISK_BY_UPADMIN), + /** + * UPADMIN_PLUS show vlun + */ + UPADMIN_PLUS(SshCommandConstants.UPADMIN_PLUS, SshCommandConstants.FIND_DISK_BY_UPADMIN_PLUS); + + private final String queryAllDiskCmd; + private final String queryDiskVolumeCmd; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/GucSettingContextEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/GucSettingContextEnum.java new file mode 100644 index 000000000..6e5229a68 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/GucSettingContextEnum.java @@ -0,0 +1,17 @@ +package org.opengauss.admin.plugin.enums.ops; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum GucSettingContextEnum { + POSTMASTER("postmaster"), + USERSET("user"), + INTERNAL("internal"), + BACKEND("backend"), + SUSET("superuser"), + SIGHUP("sighup"); + + private String code; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/HostAuthenticationTypeEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/HostAuthenticationTypeEnum.java new file mode 100644 index 000000000..660cc6fe4 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/HostAuthenticationTypeEnum.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostAuthenticationTypeEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/HostAuthenticationTypeEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +/** + * Host authentication type + * + * @author lhf + * @date 2022/8/4 22:21 + **/ +public enum HostAuthenticationTypeEnum { + /** + * User Name Password + */ + USERNAME_PASSWORD; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/HostEnvStatusEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/HostEnvStatusEnum.java new file mode 100644 index 000000000..3287d2332 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/HostEnvStatusEnum.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostEnvStatusEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/HostEnvStatusEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +/** + * Host Environment Status + * + * @author lhf + * @date 2022/8/10 10:34 + **/ +public enum HostEnvStatusEnum { + NORMAL, + WARMING, + ERROR, + INFO +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/HostFileTypeEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/HostFileTypeEnum.java new file mode 100644 index 000000000..f0920bbf0 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/HostFileTypeEnum.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostFileTypeEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/HostFileTypeEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +/** + * @author lhf + * @date 2022/8/8 10:12 + **/ +public enum HostFileTypeEnum { + FILE, + DIRECTORY +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/InstallModeEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/InstallModeEnum.java new file mode 100644 index 000000000..162037d2b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/InstallModeEnum.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * InstallModeEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/InstallModeEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +/** + * Installation Mode + * + * @author lhf + * @date 2022/8/4 23:00 + **/ +public enum InstallModeEnum { + /** + * online + */ + ON_LINE, + /** + * offline + */ + OFF_LINE; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/OpenGaussSupportOSEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/OpenGaussSupportOSEnum.java new file mode 100644 index 000000000..984b97c4c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/OpenGaussSupportOSEnum.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpenGaussSupportOSEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/OpenGaussSupportOSEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.opengauss.admin.common.enums.OsSupportMap; +import org.opengauss.admin.plugin.domain.model.ops.SshCommandConstants; + +/** + * @author lhf + * @date 2022/12/11 13:38 + **/ +@Getter +@AllArgsConstructor +public enum OpenGaussSupportOSEnum { + CENTOS_X86_64("centos", "x86_64", + SshCommandConstants.INSTALL_DEPENDENCY), + OPENEULER_AARCH64("openEuler", "aarch64", + SshCommandConstants.INSTALL_DEPENDENCY_OPENEULER_ARCH64), + OPENEULER_X86_64("openEuler", "x86_64", + SshCommandConstants.INSTALL_DEPENDENCY_OPENEULER_X86); + + private String osId; + private String cpuArch; + private String dependencyCommand; + + public static OpenGaussSupportOSEnum of(String osInfo, String osVersionInfo, String cpuArchInfo) { + OsSupportMap osMapEnum = OsSupportMap.of(osInfo, osVersionInfo, cpuArchInfo); + if (osMapEnum == OsSupportMap.CENTOS_7_X86_64) { + return CENTOS_X86_64; + } + + if ("x86_64".equalsIgnoreCase(osMapEnum.getCpuArch())) { + return OPENEULER_X86_64; + } + return OPENEULER_AARCH64; + } + + public boolean match(String os,String cpuArch) { + return this.getOsId().equalsIgnoreCase(os) && this.getCpuArch().equalsIgnoreCase(cpuArch); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/OpenGaussVersionEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/OpenGaussVersionEnum.java new file mode 100644 index 000000000..dbf5a654e --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/OpenGaussVersionEnum.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpenGaussVersionEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/OpenGaussVersionEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +/** + * openGauss version + * + * @author lhf + * @date 2022/8/4 23:06 + **/ +public enum OpenGaussVersionEnum { + /** + * Enterprise Edition + */ + ENTERPRISE, + /** + * Minimalist version + */ + MINIMAL_LIST, + /** + * Light weight version + */ + LITE +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/OpsPackageSource.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/OpsPackageSource.java new file mode 100644 index 000000000..e2fe261b6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/OpsPackageSource.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsPackageSource.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/OpsPackageSource.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * OpsPackageSource + * + * @author wangchao + * @since 2024/10/29 09:26 + */ +@Getter +@AllArgsConstructor +public enum OpsPackageSource { + /** + * package source is online + */ + ONLINE("online"), + /** + * package source is offline + */ + OFFLINE("offline"), + /** + * unknown package source + */ + UNKNOWN("unknown"); + + private String name; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/PackageTypeEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/PackageTypeEnum.java new file mode 100644 index 000000000..bbc6ac558 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/PackageTypeEnum.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * PackageTypeEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/PackageTypeEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * installation package type + * + * @author wangyl + * @date 2023/3/9 17:08 + **/ +@Getter +@AllArgsConstructor +public enum PackageTypeEnum { + OPENGAUSS("openGauss"), + ZOOKEEPER("zookeeper"), + SHARDING_PROXY("shardingProxy"), + OPENLOOKENG("openLooKeng"), + DISTRIBUTE_DEPLOY("dad"); + + private String packageType; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/UpgradeTypeEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/UpgradeTypeEnum.java new file mode 100644 index 000000000..3ed339efe --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/UpgradeTypeEnum.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * UpgradeTypeEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/UpgradeTypeEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +/** + * @author lhf + * @date 2022/12/9 10:20 + **/ +public enum UpgradeTypeEnum { + UPGRADE, + GRAY_UPGRADE; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/WdrScopeEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/WdrScopeEnum.java new file mode 100644 index 000000000..65faeec4d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/WdrScopeEnum.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WdrScopeEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/WdrScopeEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +/** + * @author lhf + * @date 2022/10/13 14:53 + **/ +public enum WdrScopeEnum { + CLUSTER, + NODE; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/WdrTypeEnum.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/WdrTypeEnum.java new file mode 100644 index 000000000..15f58fe58 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/WdrTypeEnum.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WdrTypeEnum.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/WdrTypeEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +/** + * @author lhf + * @date 2022/10/13 14:54 + **/ +public enum WdrTypeEnum { + DETAIL, + SUMMARY, + ALL; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/WsConnectType.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/WsConnectType.java new file mode 100644 index 000000000..bcb793512 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/WsConnectType.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WsConnectType.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/enums/ops/WsConnectType.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums.ops; + +import lombok.AllArgsConstructor; + +/** + * websocket connection type + * + * @author lhf + * @date 2022/8/6 18:05 + **/ +@AllArgsConstructor +public enum WsConnectType { + /** + * Downloading the Installation Package + */ + DOWNLOAD_INSTALL_PACKAGE, + /** + * ssh Interaction + */ + SSH, + /** + * Execute a command + */ + COMMAND_EXEC; + + public static WsConnectType nameOf(String name) { + WsConnectType[] enumConstants = WsConnectType.class.getEnumConstants(); + for (WsConnectType enumConstant : enumConstants) { + if (enumConstant.name().equalsIgnoreCase(name)){ + return enumConstant; + } + } + return null; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/factory/OperateLogFactory.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/factory/OperateLogFactory.java new file mode 100644 index 000000000..b67faa1b7 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/factory/OperateLogFactory.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperateLogFactory.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/factory/OperateLogFactory.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.factory; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.opengauss.admin.framework.manager.AsyncManager; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterOperateLog; +import org.opengauss.admin.plugin.enums.ops.ClusterOperateTypeEnum; +import org.opengauss.admin.plugin.service.ops.IOpsClusterLogService; +import org.opengauss.admin.plugin.utils.SpringUtils; + +import java.time.LocalDateTime; +import java.util.Objects; +import java.util.TimerTask; + +/** + * OperateLogFactory + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +public class OperateLogFactory { + private static final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * save cluster create operate log + * + * @param clusterId cluster id + */ + public static void operateCreate(String clusterId) { + saveLog(logOperate(clusterId, ClusterOperateTypeEnum.CREATE, "新建集群任务")); + } + + /** + * save cluster update operate log + * + * @param clusterId cluster id + */ + public static void operateUpdate(String clusterId) { + saveLog(logOperate(clusterId, ClusterOperateTypeEnum.UPDATE, "修改集群任务")); + } + + /** + * save cluster confirm operate log + * + * @param clusterId cluster id + */ + public static void operateConfirm(String clusterId) { + saveLog(logOperate(clusterId, ClusterOperateTypeEnum.CONFIRM, "确认集群任务")); + } + + /** + * save cluster copy operate log + * + * @param clusterId cluster id + */ + public static void operateCopy(String clusterId) { + saveLog(logOperate(clusterId, ClusterOperateTypeEnum.COPY, "复制集群任务")); + } + + /** + * save cluster install operate log + * + * @param clusterId clusterId + * @param content content + */ + public static void operateInstall(String clusterId, String content) { + saveLog(logOperate(clusterId, ClusterOperateTypeEnum.INSTALL, content)); + } + + private static OpsClusterOperateLog logOperate(String clusterId, ClusterOperateTypeEnum operateType, Object content) { + OpsClusterOperateLog operateLog = new OpsClusterOperateLog(); + operateLog.setClusterId(clusterId); + operateLog.setOperateType(operateType); + operateLog.setOperateLog(toString(content)); + operateLog.setOperateTime(LocalDateTime.now()); + return operateLog; + } + + /** + * save cluster check environment operate log + * + * @param clusterId cluster id + * @param clusterNodeId cluster node id + * @param content content + */ + public static void operateCheckEnvironment(String clusterId, String clusterNodeId, Object content) { + OpsClusterOperateLog operateLog = logOperate(clusterId, ClusterOperateTypeEnum.CHECK_ENVIRONMENT, content); + operateLog.setClusterNodeId(clusterNodeId); + saveLog(operateLog); + } + + private static void saveLog(OpsClusterOperateLog operateLog) { + AsyncManager.me().execute(new TimerTask() { + @Override + public void run() { + SpringUtils.getBean(IOpsClusterLogService.class).insertOperateLog(operateLog); + } + }); + } + + private static String toString(Object content) { + try { + return Objects.isNull(content) ? "" : objectMapper.writeValueAsString(content); + } catch (JsonProcessingException e) { + return "json exception: " + e.getMessage(); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/listener/BaseOpsPluginListener.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/listener/BaseOpsPluginListener.java new file mode 100644 index 000000000..3f696cadd --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/listener/BaseOpsPluginListener.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BaseOpsPluginListener.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/listener/BaseOpsPluginListener.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.listener; + +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.vo.MenuVo; +import org.opengauss.admin.system.plugin.facade.MenuFacade; +import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.stereotype.Component; + +/** + * @className: ImplListener + * @author: xielibo + * @date: 2022/08/16 3:48 PM + **/ +@Slf4j +@Component +public class BaseOpsPluginListener implements ApplicationListener { + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ApplicationEnvironmentPreparedEvent) { + log.info("base ops init env"); + } else if (event instanceof ApplicationPreparedEvent) { + log.info("base ops init complete"); + } else if (event instanceof ContextRefreshedEvent) { + log.info("base ops was refreshed"); + } else if (event instanceof ApplicationReadyEvent) { + MainApplicationContext context = ((ApplicationReadyEvent) event).getApplicationContext().getBean(MainApplicationContext.class); + SpringBeanFactory factory = context.getSpringBeanFactory(); + MenuFacade menuFacade = factory.getBean(MenuFacade.class); + if (menuFacade != null) { + String pluginId = "base-ops"; + + MenuVo resourceParent = menuFacade.savePluginMenu(pluginId, "资源中心", "Resources", 1, "resource"); + menuFacade.savePluginMenu(pluginId, "集群管理", "cluster ops", 3, "monitor/dailyOps", resourceParent.getMenuId()); + menuFacade.savePluginMenu(pluginId, "OpenLooKeng管理", "openLooKeng manage", 4, "ops/olk", resourceParent.getMenuId()); + MenuVo monitorParent = menuFacade.savePluginMenu(pluginId, "基础运维", "monitor", 3, "monitor"); + menuFacade.savePluginMenu(pluginId, "集群监控", "cluster moniter", 1, "monitor/basic", monitorParent.getMenuId()); + menuFacade.savePluginMenu(pluginId, "日志分析", "log analysis", 2, "monitor/logCenter", monitorParent.getMenuId()); + menuFacade.savePluginMenu(pluginId, "WDR报告", "WDR report", 3, "monitor/wdr", monitorParent.getMenuId()); + menuFacade.savePluginMenu(pluginId, "SQL诊断", "sql analysis", 4, "monitor/slowSql", monitorParent.getMenuId()); + menuFacade.savePluginMenu(pluginId, "自定义控制台", "custom control", 6, "monitor/customControl", monitorParent.getMenuId()); + menuFacade.savePluginMenu(pluginId, "备份恢复", "backup recovery", 7, "ops/backup", monitorParent.getMenuId()); + + MenuVo modelingParent = menuFacade.savePluginMenu(pluginId, "业务建模", "business modeling", 2, "modeling"); + menuFacade.savePluginMenu(pluginId, "模型设计", "model design", 1, "modeling/modelDesign", modelingParent.getMenuId()); + menuFacade.savePluginMenu(pluginId, "报表设计", "data flow design", 2, "modeling/dataflow", modelingParent.getMenuId()); + menuFacade.savePluginRoute(pluginId, "数据流详情", "modeling/dataflow/detail", modelingParent.getMenuId()); + + MenuVo opsInstallParent = menuFacade.savePluginMenu(pluginId, "安装部署", "install", 1, "ops"); + menuFacade.savePluginMenu(pluginId, "一键安装", "quick install", 1, "ops/simpleInstall", opsInstallParent.getMenuId()); + menuFacade.savePluginMenu(pluginId, "集群安装", "cluster install", 2, "ops/install", opsInstallParent.getMenuId()); + menuFacade.savePluginMenu(pluginId, "批量安装", "cluster batch upgrade", 3, "ops/batchInstall", opsInstallParent.getMenuId()); + menuFacade.savePluginMenu(pluginId, "批量升级", "cluster upgrade", 4, "ops/upgrade", opsInstallParent.getMenuId()); + } + log.info("base ops start complete"); + } else if (event instanceof ContextClosedEvent) { + MainApplicationContext context = ((ContextClosedEvent) event).getApplicationContext().getBean(MainApplicationContext.class); + SpringBeanFactory factory = context.getSpringBeanFactory(); + MenuFacade menuFacade = factory.getBean(MenuFacade.class); + if (menuFacade != null) { + menuFacade.deletePluginMenu("base-ops"); + } + log.info("base ops is stopped"); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/listener/OpsImportEntityListener.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/listener/OpsImportEntityListener.java new file mode 100644 index 000000000..b49cf3a24 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/listener/OpsImportEntityListener.java @@ -0,0 +1,25 @@ +package org.opengauss.admin.plugin.listener; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import org.opengauss.admin.plugin.domain.entity.ops.OpsImportEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class OpsImportEntityListener extends AnalysisEventListener { + private static final Logger logger = LoggerFactory.getLogger(OpsImportEntityListener.class); + + @Override + public void invoke(OpsImportEntity user, AnalysisContext analysisContext) { + logger.info("Process data rows: {}", user.toString()); + int currentRowNum = analysisContext.readRowHolder().getRowIndex() + 1; + logger.info("Current processing line number: {}", currentRowNum); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + logger.info("All data processing completed"); + int totalRows = analysisContext.getTotalCount(); + logger.info("Total number of processed lines: {}", totalRows); + } +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/handler/LocalDateTimeTypeHandler.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/handler/LocalDateTimeTypeHandler.java new file mode 100644 index 000000000..25e7367ad --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/handler/LocalDateTimeTypeHandler.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * LocalDateTimeTypeHandler.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/handler/LocalDateTimeTypeHandler.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.handler; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; + +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; + +/** + * LocalDateTimeTypeHandler + * LocalDateTime translate to Timestamp + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +public class LocalDateTimeTypeHandler extends BaseTypeHandler { + @Override + public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType) + throws SQLException { + ps.setTimestamp(i, java.sql.Timestamp.valueOf(parameter)); + } + + @Override + public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException { + java.sql.Timestamp timestamp = rs.getTimestamp(columnName); + return timestamp == null ? null : timestamp.toLocalDateTime(); + } + + @Override + public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + java.sql.Timestamp timestamp = rs.getTimestamp(columnIndex); + return timestamp == null ? null : timestamp.toLocalDateTime(); + } + + @Override + public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + java.sql.Timestamp timestamp = cs.getTimestamp(columnIndex); + return timestamp == null ? null : timestamp.toLocalDateTime(); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowMapper.java new file mode 100644 index 000000000..31b1a8dc0 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowMapper.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.modeling; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Select; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** +* @author LZW +* @description modeling_data_flow +* @createDate 2022-08-10 11:41:13 +* @Entity org.opengauss.admin.plugin.domain.modeling.dataflow.system.ModelingDataFlow +*/ +@Mapper +public interface ModelingDataFlowMapper extends BaseMapper { + @Select("SELECT * FROM modeling_data_flow WHERE name = #{name}") + List findByName(String name); +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowOperatorGroupMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowOperatorGroupMapper.java new file mode 100644 index 000000000..04a2a3be7 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowOperatorGroupMapper.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowOperatorGroupMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowOperatorGroupMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.modeling; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowOperatorGroupEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* @author LZW +* @description modeling_data_flow_operator_group +* @createDate 2022-08-10 10:00:51 +* @Entity org.opengauss.admin.plugin.domain.modeling.dataflow.system.ModelingDataFlowOperatorGroup +*/ +@Mapper +public interface ModelingDataFlowOperatorGroupMapper extends BaseMapper { + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowOperatorMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowOperatorMapper.java new file mode 100644 index 000000000..1683e8311 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowOperatorMapper.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowOperatorMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowOperatorMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.modeling; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowOperatorEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* @author LZW +* @description modeling_data_flow_operator +* @createDate 2022-08-10 10:00:51 +* @Entity org.opengauss.admin.plugin.domain.modeling.dataflow.system.ModelingDataFlowOperator +*/ +@Mapper +public interface ModelingDataFlowOperatorMapper extends BaseMapper { + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowTypeMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowTypeMapper.java new file mode 100644 index 000000000..ddb65f2c6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowTypeMapper.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowTypeMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingDataFlowTypeMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.modeling; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowTypeEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* @author LZW +* @description modeling_data_flow_type +* @createDate 2022-08-10 10:00:51 +* @Entity org.opengauss.admin.plugin.domain.modeling.dataflow.system.ModelingDataFlowType +*/ +@Mapper +public interface ModelingDataFlowTypeMapper extends BaseMapper { + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationCustomDimensionsMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationCustomDimensionsMapper.java new file mode 100644 index 000000000..5205d893a --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationCustomDimensionsMapper.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationCustomDimensionsMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationCustomDimensionsMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.modeling; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationCustomDimensionsEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* @author LZW +* @description modeling_visualization_custom_dimensions +* @createDate 2022-10-18 10:54:56 +* @Entity generator.domain.ModelingVisualizationCustomDimensions +*/ +@Mapper +public interface ModelingVisualizationCustomDimensionsMapper extends BaseMapper { + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationGeoFilesMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationGeoFilesMapper.java new file mode 100644 index 000000000..eec1f6231 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationGeoFilesMapper.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationGeoFilesMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationGeoFilesMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.modeling; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationGeoFilesEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* @author LZW +* @description modeling_visualization_geo_files +* @createDate 2022-11-07 10:42:27 +* @Entity generator.domain.ModelingVisualizationGeoFiles +*/ +@Mapper +public interface ModelingVisualizationGeoFilesMapper extends BaseMapper { + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationParamsMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationParamsMapper.java new file mode 100644 index 000000000..6f38b3ecf --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationParamsMapper.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationParamsMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationParamsMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.modeling; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationParamsEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* @author LZW +* @description modeling_visualization_params +* @createDate 2022-09-23 13:44:13 +* @Entity generator.domain.ModelingVisualizationParams +*/ +@Mapper +public interface ModelingVisualizationParamsMapper extends BaseMapper { + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationReportsMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationReportsMapper.java new file mode 100644 index 000000000..0a436750b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationReportsMapper.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationReportsMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationReportsMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.modeling; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationReportsEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* @author LZW +* @description modeling_visualization_reports +* @Entity generator.domain.ModelingVisualizationReports +*/ +@Mapper +public interface ModelingVisualizationReportsMapper extends BaseMapper { + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationSnapshotsMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationSnapshotsMapper.java new file mode 100644 index 000000000..730d55717 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationSnapshotsMapper.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationSnapshotsMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/modeling/ModelingVisualizationSnapshotsMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.modeling; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationSnapshotsEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* @author LZW +* @description modeling_visualization_snapshots +* @createDate 2022-09-23 13:44:43 +* @Entity generator.domain.ModelingVisualizationSnapshots +*/ +@Mapper +public interface ModelingVisualizationSnapshotsMapper extends BaseMapper { + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsBackupMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsBackupMapper.java new file mode 100644 index 000000000..f539455fa --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsBackupMapper.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsBackupMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsBackupMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.opengauss.admin.plugin.domain.entity.ops.OpsBackupEntity; +import org.opengauss.admin.plugin.vo.ops.BackupVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * @author lhf + * @date 2022/11/5 09:47 + **/ +@Mapper +public interface OpsBackupMapper extends BaseMapper { + Page pageBackup(Page page, @Param("clusterId") String clusterId); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsBaseHostMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsBaseHostMapper.java new file mode 100644 index 000000000..01f255f80 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsBaseHostMapper.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.mapper.ops; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * base host mapper + * + * @author zt + * @since 2025/1/10 + **/ +@Mapper +public interface OpsBaseHostMapper { + /** + * query port by hostId + * + * @param hostId hostId + * @return int + */ + int queryPortByHostId(@Param("hostId") String hostId); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsCheckMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsCheckMapper.java new file mode 100644 index 000000000..e001ee8b8 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsCheckMapper.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsCheckMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsCheckMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.plugin.domain.entity.ops.OpsCheckEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author lhf + * @date 2022/11/13 17:16 + **/ +@Mapper +public interface OpsCheckMapper extends BaseMapper { +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterMapper.java new file mode 100644 index 000000000..e1fc4156c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterMapper.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author lhf + * @date 2022/8/12 09:04 + **/ +@Mapper +public interface OpsClusterMapper extends BaseMapper { +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterNodeMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterNodeMapper.java new file mode 100644 index 000000000..c034add5c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterNodeMapper.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022-2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * cluster node mapper + * + * @author wbd + * @since 2024/1/20 09:14 + **/ +@Mapper +public interface OpsClusterNodeMapper extends BaseMapper { +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterOperateLogMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterOperateLogMapper.java new file mode 100644 index 000000000..638f6e79a --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterOperateLogMapper.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterOperateLogMapper.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterOperateLogMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterOperateLog; + +/** + * OpsClusterOperateLogMapper + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Mapper +public interface OpsClusterOperateLogMapper extends BaseMapper { +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterTaskMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterTaskMapper.java new file mode 100644 index 000000000..a7b853765 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterTaskMapper.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskMapper.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterTaskMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskEntity; + + +/** + * OpsClusterTaskMapper + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Mapper +public interface OpsClusterTaskMapper extends BaseMapper { +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterTaskNodeMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterTaskNodeMapper.java new file mode 100644 index 000000000..1acfdcaaf --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterTaskNodeMapper.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskNodeMapper.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsClusterTaskMapper.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.plugin.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskNodeEntity; + +/** + * OpsClusterTaskNodeMapper + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Mapper +public interface OpsClusterTaskNodeMapper extends BaseMapper { +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsImportSshMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsImportSshMapper.java new file mode 100644 index 000000000..0dca6f4f6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsImportSshMapper.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsImportSshMapper.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsImportSshMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.ops; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.opengauss.admin.plugin.domain.entity.ops.OpsImportSshEntity; + +import java.util.List; + +/** + * OpsImportSshMapper + *

+ * @author: wangchao + * @Date: 2024/11/4 12:02 + * @Description: OpsImportSshMapper + * + * @since 7.0.0 + **/ +@Mapper +public interface OpsImportSshMapper { + /** + * query port, password HostIdAndInstallUserId of installUserName by device publicIp + * + * @param publicIp,installUserName publicIp,installUserName + * @return List + */ + OpsImportSshEntity queryHostInfo(@Param("installUserName") String installUserName, + @Param("publicIp") String publicIp); + + /** + * query clusterId of cluster by publicIp and databasePort + * + * @param publicIp,databasePort publicIp,databasePort + * @return List + */ + List checkPublicIpAndPort(@Param("publicIp") String publicIp, @Param("databasePort") String databasePort); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsOlkMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsOlkMapper.java new file mode 100644 index 000000000..8fe55b8c5 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsOlkMapper.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsOlkMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsOlkMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.plugin.domain.entity.ops.OpsOlkEntity; + +@Mapper +public interface OpsOlkMapper extends BaseMapper { +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsPackageManagerMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsPackageManagerMapper.java new file mode 100644 index 000000000..dd44ac4a1 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsPackageManagerMapper.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsPackageManagerMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsPackageManagerMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackageManagerEntity; + +/** + * @author lhf + * @date 2022/12/11 16:13 + **/ +@Mapper +public interface OpsPackageManagerMapper extends BaseMapper { +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsPackagePathDictMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsPackagePathDictMapper.java new file mode 100644 index 000000000..935836cba --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsPackagePathDictMapper.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsPackagePathDictMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsPackagePathDictMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackagePathDictEntity; + +/** + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Mapper +public interface OpsPackagePathDictMapper extends BaseMapper { +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsWdrMapper.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsWdrMapper.java new file mode 100644 index 000000000..5063c70e1 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsWdrMapper.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsWdrMapper.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/mapper/ops/OpsWdrMapper.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.mapper.ops; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.opengauss.admin.plugin.domain.entity.ops.OpsWdrEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author lhf + * @date 2022/10/13 15:12 + **/ +@Mapper +public interface OpsWdrMapper extends BaseMapper { +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataBaseService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataBaseService.java new file mode 100644 index 000000000..2d8e9bbea --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataBaseService.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IModelingDataBaseService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataBaseService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling; + +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO; +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.opengauss.admin.plugin.domain.model.modeling.OpenGaussConnectorBody; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +/** +* @author LZW +* @description ops facade and db operation +* @createDate 2022-08-10 11:41:13 +*/ +public interface IModelingDataBaseService { + + List> executeWithClusterNode(OpsClusterNodeVO clusterNode, String sql, List params) throws SQLException, ClassNotFoundException; + + List> executeWithClusterNodeAndSchema(OpsClusterNodeVO clusterNode, String schema, String sql, List params) throws SQLException, ClassNotFoundException; + + List> queryWithSqlObject(ModelingDataFlowSqlObject sqlObject) throws SQLException, ClassNotFoundException; + + String updateWithSqlObject(ModelingDataFlowSqlObject sqlObject, String queryType) throws SQLException, ClassNotFoundException; + + Object getSqlResult(ModelingDataFlowSqlObject sqlObject); + + List getClusterList(); + + List getClusterListWithDataBaseName(); + + OpsClusterNodeVO getClusterNodeById(String clusterNodeId); + + ResultSet query(OpenGaussConnectorBody openGaussConnectorBody, String sql, List params) throws ClassNotFoundException, SQLException; + + String update(OpenGaussConnectorBody openGaussConnectorBody, String sql, String queryType) throws ClassNotFoundException, SQLException; + + List> convertList(ResultSet rs); + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowOperatorGroupService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowOperatorGroupService.java new file mode 100644 index 000000000..64e16d3a7 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowOperatorGroupService.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IModelingDataFlowOperatorGroupService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowOperatorGroupService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowOperatorGroupEntity; + +/** +* @author LZW +* @description modeling_data_flow_operator_group +* @createDate 2022-08-10 10:00:51 +*/ +public interface IModelingDataFlowOperatorGroupService extends IService { + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowOperatorService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowOperatorService.java new file mode 100644 index 000000000..f2e434ec3 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowOperatorService.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IModelingDataFlowOperatorService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowOperatorService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowOperatorEntity; + +import java.util.List; + +/** +* @author LZW +* @description modeling_data_flow_operator +* @createDate 2022-08-10 10:00:51 +*/ +public interface IModelingDataFlowOperatorService extends IService { + List selectLegalOperatorConfigNameByType(Integer type); + List selectMainOperatorConfigName(); + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowService.java new file mode 100644 index 000000000..638111980 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowService.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IModelingDataFlowService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowEntity; + +import java.util.List; +import java.util.Map; + +/** +* @author LZW +* @description modeling_data_flow +* @createDate 2022-08-10 11:41:13 +*/ +public interface IModelingDataFlowService extends IService { + + IPage selectList(IPage page, String name); + + Long insertDataFlow(ModelingDataFlowEntity dataFlowData); + + int deleteDataFlowByIds(String[] flowIds); + + int updateDataFlow(ModelingDataFlowEntity dataFlowData); + + List findByName(String name); + + List queryProcessInfo(); + + List> queryGroupByType(); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowTypeService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowTypeService.java new file mode 100644 index 000000000..849de02b1 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowTypeService.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IModelingDataFlowTypeService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingDataFlowTypeService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowTypeEntity; + +/** +* @author LZW +* @description modeling_data_flow_type +* @createDate 2022-08-10 10:00:51 +*/ +public interface IModelingDataFlowTypeService extends IService { + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingVisualizationGeoFilesService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingVisualizationGeoFilesService.java new file mode 100644 index 000000000..6fd8a89aa --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingVisualizationGeoFilesService.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IModelingVisualizationGeoFilesService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/IModelingVisualizationGeoFilesService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationGeoFilesEntity; + +import java.util.List; + +/** +* @author LZW +* @description modeling_visualization_geo_files +* @createDate 2022-11-07 10:42:27 +*/ +public interface IModelingVisualizationGeoFilesService extends IService { + public int add(ModelingVisualizationGeoFilesEntity geoFile); + public List selectByDataFlowId(Long dataFlowId); + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataBaseServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataBaseServiceImpl.java new file mode 100644 index 000000000..f118ba904 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataBaseServiceImpl.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataBaseServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataBaseServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.impl; + +import com.alibaba.fastjson.JSONArray; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO; +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.opengauss.admin.plugin.domain.model.modeling.OpenGaussConnectorBody; + +import org.opengauss.admin.plugin.service.modeling.IModelingDataBaseService; +import org.opengauss.admin.system.plugin.facade.OpsFacade; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.sql.*; +import java.util.*; + +/** +* @author LZW +* @description ops facade +* @createDate 2022-08-10 11:41:13 +*/ +@Slf4j +@Service +public class ModelingDataBaseServiceImpl + implements IModelingDataBaseService { + + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private OpsFacade opsFacade; + + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + + @Override + public List> executeWithClusterNode(OpsClusterNodeVO clusterNode, String sql, List params) throws SQLException, ClassNotFoundException { + OpenGaussConnectorBody openGaussConnectorBody = new OpenGaussConnectorBody(clusterNode); + return queryToList(openGaussConnectorBody,sql,params); + } + + @Override + public List> executeWithClusterNodeAndSchema(OpsClusterNodeVO clusterNode, String schema, String sql, List params) throws SQLException, ClassNotFoundException { + OpenGaussConnectorBody openGaussConnectorBody = new OpenGaussConnectorBody(clusterNode); + openGaussConnectorBody.setSchema(schema); + return queryToList(openGaussConnectorBody,sql,params); + } + + @Override + public List> queryWithSqlObject(ModelingDataFlowSqlObject sqlObject) throws SQLException, ClassNotFoundException { + assert sqlObject != null; + + OpenGaussConnectorBody openGaussConnectorBody = new OpenGaussConnectorBody(sqlObject.getExecuteClusterNode()); + openGaussConnectorBody.setSchema(sqlObject.getExecuteSchema()); + + return queryToList(openGaussConnectorBody,sqlObject.getPreparedSql(),sqlObject.getPreparedParams()); + } + + @Override + public String updateWithSqlObject(ModelingDataFlowSqlObject sqlObject, String queryType) throws SQLException, ClassNotFoundException { + assert sqlObject != null; + + OpenGaussConnectorBody openGaussConnectorBody = new OpenGaussConnectorBody(sqlObject.getExecuteClusterNode()); + openGaussConnectorBody.setSchema(sqlObject.getExecuteSchema()); + + return update(openGaussConnectorBody,sqlObject.toSql(),queryType); + } + + @Override + public Object getSqlResult(ModelingDataFlowSqlObject sqlObject) { + try { + if (Objects.equals(sqlObject.getMainType(), "query")) { + return queryWithSqlObject(sqlObject); + } else { + return updateWithSqlObject(sqlObject,sqlObject.getMainType()); + } + } catch (SQLException | ClassNotFoundException e) { + return e.getMessage(); + } + } + + @Override + public List getClusterList() { + if (opsFacade == null) { + return null; + } + List list = opsFacade.listCluster(); + + return list; + } + + @Override + public List getClusterListWithDataBaseName() { + if (opsFacade == null) { + return null; + } + List list = opsFacade.listCluster(); + list.forEach(cluster -> { + cluster.getClusterNodes().forEach(node -> { + //only support ssh default port + if (node.getHostPort() == 22) { + //get database name list + String sql = "select datname from pg_database;"; + try { + List dbList = new ArrayList<>(); + + List> resultSet = executeWithClusterNodeAndSchema(node,"",sql,null); + resultSet.forEach(ret->{ + dbList.add((String) ret.get("datname")); + }); + node.setDbName(JSONArray.toJSONString(dbList)); + } catch (SQLException | ClassNotFoundException e) { + node.setDbName(JSONArray.toJSONString(new ArrayList<>())); + } + } else { + node.setDbName(JSONArray.toJSONString(new ArrayList<>())); + } + }); + }); + return list; + } + + @Override + public OpsClusterNodeVO getClusterNodeById(String clusterNodeId) { + if (opsFacade == null) { + return null; + } + List list = opsFacade.listCluster(); + for (OpsClusterVO cluster : list) { + for (OpsClusterNodeVO node : cluster.getClusterNodes()) { + if (Objects.equals(node.getNodeId(), clusterNodeId)) { + return node; + } + } + } + return null; + } + + public String update(OpenGaussConnectorBody openGaussConnectorBody, String sql, String queryType) throws ClassNotFoundException, SQLException + { + Connection conn = null; + Class.forName("org.opengauss.Driver"); + String resultMessage; + String openGaussUrl = "jdbc:opengauss://"+openGaussConnectorBody.getIp()+":"+openGaussConnectorBody.getPort()+"/"+openGaussConnectorBody.getDatabase()+"?currentSchema="+openGaussConnectorBody.getSchema(); + conn = DriverManager.getConnection(openGaussUrl, openGaussConnectorBody.getDbUser(), + encryptionUtils.decrypt(openGaussConnectorBody.getDbPassword())); + //start transaction + conn.setAutoCommit(false); + try { + PreparedStatement preparedStatement = conn.prepareStatement(sql); + preparedStatement.executeUpdate(); + conn.commit(); + resultMessage = "database " + queryType + " success!"; + } catch (SQLException e) { + conn.rollback(); + conn.close(); + resultMessage = "database " + queryType + " failed:" + e.getMessage(); + } + + conn.close(); + + return resultMessage; + } + + public ResultSet query(OpenGaussConnectorBody openGaussConnectorBody, String sql, List params) throws ClassNotFoundException, SQLException + { + ResultSet resultSet; + + Class.forName("org.opengauss.Driver"); + String openGaussUrl = "jdbc:opengauss://"+openGaussConnectorBody.getIp()+":"+openGaussConnectorBody.getPort()+"/"+openGaussConnectorBody.getDatabase()+"?currentSchema="+openGaussConnectorBody.getSchema(); + Connection conn = DriverManager.getConnection(openGaussUrl, openGaussConnectorBody.getDbUser(), + encryptionUtils.decrypt(openGaussConnectorBody.getDbPassword())); + + if (sql == null || conn == null) { + throw new RuntimeException("sql is empty"); + } + log.debug("prepared: " + sql); + log.debug("params: " + params); + + PreparedStatement preparedStatement = conn.prepareStatement(sql); + if (params != null && params.size() > 0) { + for (int i = 0; i < params.size(); i++) { + preparedStatement.setString(i + 1, params.get(i)); + } + } + + log.debug("executing: " + preparedStatement.toString()); + + resultSet = preparedStatement.executeQuery(); + conn.close(); + + return resultSet; + } + + public List> queryToList(OpenGaussConnectorBody openGaussConnectorBody, String sql, List params) throws ClassNotFoundException, SQLException + { + return convertList(this.query(openGaussConnectorBody,sql,params)); + } + + public List> convertList(ResultSet rs) { + List> list = new ArrayList>(); + try { + ResultSetMetaData md = rs.getMetaData(); + int columnCount = md.getColumnCount(); + while (rs.next()) { + Map rowData = new HashMap(); + for (int i = 1; i <= columnCount; i++) { + rowData.put(md.getColumnName(i), rs.getObject(i)); + } + list.add(rowData); + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + try { + if (rs != null) { + rs.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + return list; + } +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowOperatorGroupServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowOperatorGroupServiceImpl.java new file mode 100644 index 000000000..27faa59d4 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowOperatorGroupServiceImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowOperatorGroupServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowOperatorGroupServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.plugin.service.modeling.IModelingDataFlowOperatorGroupService; +import org.springframework.stereotype.Service; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowOperatorGroupEntity; +import org.opengauss.admin.plugin.mapper.modeling.ModelingDataFlowOperatorGroupMapper; + +/** +* @author LZW +* @description modeling_data_flow_operator_group +* @createDate 2022-08-10 10:00:51 +*/ +@Service +public class ModelingDataFlowOperatorGroupServiceImpl extends ServiceImpl + implements IModelingDataFlowOperatorGroupService { + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowOperatorServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowOperatorServiceImpl.java new file mode 100644 index 000000000..677cacb89 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowOperatorServiceImpl.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowOperatorServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowOperatorServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.plugin.mapper.modeling.ModelingDataFlowOperatorMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowOperatorEntity; +import org.opengauss.admin.plugin.service.modeling.IModelingDataFlowOperatorService; +import java.util.ArrayList; +import java.util.List; + +/** +* @author LZW +* @description modeling_data_flow_operator +* @createDate 2022-08-10 10:00:51 +*/ +@Service +public class ModelingDataFlowOperatorServiceImpl extends ServiceImpl + implements IModelingDataFlowOperatorService { + + @Autowired + private ModelingDataFlowOperatorMapper modelingDataFlowOperatorMapper; + + @Override + public List selectLegalOperatorConfigNameByType(Integer type) { + List result = modelingDataFlowOperatorMapper + .selectList( + new QueryWrapper().lambda().eq(ModelingDataFlowOperatorEntity::getType, type) + ); + + List configNameList = new ArrayList<>(); + for (ModelingDataFlowOperatorEntity operator : result) { + configNameList.add(operator.getPackagePath()); + } + + return configNameList; + } + + public List selectMainOperatorConfigName() { + List result = modelingDataFlowOperatorMapper + .selectList( + new QueryWrapper().lambda().eq(ModelingDataFlowOperatorEntity::getGroupId, 1) + ); + + List configNameList = new ArrayList<>(); + for (ModelingDataFlowOperatorEntity operator : result) { + configNameList.add(operator.getPackagePath()); + } + + return configNameList; + } +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowServiceImpl.java new file mode 100644 index 000000000..fa88ed59d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowServiceImpl.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.plugin.mapper.modeling.ModelingDataFlowMapper; +import org.opengauss.admin.plugin.service.modeling.IModelingDataFlowService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowEntity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * @author LZW + * @date 2022/8/19 22:38 + */ +@Service +public class ModelingDataFlowServiceImpl extends ServiceImpl + implements IModelingDataFlowService { + + @Autowired + private ModelingDataFlowMapper modelingDataFlowMapper; + + @Override + public IPage selectList(IPage page, String name) { + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(ModelingDataFlowEntity.class) + .like(name != null, ModelingDataFlowEntity::getName, name) + .orderByDesc(ModelingDataFlowEntity::getCreateTime); + + return modelingDataFlowMapper.selectPage(page, queryWrapper); + } + + /** + * insert + * + * @param dataFlowData dataflow body + * @return result + */ + @Override + public Long insertDataFlow(ModelingDataFlowEntity dataFlowData) { + modelingDataFlowMapper.insert(dataFlowData); + return dataFlowData.getId(); + } + + /** + * batch delete dataflow + * + * @param flowIds ids list of dataflow + * @return result + */ + @Override + public int deleteDataFlowByIds(String[] flowIds){ + int row = modelingDataFlowMapper.deleteBatchIds(Arrays.asList(flowIds)); + return row; + } + + /** + * save + * + * @param dataFlowData dataflow body + * @return result + */ + @Override + public int updateDataFlow(ModelingDataFlowEntity dataFlowData) { + int row = modelingDataFlowMapper.updateById(dataFlowData); + return row; + } + + /** + * check name + * + * @param name dataflow name + * @return result + */ + @Override + public List findByName(String name) { + return modelingDataFlowMapper.findByName(name); + } + + @Override + public List> queryGroupByType() { + Page result = modelingDataFlowMapper + .selectPage(new Page<>(1, 5), + new QueryWrapper() + .select(" COUNT(*) AS count, type") + .lambda().groupBy(ModelingDataFlowEntity::getType) + ); + + List> resultList = new ArrayList<>(); + + result.getRecords().forEach( item-> resultList.add(Map.of(item.getType(), item.getCount()))); + + return resultList; + } + + @Override + public List queryProcessInfo() { + return modelingDataFlowMapper + .selectList( + new QueryWrapper() + .lambda() + .isNotNull(ModelingDataFlowEntity::getOperatorContent) + ); + } +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowTypeServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowTypeServiceImpl.java new file mode 100644 index 000000000..0e2164c41 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowTypeServiceImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowTypeServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingDataFlowTypeServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.plugin.mapper.modeling.ModelingDataFlowTypeMapper; +import org.opengauss.admin.plugin.service.modeling.IModelingDataFlowTypeService; +import org.springframework.stereotype.Service; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowTypeEntity; + +/** +* @author LZW +* @description modeling_data_flow_type +* @createDate 2022-08-10 10:00:51 +*/ +@Service +public class ModelingDataFlowTypeServiceImpl extends ServiceImpl + implements IModelingDataFlowTypeService { + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingVisualizationGeoFilesServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingVisualizationGeoFilesServiceImpl.java new file mode 100644 index 000000000..6509d935b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingVisualizationGeoFilesServiceImpl.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationGeoFilesServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/impl/ModelingVisualizationGeoFilesServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationGeoFilesEntity; +import org.opengauss.admin.plugin.mapper.modeling.ModelingVisualizationGeoFilesMapper; +import org.opengauss.admin.plugin.service.modeling.IModelingVisualizationGeoFilesService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** +* @author LZW +* @description modeling_visualization_geo_files +* @createDate 2022-11-07 10:42:27 +*/ +@Service +public class ModelingVisualizationGeoFilesServiceImpl extends ServiceImpl + implements IModelingVisualizationGeoFilesService { + + @Autowired + private ModelingVisualizationGeoFilesMapper modelingVisualizationGeoFilesMapper; + + @Override + public int add(ModelingVisualizationGeoFilesEntity geoFile) { + return modelingVisualizationGeoFilesMapper.insert(geoFile); + } + + @Override + public List selectByDataFlowId(Long dataFlowId) { + List result = modelingVisualizationGeoFilesMapper + .selectList( + new QueryWrapper().lambda().eq(ModelingVisualizationGeoFilesEntity::getDataFlowId, dataFlowId) + ); + + return result; + } +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/IModelingDataFlowOperatorBuilderService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/IModelingDataFlowOperatorBuilderService.java new file mode 100644 index 000000000..75ae99d28 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/IModelingDataFlowOperatorBuilderService.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IModelingDataFlowOperatorBuilderService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/IModelingDataFlowOperatorBuilderService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator; + +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public interface IModelingDataFlowOperatorBuilderService { + + ModelingDataFlowSqlObject getOperatorSql(JSONObject params , ModelingDataFlowSqlObject sqlObject); + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/IModelingDataFlowProcessService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/IModelingDataFlowProcessService.java new file mode 100644 index 000000000..83c94f35b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/IModelingDataFlowProcessService.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IModelingDataFlowProcessService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/IModelingDataFlowProcessService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator; + +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public interface IModelingDataFlowProcessService { + + List getAllSqlObject(JSONObject params); + + ModelingDataFlowSqlObject getSqlObjectByTargetOperatorId(JSONObject params, String operatorId, String dataFlowId); + + String getMainQueryType(JSONObject params); + + Integer getQueryCount(JSONObject params); + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/factory/ModelingDataFlowOperatorFactory.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/factory/ModelingDataFlowOperatorFactory.java new file mode 100644 index 000000000..4671f1c53 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/factory/ModelingDataFlowOperatorFactory.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowOperatorFactory.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/factory/ModelingDataFlowOperatorFactory.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.factory; + +import org.opengauss.admin.plugin.service.modeling.operator.IModelingDataFlowOperatorBuilderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Service +public class ModelingDataFlowOperatorFactory { + + @Autowired + Map builderServiceMap = new ConcurrentHashMap<>(3); + + public IModelingDataFlowOperatorBuilderService getBuilder(String component) { + return builderServiceMap.get(component); + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/BaseBuilderServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/BaseBuilderServiceImpl.java new file mode 100644 index 000000000..d61082c1b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/BaseBuilderServiceImpl.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BaseBuilderServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/BaseBuilderServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + + +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.opengauss.admin.plugin.service.modeling.operator.IModelingDataFlowOperatorBuilderService; + +/** + * @author songlei + * @date 2022/8/25 10:08 + */ +public class BaseBuilderServiceImpl implements IModelingDataFlowOperatorBuilderService { + + @Override + public ModelingDataFlowSqlObject getOperatorSql(JSONObject params, ModelingDataFlowSqlObject sqlObject) { + return sqlObject; + } + + public String formatField(String field) { + if (field.contains(".")) { + return "\"" + field.split("\\.")[0] + "\"." + field.split("\\.")[1]; + } + return field; + } + + public String pureField(String field) { + if (field.contains(".")) { + return field.split("\\.")[1]; + } + return field; + } + + public String pureAliasField(String field) { + return pureField(field).replace("[as]",""); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/ModelingDataFlowProcessServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/ModelingDataFlowProcessServiceImpl.java new file mode 100644 index 000000000..93c178220 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/ModelingDataFlowProcessServiceImpl.java @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingDataFlowProcessServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/ModelingDataFlowProcessServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + +import com.alibaba.fastjson.JSONObject; + + +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowEntity; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowLineParams; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowOperatorParams; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.opengauss.admin.plugin.service.modeling.IModelingDataBaseService; +import org.opengauss.admin.plugin.service.modeling.IModelingDataFlowOperatorService; +import org.opengauss.admin.plugin.service.modeling.IModelingDataFlowService; +import org.opengauss.admin.plugin.service.modeling.operator.IModelingDataFlowOperatorBuilderService; +import org.opengauss.admin.plugin.service.modeling.operator.IModelingDataFlowProcessService; +import org.opengauss.admin.plugin.service.modeling.operator.factory.ModelingDataFlowOperatorFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * @author Administrator + * @createDate 2022-08-10 10:00:51 + */ +@Service +public class ModelingDataFlowProcessServiceImpl implements IModelingDataFlowProcessService { + @Autowired + ModelingDataFlowOperatorFactory modelingDataFlowOperatorFactory; + @Autowired + IModelingDataFlowOperatorService modelingDataFlowOperatorService; + @Autowired + private IModelingDataFlowService modelingDataFlowService; + @Autowired + private IModelingDataBaseService modelingDataBaseService; + + public ModelingDataFlowSqlObject initSqlObject(List operatorParamsList) { + ModelingDataFlowSqlObject resultObj = new ModelingDataFlowSqlObject(); + + for (ModelingDataFlowOperatorParams operatorParams : operatorParamsList) { + String cellsType = operatorParams.getCellType(); + JSONObject data = operatorParams.getData(); + if (operatorParams.getDisable() == null || !operatorParams.getDisable()) { + try { + IModelingDataFlowOperatorBuilderService builder = modelingDataFlowOperatorFactory.getBuilder(cellsType); + if (builder == null) { + throw new RuntimeException("unknown operator type :" + cellsType); + } + resultObj = builder.getOperatorSql(data,resultObj); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + //config db and main operatorId + fillStartOperator(resultObj, operatorParams); + } + + return resultObj; + } + + public ModelingDataFlowSqlObject initSqlObjectWithTargetOperatorId(List operatorParamsList, String targetOperatorId) throws Exception { + int targetLevel = -1; + for (ModelingDataFlowOperatorParams operatorParams : operatorParamsList) { + if (Objects.equals(operatorParams.getUid(), targetOperatorId)) { + targetLevel = operatorParams.getLevel(); + } + } + if (targetLevel == -1) { + return null; + } + + ModelingDataFlowSqlObject resultObj = new ModelingDataFlowSqlObject(); + for (ModelingDataFlowOperatorParams operatorParams : operatorParamsList) { + String cellsType = operatorParams.getCellType(); + JSONObject data = operatorParams.getData(); + if (operatorParams.getDisable() == null || !operatorParams.getDisable()) { + resultObj = modelingDataFlowOperatorFactory.getBuilder(cellsType).getOperatorSql(data,resultObj); + } + //config db and main operatorId + fillStartOperator(resultObj, operatorParams); + //break if reach target operator + if (operatorParams.getLevel() == targetLevel) { + break; + } + } + return resultObj; + } + + @Override + public List getAllSqlObject(JSONObject params) { + ModelingDataFlowEntity dataFlow = modelingDataFlowService.getById(params.getString("dataFlowId")); + + List cells = params.getJSONArray("cells").toJavaList(JSONObject.class); + Map> operatorParamsMap = dispatchOperatorsIntoMap(cells); + + List sqlObjects = new ArrayList<>(); + operatorParamsMap.forEach((k,v)->{ + ModelingDataFlowSqlObject sqlObject = initSqlObject(v); + //check target db config and schema, fill with dataflow default db if null + String targetDbName = sqlObject.getExecuteDbName() != null ? sqlObject.getExecuteDbName() : dataFlow.getDbName(); + String targetClusterNodeId = sqlObject.getExecuteClusterNodeId() != null ? sqlObject.getExecuteClusterNodeId() : dataFlow.getClusterNodeId(); + String targetSchema = sqlObject.getExecuteSchema() != null ? sqlObject.getExecuteSchema() : dataFlow.getSchema(); + OpsClusterNodeVO node = modelingDataBaseService.getClusterNodeById(targetClusterNodeId); + node.setDbName(targetDbName); + + sqlObject.initDataBase(node,targetSchema); + //prepare params @LZW 2022/11/24 + sqlObject.doPrepare(); + sqlObjects.add(sqlObject); + }); + + return sqlObjects; + } + + @Override + public ModelingDataFlowSqlObject getSqlObjectByTargetOperatorId(JSONObject params, String operatorId, String dataFlowId) { + ModelingDataFlowEntity dataFlow = modelingDataFlowService.getById(dataFlowId); + List cells = params.getJSONArray("cells").toJavaList(JSONObject.class); + Map> operatorParamsMap = dispatchOperatorsIntoMap(cells); + for(Map.Entry> entry : operatorParamsMap.entrySet()){ + try { + ModelingDataFlowSqlObject sqlObject = initSqlObjectWithTargetOperatorId(entry.getValue(),operatorId); + //prepare params @LZW 2022/11/24 + if (sqlObject != null) { + //check target db config and schema, fill with dataflow default db if null + String targetDbName = sqlObject.getExecuteDbName() != null ? sqlObject.getExecuteDbName() : dataFlow.getDbName(); + String targetClusterNodeId = sqlObject.getExecuteClusterNodeId() != null ? sqlObject.getExecuteClusterNodeId() : dataFlow.getClusterNodeId(); + String targetSchema = sqlObject.getExecuteSchema() != null ? sqlObject.getExecuteSchema() : dataFlow.getSchema(); + OpsClusterNodeVO node = modelingDataBaseService.getClusterNodeById(targetClusterNodeId); + node.setDbName(targetDbName); + + sqlObject.initDataBase(node,targetSchema); + //prepare params @LZW 2022/11/24 + sqlObject.doPrepare(); + return sqlObject; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return null; + } + + public ModelingDataFlowSqlObject getSqlByMainOperatorId(JSONObject params, String operatorId) { + for (ModelingDataFlowSqlObject sqlObject : this.getAllSqlObject(params)) { + if (Objects.equals(sqlObject.getMainOperatorId(), operatorId)) { + return sqlObject; + } + } + return null; + } + + @Override + public String getMainQueryType(JSONObject params) { + for (ModelingDataFlowSqlObject sqlObject : this.getAllSqlObject(params)) { + return sqlObject.getMainType(); + } + return null; + } + + @Override + public Integer getQueryCount(JSONObject params) { + return this.getAllSqlObject(params).size(); + } + + public Map> dispatchOperatorsIntoMap(List cells ) { + + //dispatch operators into seq + Map> result = new HashMap<>(); + //get enable type of operators from db config + List mainOperatorConfigNames = modelingDataFlowOperatorService.selectMainOperatorConfigName(); + //collect nodes and lines from params + List operatorParamsList = new ArrayList<>(); + List lineList = new ArrayList<>(); + + for(JSONObject cell: cells) { + String cellsType = cell.getString("cells_type"); + //fill line entities + if (Objects.equals(cellsType, "line")) { + ModelingDataFlowLineParams lp = new ModelingDataFlowLineParams(); + lp.setUid(cell.getString("id")); + lp.setSource(cell.getJSONObject("source").getString("cell")); + lp.setTarget(cell.getJSONObject("target").getString("cell")); + lineList.add(lp); + } else { + //fill node entities if legal + ModelingDataFlowOperatorParams op = new ModelingDataFlowOperatorParams(); + op.setDisable(cell.getJSONObject("data").getBoolean("disabled")); + op.setUid(cell.getString("id")); + op.setCellType(cellsType); + op.setData(cell.getJSONObject("data")); + if (mainOperatorConfigNames.contains(cellsType)) { + //set operator to level zero if it belongs to main type + op.setLevel(0); + op.setBelongsToMainOperatorId(op.getUid()); + //add main operator`s uid as key in result map + if (op.getDisable() == null || !op.getDisable()) { + result.put(op.getUid(),new ArrayList<>()); + } + } + operatorParamsList.add(op); + } + } + + operatorParamsList.forEach(operatorParams -> { + if (Objects.equals(operatorParams.getLevel(),0)) { + operatorParams.initDataSource(operatorParamsList,lineList); + operatorParams.sortChildren(operatorParamsList,lineList); + } + }); + + operatorParamsList.removeIf(op -> op.getLevel() == null); + + operatorParamsList.forEach(operatorParams -> { + String uid = operatorParams.getBelongsToMainOperatorId(); + if (result.containsKey(uid)) { + result.get(uid).add(operatorParams); + } + }); + + return result; + } + + private void fillStartOperator(ModelingDataFlowSqlObject resultObj, ModelingDataFlowOperatorParams operatorParams) { + if (operatorParams.getLevel() == 0) { + if (operatorParams.getDataSource() != null) { + //if it has a data source operator + resultObj.setExecuteDbName(operatorParams.getDataSource().getData().getString("dbName")); + resultObj.setExecuteClusterId(operatorParams.getDataSource().getData().getString("clusterId")); + resultObj.setExecuteClusterNodeId(operatorParams.getDataSource().getData().getString("clusterNodeId")); + resultObj.setExecuteSchema(operatorParams.getDataSource().getData().getString("schema")); + } + resultObj.setMainOperatorId(operatorParams.getBelongsToMainOperatorId()); + resultObj.setMainType(operatorParams.getCellType()); + } + } + + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorConditionBuilderServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorConditionBuilderServiceImpl.java new file mode 100644 index 000000000..41c6d9821 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorConditionBuilderServiceImpl.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperatorConditionBuilderServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorConditionBuilderServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** +* @author Administrator +* @createDate 2022-08-10 10:00:51 +*/ +@Component("condition") +public class OperatorConditionBuilderServiceImpl extends BaseBuilderServiceImpl { + + private static final String WHERE_KEYWORD = "where"; + private static final String GROUP_SEPARATOR_KEYWORD = "("; + + @Override + public ModelingDataFlowSqlObject getOperatorSql(JSONObject params , ModelingDataFlowSqlObject sqlObject) { + + //WHERE region modify + String oriSql = sqlObject.getWhereRegion(); + + JSONArray conditions = params.getJSONArray("or"); + + List orSqlList = new ArrayList<>(); + for (int i=0; i andSqlList = new ArrayList<>(); + for (int z=0; z values = List.of(andItem.getString("value").split(",")); + preparedParam = sqlObject.addParamList(values); + } else { + preparedParam = sqlObject.addParam(andItem.getString("value")); + } + String sql = conditionConvert(andItem.getString("field"),condition,preparedParam); + andSqlList.add(sql); + } + } + if (andSqlList.size() > 0) + { + orSqlList.add("( "+String.join(" and ",andSqlList)+" )"); + } + } + + if ( orSqlList.size() > 0 ) { + if (!oriSql.contains(WHERE_KEYWORD)) { + oriSql = "where "; + } + String orSqlString = String.join(" or ",orSqlList); + //Starting from the second conditional operator, the conditions contained in each complete conditional operator are paralleled as [AND] + if (oriSql.contains(GROUP_SEPARATOR_KEYWORD)) { + orSqlString = " and (" + orSqlString + ")"; + } + sqlObject.setWhereRegion(oriSql + orSqlString+" "); + } + + return sqlObject; + } + + public String conditionConvert(String field,String condition,String value) + { + + switch (condition) { + case "equal": + return field + " = " + value; + + case "notEqual": + return field + " <> " + value; + + case "lessThan": + return field + " < " + value; + + case "equalLessThan": + return field + " <= " + value; + + case "greaterThan": + return field + " > " + value; + + case "equalGreaterThan": + return field + " >= " + value; + + case "include": + return field +" like " + value; + + case "notInclude": + return "notlike("+field+"," +value+ ")"; + + case "isNull": + return field+" is NULL"; + + case "notNull": + return field+" is NOT NULL"; + + case "lengthLessThan": + return "length("+field+") < " + value; + + case "lengthLongerThan": + return "length("+field+") > " + value; + + case "in": + return field + " IN (" + value + ")"; + + case "notIn": + return field + " NOT IN " + value + ")"; + + default: + return field + " " + condition + value ; + } + } +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorDeleteBuilderServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorDeleteBuilderServiceImpl.java new file mode 100644 index 000000000..143142247 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorDeleteBuilderServiceImpl.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperatorDeleteBuilderServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorDeleteBuilderServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.springframework.stereotype.Component; +/** + * @author LZW + * @date 2022/10/29 22:38 + */ +@Component("delete") +public class OperatorDeleteBuilderServiceImpl extends BaseBuilderServiceImpl { + + @Override + public ModelingDataFlowSqlObject getOperatorSql(JSONObject params , ModelingDataFlowSqlObject sqlObject) { + // delete region init + String oriSql = sqlObject.getDeleteRegion(); + String tableName = params.getString("table"); + oriSql += tableName+" "; + sqlObject.setDeleteRegion(oriSql); + + return sqlObject; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorDictionaryBuilderServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorDictionaryBuilderServiceImpl.java new file mode 100644 index 000000000..e33d7c772 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorDictionaryBuilderServiceImpl.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperatorDictionaryBuilderServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorDictionaryBuilderServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.springframework.stereotype.Component; + +/** + * @author songlei + * @date 2022/8/25 10:08 + */ +@Component("dictionary") +public class OperatorDictionaryBuilderServiceImpl extends BaseBuilderServiceImpl { + + @Override + public ModelingDataFlowSqlObject getOperatorSql(JSONObject params, ModelingDataFlowSqlObject sqlObject) { + + String dictionaryTable = params.getString("table"); + String matchField = params.getString("matchField"); + String sourceFiled = params.getString("field"); + String rigidField = params.getString("rigidField"); + + if (!dictionaryTable.isEmpty() + && !matchField.isEmpty() + && !sourceFiled.isEmpty() + && !rigidField.isEmpty()) { + // join region modify + String oriSql = sqlObject.getJoinRegion(); + String joinSql = oriSql + " left join \"" + dictionaryTable + "\" on " + formatField(matchField) + " = " + pureField(sourceFiled) + " "; + sqlObject.setJoinRegion(joinSql); + + //mapping region modify + String mappingSql = formatField(rigidField) + " as " + sourceFiled.split("\\.")[1]; + sqlObject.getMappingRegion().add(mappingSql); + } + + return sqlObject; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorGroupBuilderServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorGroupBuilderServiceImpl.java new file mode 100644 index 000000000..69292619a --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorGroupBuilderServiceImpl.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperatorGroupBuilderServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorGroupBuilderServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author songlei + * @date 2022/8/15 16:35 + */ +@Component("group") +public class OperatorGroupBuilderServiceImpl extends BaseBuilderServiceImpl { + + @Override + public ModelingDataFlowSqlObject getOperatorSql(JSONObject params, ModelingDataFlowSqlObject sqlObject){ + //group region modify + String oriSql = sqlObject.getGroupRegion(); + + JSONArray groupFields = params.getJSONArray("groups"); + List sqlGroupFields = new ArrayList<>(); + + if (!groupFields.isEmpty()) { + for (int i = 0; i < groupFields.size(); i++) { + sqlGroupFields.add(groupFields.getJSONObject(i).getString("field")); + } + if (sqlGroupFields.size()>0) { + oriSql = "group by "; + oriSql += String.join(",",sqlGroupFields) + " "; + } + } + + sqlObject.setGroupRegion(oriSql); + + return sqlObject; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorInsertBuilderServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorInsertBuilderServiceImpl.java new file mode 100644 index 000000000..5667fc0b0 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorInsertBuilderServiceImpl.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperatorInsertBuilderServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorInsertBuilderServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.springframework.stereotype.Component; + +import java.util.*; + +/** + * @author LZW + * @date 2022/10/29 22:38 + */ +@Component("insert") +public class OperatorInsertBuilderServiceImpl extends BaseBuilderServiceImpl { + + @Override + public ModelingDataFlowSqlObject getOperatorSql(JSONObject params , ModelingDataFlowSqlObject sqlObject) { + // insert region init + String oriSql = sqlObject.getInsertRegion(); + String tableName = params.getString("table"); + oriSql += tableName+" "; + sqlObject.setInsertRegion(oriSql); + + //modify insert fields + String insertFields = sqlObject.getInsertFieldsRegion(); + //get fields to insert + List insertFieldString = new ArrayList<>(); + List> insertValueString = new ArrayList<>(); + + JSONArray prepareInsertData = params.getJSONArray("list1"); + if (!prepareInsertData.isEmpty()) { + + for (int i = 0; i < prepareInsertData.size(); i++) { + JSONArray fieldDataList = prepareInsertData.getJSONArray(i); + for (int z = 0; z < fieldDataList.size(); z++) { + JSONObject data = fieldDataList.getJSONObject(z); + String field = data.getString("field").split("\\.")[1]; + if (!insertFieldString.contains(field)) { + insertFieldString.add(field); + } + } + } + + for (int i = 0; i < prepareInsertData.size(); i++) { + JSONArray fieldDataList = prepareInsertData.getJSONArray(i); + List childValue = new ArrayList<>(Collections.nCopies(insertFieldString.size(), "''")); + for (int z = 0; z < fieldDataList.size(); z++) { + JSONObject data = fieldDataList.getJSONObject(z); + String field = data.getString("field").split("\\.")[1]; + String value = data.getString("value"); + if (Objects.equals(value, "null")) { + value = "''"; + } else if (!value.chars().allMatch(Character::isDigit)) { + value = "'" + value + "'"; + } + childValue.set(insertFieldString.indexOf(field),value); + } + insertValueString.add(i,childValue); + } + } + + insertFields = "("+String.join(",",insertFieldString)+") " + insertFields; + List insertValue = new ArrayList<>(); + for (List value:insertValueString) { + insertValue.add("("+String.join(",",value)+")"); + } + insertFields = insertFields + String.join(",",insertValue) + " "; + sqlObject.setInsertFieldsRegion(insertFields); + + return sqlObject; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorJoinBuilderServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorJoinBuilderServiceImpl.java new file mode 100644 index 000000000..f5dc5bcde --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorJoinBuilderServiceImpl.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperatorJoinBuilderServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorJoinBuilderServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** +* @author Administrator +* @createDate 2022-08-10 10:00:51 +*/ +@Component("join") +public class OperatorJoinBuilderServiceImpl extends BaseBuilderServiceImpl { + + + @Override + public ModelingDataFlowSqlObject getOperatorSql(JSONObject params , ModelingDataFlowSqlObject sqlObject) { + + // join region modify + String oriSql = sqlObject.getJoinRegion(); + + String joinTable = params.getString("table"); + String joinType = params.getString("connectType"); + JSONArray joinConditions = params.getJSONArray("condition"); + + String joinSql = joinConvert(joinType,joinTable,joinConditions); + if (joinSql.length()>0) { + oriSql += joinSql; + } + + sqlObject.setJoinRegion(oriSql); + + return sqlObject; + } + + public String joinConvert(String joinType, String joinTable,JSONArray joinConditions) { + String joinSql = ""; + joinType = joinTypeConvert(joinType); + String joinCondition = joinConditionsConvert(joinConditions); + + if (joinType.length()>0 && joinTable.length()>0 && joinCondition.length()>0) { + joinSql = " "+joinType+" "+joinTable+ " "+joinCondition+" "; + } + return joinSql; + } + + public String joinTypeConvert(String joinType) + { + switch (joinType) { + case "innerJoin": + return "join "; + + case "leftJoin": + return "left join "; + + case "rightJoin": + return "right join "; + + case "fullJoin": + return "full join "; + + default: + return ""; + } + + } + + public String joinConditionsConvert(JSONArray conditions) { + String conditionSql = ""; + + List orSqlList = new ArrayList<>(); + OperatorConditionBuilderServiceImpl operatorConditionBuilderServiceImpl = new OperatorConditionBuilderServiceImpl(); + + for (int i=0; i andSqlList = new ArrayList<>(); + for (int z=0; z 0) + { + orSqlList.add("( "+String.join(" and ",andSqlList)+" )"); + } + } + + if ( orSqlList.size() > 0 ) { + + conditionSql = " on "+" "+String.join(" or ",orSqlList); + + } + + return conditionSql; + + } + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorLimitBuilderServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorLimitBuilderServiceImpl.java new file mode 100644 index 000000000..a343f635c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorLimitBuilderServiceImpl.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperatorLimitBuilderServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorLimitBuilderServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.springframework.stereotype.Component; + +/** + * @author songlei + * @date 2022/8/16 9:46 + */ +@Component("limit") +public class OperatorLimitBuilderServiceImpl extends BaseBuilderServiceImpl { + @Override + public ModelingDataFlowSqlObject getOperatorSql(JSONObject params, ModelingDataFlowSqlObject sqlObject){ + //limit region modify + String oriSql = sqlObject.getLimitRegion(); + + JSONObject restriction = params.getJSONObject("restriction"); + + if (!restriction.isEmpty()) { + String limitCount = restriction.getString("limitCount"); + if (limitCount != null) { + oriSql += "limit "+ limitCount + " "; + } + + String skip = restriction.getString("skip"); + if (skip != null) { + oriSql += "offset " + skip + " "; + } + } + + sqlObject.setLimitRegion(oriSql); + + return sqlObject; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorMappingBuilderServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorMappingBuilderServiceImpl.java new file mode 100644 index 000000000..7f53dfbbb --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorMappingBuilderServiceImpl.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperatorMappingBuilderServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorMappingBuilderServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author songlei + * @date 2022/8/25 10:08 + */ +@Component("mapping") +public class OperatorMappingBuilderServiceImpl extends BaseBuilderServiceImpl { + + @Override + public ModelingDataFlowSqlObject getOperatorSql(JSONObject params, ModelingDataFlowSqlObject sqlObject) { + // mapping region modify + JSONArray mappings = params.getJSONArray("mappings"); + List sqlMapping = new ArrayList<>(); + + for (int i = 0; i < mappings.size(); i++) { + JSONObject mappingItem = mappings.getJSONObject(i); + String field = mappingItem.getString("field"); + String value = mappingItem.getString("value"); + if (field != null && field.length()!=0 && value !=null && value.trim().length() != 0) { + sqlMapping.add(field + " as " + value.trim()); + } + } + + sqlObject.setMappingRegion(sqlMapping); + + return sqlObject; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorPolymerizationBuilderServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorPolymerizationBuilderServiceImpl.java new file mode 100644 index 000000000..403145da9 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorPolymerizationBuilderServiceImpl.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperatorPolymerizationBuilderServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorPolymerizationBuilderServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author songlei + * @date 2022/8/15 16:35 + */ +@Component("polymerization") +public class OperatorPolymerizationBuilderServiceImpl extends BaseBuilderServiceImpl { + + @Override + public ModelingDataFlowSqlObject getOperatorSql(JSONObject params, ModelingDataFlowSqlObject sqlObject){ + // Polymerization region modify + String oriSql = sqlObject.getPolymerizationRegion(); + + JSONArray polymerizations = params.getJSONArray("polymerization"); + List sqlPolymerization = new ArrayList<>(); + + if (!polymerizations.isEmpty()) { + for (int i = 0; i < polymerizations.size(); i++) { + JSONObject polymerizationItem = polymerizations.getJSONObject(i); + String way = polymerizationItem.getString("way"); + String field = polymerizationItem.getString("field"); + String alias = polymerizationItem.getString("alias"); + if (way != null && field !=null) { + String polymerField = polymerizationConvert(way, field); + sqlPolymerization.add(polymerField + " as " + alias); + //add field to group by region + if ("year".equals(way) || "month".equals(way) || "day".equals(way)) { + String oldGroupBy = sqlObject.getGroupRegion(); + if (oldGroupBy == null || oldGroupBy.isEmpty()) { + sqlObject.setGroupRegion("group by " + polymerField + " "); + } else { + sqlObject.setGroupRegion(oldGroupBy + "," + polymerField + " "); + } + } + } + } + + if (sqlPolymerization.size()>0) { + oriSql += String.join(",",sqlPolymerization) + " "; + } + } + + sqlObject.setPolymerizationRegion(oriSql); + + return sqlObject; + } + + private String polymerizationConvert(String way,String field) { + String sql; + switch (way) { + case "max": + sql = "COALESCE( max("+field+") , 0)"; + break; + case "min": + sql = "COALESCE( min("+field+") , 0)"; + break; + case "avg": + sql = "COALESCE( avg("+field+") , 0)"; + break; + case "sum": + sql = "COALESCE( sum("+field+") , 0)"; + break; + case "count": + sql = "count("+field+")"; + break; + case "year": + sql = "TO_CHAR( "+field+",'YYYY')"; + break; + case "month": + sql = "TO_CHAR( "+field+",'YYYY-MM')"; + break; + default: + sql = field; + break; + } + + return sql; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorQueryBuilderServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorQueryBuilderServiceImpl.java new file mode 100644 index 000000000..24f2d8256 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorQueryBuilderServiceImpl.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperatorQueryBuilderServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorQueryBuilderServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + */ +@Component("query") +public class OperatorQueryBuilderServiceImpl extends BaseBuilderServiceImpl { + + @Override + public ModelingDataFlowSqlObject getOperatorSql(JSONObject params , ModelingDataFlowSqlObject sqlObject) { + + String oriSql = sqlObject.getSelectRegion(); + + JSONArray fields = params.getJSONArray("fields"); + List sqlFields = new ArrayList<>(); + + if (fields.isEmpty()) { + oriSql = "select * "; + } else { + for (int i = 0; i < fields.size(); i++) { + sqlFields.add(fields.getJSONObject(i).getString("value")); + } + oriSql += String.join(",",sqlFields) + " "; + } + + sqlObject.setSelectRegion(oriSql); + + oriSql = sqlObject.getFromRegion(); + + String table = params.getString("table"); + oriSql = "from "+table+" "; + + sqlObject.setFromRegion(oriSql); + + return sqlObject; + } + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorSortBuilderServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorSortBuilderServiceImpl.java new file mode 100644 index 000000000..03f4dc952 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorSortBuilderServiceImpl.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperatorSortBuilderServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorSortBuilderServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * @author songlei + * @date 2022/8/15 16:35 + */ +@Component("sort") +public class OperatorSortBuilderServiceImpl extends BaseBuilderServiceImpl { + + @Override + public ModelingDataFlowSqlObject getOperatorSql(JSONObject params, ModelingDataFlowSqlObject sqlObject){ + String oriSql = sqlObject.getOrderByRegion(); + + JSONArray sorts = params.getJSONArray("sorts"); + List sqlSorts = new ArrayList<>(); + + if (!sorts.isEmpty()) { + for (int i = 0; i < sorts.size(); i++) { + JSONObject sortItem = sorts.getJSONObject(i); + String sortField = sortItem.getString("field"); + //if is an alias name + if (sortField.contains("[as]")) { + sortField = pureAliasField(sortField); + } + String sortValue = sortItem.getString("value"); + if (sortField != null && sortValue !=null) { + sqlSorts.add(sortConvert(sortField,sortValue)); + } + } + if (sqlSorts.size()>0) { + oriSql = "order by "; + oriSql += String.join(",",sqlSorts) + " "; + } + } + + sqlObject.setOrderByRegion(oriSql); + + return sqlObject; + } + + private static final HashMap SORT_CONVERT = new HashMap(){{ + this.put("asc","asc"); + this.put("des","desc"); + }}; + + private String sortConvert(String field,String value) + { + switch (value) { + case "phoneticize": + return " NLSSORT ("+field+",'nls_sort=schinese_pinying_m')"; + default: + return field + " " + SORT_CONVERT.get(value); + } + + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorUpdateBuilderServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorUpdateBuilderServiceImpl.java new file mode 100644 index 000000000..84aa7d918 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorUpdateBuilderServiceImpl.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OperatorUpdateBuilderServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/operator/impl/OperatorUpdateBuilderServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.operator.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author songlei + * @date 2022/8/23 10:06 + */ +@Component("update") +public class OperatorUpdateBuilderServiceImpl extends BaseBuilderServiceImpl { + + @Override + public ModelingDataFlowSqlObject getOperatorSql(JSONObject params , ModelingDataFlowSqlObject sqlObject) { + // update region modify + String oriSql = sqlObject.getUpdateRegion(); + String tableName = params.getString("table"); + oriSql += tableName+" "; + sqlObject.setUpdateRegion(oriSql); + // update region modify + oriSql = sqlObject.getUpdateFieldsRegion(); + JSONArray fields = params.getJSONArray("list"); + List sqlFields = new ArrayList<>(); + + for (int i = 0; i < fields.size(); i++) { + JSONObject fieldItem = fields.getJSONObject(i); + String field = fieldItem.getString("field"); + String fieldType = fieldItem.getString("fieldType"); + String value = fieldItem.getString("value"); + String valueType = fieldItem.getString("valueType"); + String splitValue = fieldItem.getString("splitValue"); + if (field != null) { + if ("customSql".equals(valueType)) { + value = "'"+value+"'"; + } + if ("concat".equals(fieldType) && splitValue != null) { + value = "concat("+value+","+splitValue+")"; + } + sqlFields.add(field + " = " + value); + } + } + + if (sqlFields.size()>0) { + oriSql += String.join(",", sqlFields) + " "; + } + + sqlObject.setUpdateFieldsRegion(oriSql); + + return sqlObject; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationChartGenerateService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationChartGenerateService.java new file mode 100644 index 000000000..f7bd565d0 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationChartGenerateService.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IModelingVisualizationChartGenerateService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationChartGenerateService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization; + +import com.alibaba.fastjson.JSONObject; + +import java.sql.SQLException; + +/** +* @author LZW +*/ +public interface IModelingVisualizationChartGenerateService { + + void setFullParams(JSONObject fullParams); + String generate() throws SQLException, ClassNotFoundException; + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationCustomDimensionsService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationCustomDimensionsService.java new file mode 100644 index 000000000..e01629982 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationCustomDimensionsService.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IModelingVisualizationCustomDimensionsService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationCustomDimensionsService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationCustomDimensionsEntity; +import org.opengauss.admin.plugin.domain.model.modeling.CustomDimension; + +import java.util.List; + +/** +* @author LZW +* @description modeling_visualization_custom_dimensions +* @createDate 2022-10-18 10:54:56 +*/ +public interface IModelingVisualizationCustomDimensionsService extends IService { + public List getByOperatorId(String operatorId); + public int add(CustomDimension customDimension); + public int deleteByIds(String[] paramIds); + public int update(CustomDimension customDimension); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationParamsService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationParamsService.java new file mode 100644 index 000000000..e81e1ffad --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationParamsService.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IModelingVisualizationParamsService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationParamsService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationParamsEntity; + +import java.util.List; + +/** +* @author LZW +* @description modeling_visualization_params +* @createDate 2022-09-23 13:44:13 +*/ +public interface IModelingVisualizationParamsService extends IService { + + IPage selectList(IPage page, ModelingVisualizationParamsEntity modelingVisualizationParamsEntity); + public List getByOperatorId(String operatorId); + public int add(ModelingVisualizationParamsEntity modelingVisualizationParamsEntity); + public int deleteByIds(String[] paramIds); + public int update(ModelingVisualizationParamsEntity modelingVisualizationParamsEntity); + public String generateChart(JSONObject params) throws Exception; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationReportsService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationReportsService.java new file mode 100644 index 000000000..4ca5dcf1f --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationReportsService.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IModelingVisualizationReportsService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationReportsService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationReportsEntity; + +import java.util.List; + +/** +* @author LZW +* @description modeling_visualization_reports +* @createDate 2022-09-29 13:12:50 +*/ +public interface IModelingVisualizationReportsService extends IService { + + public List getByDataFlowIdAndName(Long dataFlowId,String name); + public int add(ModelingVisualizationReportsEntity modelingVisualizationParams); + public int deleteByIds(String[] reportIds); + public ModelingVisualizationReportsEntity selectById(Integer reportId); + public int update(ModelingVisualizationReportsEntity modelingVisualizationParams); + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationSnapshotsService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationSnapshotsService.java new file mode 100644 index 000000000..e12cf0874 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationSnapshotsService.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IModelingVisualizationSnapshotsService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/IModelingVisualizationSnapshotsService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationSnapshotsEntity; + +import java.util.List; + +/** +* @author LZW +* @description modeling_visualization_snapshots +* @createDate 2022-09-23 13:44:43 +*/ +public interface IModelingVisualizationSnapshotsService extends IService { + IPage selectList(IPage page, ModelingVisualizationSnapshotsEntity modelingVisualizationSnapshotsEntity); + + public List getByDataFlowId(Long dataFlowId); + int insertDataFlow(ModelingVisualizationSnapshotsEntity modelingVisualizationSnapshotsEntity); + int deleteByIds(String[] flowIds); + int updateDataFlow(ModelingVisualizationSnapshotsEntity modelingVisualizationSnapshotsEntity); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/factory/ModelingVisualizationChartFactory.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/factory/ModelingVisualizationChartFactory.java new file mode 100644 index 000000000..6967d4800 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/factory/ModelingVisualizationChartFactory.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationChartFactory.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/factory/ModelingVisualizationChartFactory.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization.factory; + +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.service.modeling.visualization.IModelingVisualizationChartGenerateService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Service +public class ModelingVisualizationChartFactory { + + @Autowired + Map generateServiceMap = new ConcurrentHashMap<>(3); + + public IModelingVisualizationChartGenerateService getGenerateServiceByJsonParams(JSONObject params) throws Exception{ + String component = params.getJSONObject("paramsData").getString("chartType"); + IModelingVisualizationChartGenerateService generator = getGenerateService(component); + if (component == null || generator == null) { + throw new RuntimeException("chart type not found!"); + } + generator.setFullParams(params); + return generator; + } + + public IModelingVisualizationChartGenerateService getGenerateService(String component) throws Exception{ + return generateServiceMap.get(component); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationCustomDimensionsServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationCustomDimensionsServiceImpl.java new file mode 100644 index 000000000..cbcc52df2 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationCustomDimensionsServiceImpl.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationCustomDimensionsServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationCustomDimensionsServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationCustomDimensionsEntity; +import org.opengauss.admin.plugin.domain.model.modeling.CustomDimension; +import org.opengauss.admin.plugin.mapper.modeling.ModelingVisualizationCustomDimensionsMapper; +import org.opengauss.admin.plugin.service.modeling.visualization.IModelingVisualizationCustomDimensionsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; + +/** +* @author LZW +* @description modeling_visualization_custom_dimensions +* @createDate 2022-10-18 10:54:56 +*/ +@Service +public class ModelingVisualizationCustomDimensionsServiceImpl extends ServiceImpl + implements IModelingVisualizationCustomDimensionsService { + + @Autowired + private ModelingVisualizationCustomDimensionsMapper modelingVisualizationCustomDimensionsMapper; + + @Override + public List getByOperatorId(String operatorId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(ModelingVisualizationCustomDimensionsEntity.class) + .eq(ModelingVisualizationCustomDimensionsEntity::getOperatorId, operatorId) + .orderByAsc(ModelingVisualizationCustomDimensionsEntity::getId); + return list(queryWrapper); + } + + @Override + public int add(CustomDimension customDimension) { + ModelingVisualizationCustomDimensionsEntity modelingVisualizationCustomDimensionsEntity = new ModelingVisualizationCustomDimensionsEntity(); + int row = modelingVisualizationCustomDimensionsMapper.insert(modelingVisualizationCustomDimensionsEntity.from(customDimension)); + return row; + } + + @Override + public int deleteByIds(String[] paramIds) { + int row = modelingVisualizationCustomDimensionsMapper.deleteBatchIds(Arrays.asList(paramIds)); + return row; + } + + @Override + public int update(CustomDimension customDimension) { + ModelingVisualizationCustomDimensionsEntity modelingVisualizationCustomDimensionsEntity = new ModelingVisualizationCustomDimensionsEntity(); + int row = modelingVisualizationCustomDimensionsMapper.updateById(modelingVisualizationCustomDimensionsEntity.from(customDimension)); + return row; + } +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationParamsServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationParamsServiceImpl.java new file mode 100644 index 000000000..abde4e7ce --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationParamsServiceImpl.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationParamsServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationParamsServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization.impl; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowEntity; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationParamsEntity; +import org.opengauss.admin.plugin.mapper.modeling.ModelingVisualizationParamsMapper; +import org.opengauss.admin.plugin.service.modeling.IModelingDataFlowService; +import org.opengauss.admin.plugin.service.modeling.visualization.IModelingVisualizationChartGenerateService; +import org.opengauss.admin.plugin.service.modeling.visualization.IModelingVisualizationParamsService; +import org.opengauss.admin.plugin.service.modeling.visualization.factory.ModelingVisualizationChartFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** +* @author LZW +* @description modeling_visualization_params +* @createDate 2022-09-23 13:44:13 +*/ +@Service +public class ModelingVisualizationParamsServiceImpl extends ServiceImpl + implements IModelingVisualizationParamsService { + + @Autowired + private ModelingVisualizationParamsMapper modelingVisualizationParamsMapper; + @Autowired + private IModelingDataFlowService modelingDataFlowService; + @Autowired + ModelingVisualizationChartFactory modelingVisualizationChartFactory; + + @Override + public IPage selectList(IPage page, ModelingVisualizationParamsEntity modelingVisualizationParamsEntity) { + return null; + } + + @Override + public List getByOperatorId(String operatorId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(ModelingVisualizationParamsEntity.class) + .eq(ModelingVisualizationParamsEntity::getOperatorId, operatorId) + .orderByAsc(ModelingVisualizationParamsEntity::getId); + return list(queryWrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int add(ModelingVisualizationParamsEntity modelingVisualizationParamsEntity) { + Long dataFlowId = modelingVisualizationParamsEntity.getDataFlowId(); + ModelingDataFlowEntity modelingDataFlowEntity = modelingDataFlowService.getById(dataFlowId); + if (Objects.isNull(modelingDataFlowEntity)) { + throw new RuntimeException("dataflow not found"); + } + + int row = modelingVisualizationParamsMapper.insert(modelingVisualizationParamsEntity); + return row; + } + + @Override + public int deleteByIds(String[] paramIds){ + int row = modelingVisualizationParamsMapper.deleteBatchIds(Arrays.asList(paramIds)); + return row; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int update(ModelingVisualizationParamsEntity modelingVisualizationParamsEntity) { + Long dataFlowId = modelingVisualizationParamsEntity.getDataFlowId(); + ModelingDataFlowEntity modelingDataFlowEntity = modelingDataFlowService.getById(dataFlowId); + if (Objects.isNull(modelingDataFlowEntity)) { + throw new RuntimeException("dataflow not found"); + } + int row = modelingVisualizationParamsMapper.updateById(modelingVisualizationParamsEntity); + return row; + } + + @Override + public String generateChart(JSONObject params) throws Exception { + IModelingVisualizationChartGenerateService generator = modelingVisualizationChartFactory.getGenerateServiceByJsonParams(params); + return generator.generate(); + } +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationReportsServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationReportsServiceImpl.java new file mode 100644 index 000000000..d49d71605 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationReportsServiceImpl.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationReportsServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationReportsServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowEntity; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationReportsEntity; +import org.opengauss.admin.plugin.mapper.modeling.ModelingVisualizationReportsMapper; +import org.opengauss.admin.plugin.service.modeling.IModelingDataFlowService; +import org.opengauss.admin.plugin.service.modeling.visualization.IModelingVisualizationReportsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** +* @author LZW +* @description modeling_visualization_reports +* @createDate 2022-09-29 13:09:24 +*/ +@Service +public class ModelingVisualizationReportsServiceImpl extends ServiceImpl + implements IModelingVisualizationReportsService { + + @Autowired + private ModelingVisualizationReportsMapper modelingVisualizationReportsMapper; + @Autowired + private IModelingDataFlowService modelingDataFlowService; + + @Override + public List getByDataFlowIdAndName(Long dataFlowId, String name) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(ModelingVisualizationReportsEntity.class) + .eq(ModelingVisualizationReportsEntity::getDataFlowId, dataFlowId) + .orderByAsc(ModelingVisualizationReportsEntity::getId); + if (name != null && !name.isEmpty()) { + queryWrapper.like(ModelingVisualizationReportsEntity::getName, name); + } + return list(queryWrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int add(ModelingVisualizationReportsEntity modelingVisualizationReports) { + Long dataFlowId = modelingVisualizationReports.getDataFlowId(); + ModelingDataFlowEntity modelingDataFlowEntity = modelingDataFlowService.getById(dataFlowId); + if (Objects.isNull(modelingDataFlowEntity)) { + throw new RuntimeException("dataflow not found!"); + } + + int row = modelingVisualizationReportsMapper.insert(modelingVisualizationReports); + return row; + } + + @Override + public int deleteByIds(String[] reportIds) { + return modelingVisualizationReportsMapper.deleteBatchIds(Arrays.asList(reportIds)); + } + + @Override + public ModelingVisualizationReportsEntity selectById(Integer reportId) { + return modelingVisualizationReportsMapper.selectById(reportId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int update(ModelingVisualizationReportsEntity modelingVisualizationReports) { + Long dataFlowId = modelingVisualizationReports.getDataFlowId(); + ModelingDataFlowEntity modelingDataFlowEntity = modelingDataFlowService.getById(dataFlowId); + if (Objects.isNull(modelingDataFlowEntity)) { + throw new RuntimeException("dataflow not found"); + } + + int row = modelingVisualizationReportsMapper.updateById(modelingVisualizationReports); + return row; + } +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationSnapshotsServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationSnapshotsServiceImpl.java new file mode 100644 index 000000000..dc81c1278 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationSnapshotsServiceImpl.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ModelingVisualizationSnapshotsServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/ModelingVisualizationSnapshotsServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationSnapshotsEntity; +import org.opengauss.admin.plugin.mapper.modeling.ModelingVisualizationSnapshotsMapper; +import org.opengauss.admin.plugin.service.modeling.visualization.IModelingVisualizationSnapshotsService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; + +/** +* @author LZW +* @description modeling_visualization_snapshots +* @createDate 2022-09-23 13:44:43 +*/ +@Service +public class ModelingVisualizationSnapshotsServiceImpl extends ServiceImpl + implements IModelingVisualizationSnapshotsService { + + @Autowired + private ModelingVisualizationSnapshotsMapper modelingVisualizationSnapshotsMapper; + + @Override + public IPage selectList(IPage page, ModelingVisualizationSnapshotsEntity modelingVisualizationSnapshotsEntity) { + LambdaQueryWrapper queryWrapper = new QueryWrapper().lambda(); + queryWrapper.eq(StringUtils.isNotEmpty(modelingVisualizationSnapshotsEntity.getId()), ModelingVisualizationSnapshotsEntity::getId, modelingVisualizationSnapshotsEntity.getId()); + queryWrapper.like(StringUtils.isNotEmpty(modelingVisualizationSnapshotsEntity.getName()), ModelingVisualizationSnapshotsEntity::getName, modelingVisualizationSnapshotsEntity.getName()); + + return modelingVisualizationSnapshotsMapper.selectPage(page, queryWrapper); + } + + @Override + public List getByDataFlowId(Long dataFlowId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(ModelingVisualizationSnapshotsEntity.class) + .eq(ModelingVisualizationSnapshotsEntity::getDataFlowId, dataFlowId); + return list(queryWrapper); + } + + @Override + public int insertDataFlow(ModelingVisualizationSnapshotsEntity modelingVisualizationSnapshotsEntity) { + int row = modelingVisualizationSnapshotsMapper.insert(modelingVisualizationSnapshotsEntity); + return row; + } + + @Override + public int deleteByIds(String[] flowIds){ + int row = modelingVisualizationSnapshotsMapper.deleteBatchIds(Arrays.asList(flowIds)); + return row; + } + + @Override + public int updateDataFlow(ModelingVisualizationSnapshotsEntity modelingVisualizationSnapshotsEntity) { + int row = modelingVisualizationSnapshotsMapper.updateById(modelingVisualizationSnapshotsEntity); + return row; + } +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/BarChartGenerateServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/BarChartGenerateServiceImpl.java new file mode 100644 index 000000000..361465f97 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/BarChartGenerateServiceImpl.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BarChartGenerateServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/BarChartGenerateServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization.impl.generator; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationCustomDimensionsEntity; + +import org.opengauss.admin.plugin.domain.model.modeling.echart.BarChartBody; +import org.opengauss.admin.plugin.domain.model.modeling.echart.constructor.BarSeriesConstructor; +import org.opengauss.admin.plugin.vo.modeling.BarChartParamsBody; +import org.opengauss.admin.plugin.vo.modeling.*; +import org.opengauss.admin.plugin.vo.modeling.component.*; +import org.opengauss.admin.plugin.vo.modeling.Categories; +import org.opengauss.admin.plugin.vo.modeling.Dimension; +import org.opengauss.admin.plugin.vo.modeling.component.*; +import org.springframework.stereotype.Component; + +import java.sql.SQLException; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Component("bar") +public class BarChartGenerateServiceImpl extends BaseGenerateServiceImpl { + + private BarChartParamsBody barChartParamsBody; + private BarSeriesConstructor barSeriesConstructor; + + @Override + public String generate() throws SQLException, ClassNotFoundException { + parseParams(); + barChartParamsBody = JSON.parseObject(JSON.toJSONString(visualizationParams), BarChartParamsBody.class); + + BarChartBody barChartBody = new BarChartBody(); + barChartBody.setTitle(new Title().setText(barChartParamsBody.getTitle())); + barChartBody.setTooltip(new Tooltip().setTrigger("axis")); + barChartBody.setGrid(new Grid().setBottom("40").setRight("3%").setLeft("3%").setContainLabel(true)); + + //set dataZoom + ArrayList dataZoom = new ArrayList<>(); + dataZoom.add(new DataZoomItem().setStart(0).setEnd(10).setType("inside")); + dataZoom.add(new DataZoomItem().setStart(0).setEnd(10)); + barChartBody.setDataZoom(dataZoom); + + List allKeys = new ArrayList<>(queryResult.get(0).keySet()); + if (!allKeys.contains(barChartParamsBody.getShowType().getDimension())) { + throw new RuntimeException("The field in the parameter is not found in the query result, please check whether the query table or condition has been replaced."); + } + //generate X axis config + Dimension xDimension; + if (barChartParamsBody.getShowType().getKey() == 1) { + xDimension = genXAxis(); + } else { + xDimension = genXAxisByTime(); + } + + barChartBody.setXAxis(new XAxis().setType("category").setBoundaryGap(true).setData(xDimension.getCategoryName())); + + //generate series data + genSeries(xDimension); + + List series = new ArrayList<>(); + Map> seriesData = barSeriesConstructor.getSeriesData(); + for(String key : seriesData.keySet()){ + List value = seriesData.get(key); + //add type and yAxis index + Series s = new Series().setType("bar").setData(value).setYAxisIndex(0); + if (barChartParamsBody.getDimension().size() > 0) { + s.setName(key); + } + if (fullParams.getJSONObject("paramsData").getInteger("showNumber") == 1) { + s.setLabel(new Label().setShow(true)); + } + series.add(s); + } + + barChartBody.setSeries(series); + + barChartBody.setLegend(new Legend("scroll").setData(barSeriesConstructor.getSeriesKey()).setTop(10).setWidth("70%")); + + //set y unit + barChartBody.setYAxis(new YAxis().setType("value").setAxisLabel(new AxisLabel(barSeriesConstructor.getYUnit()))); + + return JSONObject.toJSONString(barChartBody); + } + + public Dimension genXAxis(){ + Dimension xDimension = new Dimension(); + //get display dimension of X Axis + String xDimensionKey = barChartParamsBody.getShowType().getDimension(); + //if dimension is from custom + if (xDimensionKey.contains("custom|")) { + //get custom dimension record in database + String xDimensionId = xDimensionKey.replace("custom|",""); + ModelingVisualizationCustomDimensionsEntity cd = modelingVisualizationCustomDimensionsService.getById(xDimensionId); + xDimension.setField(cd.getField()); + //enum value by field + JSONArray categoriesJson = JSONArray.parseArray(cd.getCategoriesJson()); + xDimension.setNum(categoriesJson.size()); + categoriesJson.forEach(categoryJson->{ + Categories category = JSON.parseObject(JSON.toJSONString(categoryJson), Categories.class); + xDimension.addCategoryBody(category.toBody(cd.getField())); + }); + } else { + xDimension.setField(xDimensionKey); + //enum value by field + queryResult.forEach(item->{ + String filedValue = item.get(xDimensionKey) == null ? "null" : item.get(xDimensionKey).toString(); + if (filedValue != null) { + Categories category = new Categories(); + category.setName(filedValue); + category.setValue(List.of(filedValue)); + xDimension.addCategoryBody(category.toBody(xDimensionKey)); + } + }); + } + + return xDimension; + } + + public Dimension genXAxisByTime(){ + Dimension xDimension = new Dimension(); + //get display dimension of X Axis + String xDimensionKey = barChartParamsBody.getShowType().getDateField(); + Integer particle = barChartParamsBody.getShowType().getParticle(); + + xDimension.setField(xDimensionKey); + //enum value by field + queryResult.forEach(item->{ + String filedValue = null; + if (item.get(xDimensionKey) != null && item.get(xDimensionKey).getClass() == Timestamp.class) { + filedValue = formatDateTimeDimension((Timestamp)item.get(xDimensionKey),particle); + item.put(xDimensionKey,filedValue); + } + if (filedValue != null) { + Categories category = new Categories(); + category.setName(filedValue); + category.setValue(List.of(filedValue)); + xDimension.addCategoryBody(category.toBody(xDimensionKey)); + } + }); + + return xDimension; + } + + public void genSeries(Dimension xDimension){ + List dimensions = barChartParamsBody.getDimension(); + dimensions.forEach(dim->{ + //prepare dimensions + if (dim.getField().contains("custom|")) + { + //get custom dimension record in database + String customId = dim.getField().replace("custom|",""); + ModelingVisualizationCustomDimensionsEntity cd = modelingVisualizationCustomDimensionsService.getById(customId); + dim.setField(cd.getField()); + dim.setCustomFlag(true); + //enum value by field + JSONArray categoriesJson = JSONArray.parseArray(cd.getCategoriesJson()); + categoriesJson.forEach(categoryJson->{ + Categories category = JSON.parseObject(JSON.toJSONString(categoryJson), Categories.class); + dim.addCategoryBody(category.toBody(cd.getField())); + }); + dim.setNum(categoriesJson.size()); + } + }); + + barSeriesConstructor = new BarSeriesConstructor(xDimension, + barChartParamsBody.getIndicator(), + barChartParamsBody.getDimension(), + queryResult); + + barSeriesConstructor.getDimensionValue(); + barSeriesConstructor.getDescartes(); + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/BaseGenerateServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/BaseGenerateServiceImpl.java new file mode 100644 index 000000000..fb6dc7ca8 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/BaseGenerateServiceImpl.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BaseGenerateServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/BaseGenerateServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization.impl.generator; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingDataFlowEntity; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationCustomDimensionsEntity; +import org.opengauss.admin.plugin.domain.model.modeling.ModelingDataFlowSqlObject; +import org.opengauss.admin.plugin.service.modeling.IModelingDataBaseService; +import org.opengauss.admin.plugin.service.modeling.IModelingDataFlowService; +import org.opengauss.admin.plugin.service.modeling.operator.IModelingDataFlowProcessService; +import org.opengauss.admin.plugin.service.modeling.visualization.IModelingVisualizationChartGenerateService; +import org.opengauss.admin.plugin.service.modeling.visualization.IModelingVisualizationCustomDimensionsService; +import org.opengauss.admin.plugin.vo.modeling.Categories; +import org.opengauss.admin.plugin.vo.modeling.Dimension; +import org.springframework.beans.factory.annotation.Autowired; + +import java.sql.SQLException; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class BaseGenerateServiceImpl implements IModelingVisualizationChartGenerateService { + + @Autowired + private IModelingDataFlowProcessService modelingDataFlowProcessService; + @Autowired + private IModelingDataFlowService modelingDataFlowService; + @Autowired + private IModelingDataBaseService modelingDataBaseService; + @Autowired + public IModelingVisualizationCustomDimensionsService modelingVisualizationCustomDimensionsService; + + + public JSONObject fullParams; + public JSONObject visualizationParams; + + public List> queryResult; + + @Override + public void setFullParams(JSONObject fullParams) { this.fullParams = fullParams;} + + @Override + public String generate() throws SQLException, ClassNotFoundException { + return null; + } + + public void parseParams() throws SQLException, ClassNotFoundException { + JSONObject dataFlowContent = fullParams.getJSONObject("operatorsData"); + String dataFlowId = fullParams.getString("dataFlowId"); + visualizationParams = fullParams.getJSONObject("paramsData"); + + ModelingDataFlowEntity modelingDataFlowEntity = modelingDataFlowService.getById(fullParams.getString("dataFlowId")); + + if (dataFlowContent == null || modelingDataFlowEntity == null) + { + throw new RuntimeException("operators info is empty!"); + } + + ModelingDataFlowSqlObject sqlObject = modelingDataFlowProcessService.getSqlObjectByTargetOperatorId(dataFlowContent, visualizationParams.getString("operatorId"),dataFlowId); + + if (sqlObject == null) + { + throw new RuntimeException("build sql object failed!"); + } + + sqlObject.initDataBase(sqlObject.getExecuteClusterNode(), modelingDataFlowEntity.getSchema()); + queryResult = modelingDataBaseService.queryWithSqlObject(sqlObject); + } + + + public Dimension formatDimension(Dimension dimension) { + //if dimension is from custom + if (dimension.getField().contains("custom|")) + { + //get custom dimension record in database + String customId = dimension.getField().replace("custom|",""); + ModelingVisualizationCustomDimensionsEntity cd = modelingVisualizationCustomDimensionsService.getById(customId); + dimension.setField(cd.getField()); + dimension.setCustomFlag(true); + //enum value by field + JSONArray categoriesJson = JSONArray.parseArray(cd.getCategoriesJson()); + dimension.setNum(categoriesJson.size()); + categoriesJson.forEach(categoryJson->{ + Categories category = JSON.parseObject(JSON.toJSONString(categoryJson), Categories.class); + dimension.addCategoryBody(category.toBody(cd.getField())); + }); + } else { + //enum value by field + queryResult.forEach(item->{ + String filedValue = (String) item.get(dimension.getField()); + if (filedValue != null) { + Categories category = new Categories(); + category.setName(filedValue); + category.setValue(List.of(filedValue)); + category.setType(1); + dimension.addCategoryBody(category.toBody(dimension.getField())); + } + }); + } + + return dimension; + } + + public String formatDateTimeDimension(Timestamp value , Integer particle) { + if (value == null) { + return null; + } + //default format yyyy-mm-dd , ignore min,sec + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH"); + switch (particle) { + case 1: { + sdf = new SimpleDateFormat("yyyy年"); + break; + } + case 2: { + sdf = new SimpleDateFormat("yyyy年MM月"); + break; + } + case 3: { + sdf = new SimpleDateFormat("yyyy年MM月dd日"); + break; + } + case 4: { + sdf = new SimpleDateFormat("HH时"); + break; + } + default:{ + break; + } + } + return sdf.format(new Date(value.getTime())); + } +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/GraphChartGenerateServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/GraphChartGenerateServiceImpl.java new file mode 100644 index 000000000..f19ad86fe --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/GraphChartGenerateServiceImpl.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * GraphChartGenerateServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/GraphChartGenerateServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization.impl.generator; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.echart.*; +import org.opengauss.admin.plugin.domain.model.modeling.echart.constructor.GraphSeriesConstructor; +import org.opengauss.admin.plugin.vo.modeling.*; +import org.opengauss.admin.plugin.vo.modeling.component.*; +import org.opengauss.admin.plugin.domain.model.modeling.echart.GraphChartBody; +import org.opengauss.admin.plugin.vo.modeling.GraphChartParamsBody; +import org.opengauss.admin.plugin.vo.modeling.component.GraphSeries; +import org.opengauss.admin.plugin.vo.modeling.component.Title; +import org.opengauss.admin.plugin.vo.modeling.component.Tooltip; +import org.springframework.stereotype.Component; + +import java.sql.SQLException; +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Component("graph") +public class GraphChartGenerateServiceImpl extends BaseGenerateServiceImpl { + + private GraphChartParamsBody graphChartParamsBody; + + @Override + public String generate() throws SQLException, ClassNotFoundException { + parseParams(); + graphChartParamsBody = JSON.parseObject(JSON.toJSONString(visualizationParams), GraphChartParamsBody.class); + + GraphChartBody graphChartBody = new GraphChartBody(); + graphChartBody.setTitle(new Title().setText(graphChartParamsBody.getTitle())); + graphChartBody.setTooltip(new Tooltip().setTrigger("axis")); + List series = genSeries(); + graphChartBody.setSeries(series); + return JSONObject.toJSONString(graphChartBody); + } + + public List genSeries(){ + GraphSeriesConstructor graphSeriesConstructor = new GraphSeriesConstructor(graphChartParamsBody.getIndicator(), graphChartParamsBody.getNode(), graphChartParamsBody.getRelation(), queryResult); + return graphSeriesConstructor.getSeriesData(); + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/HeatmapChartGenerateServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/HeatmapChartGenerateServiceImpl.java new file mode 100644 index 000000000..7acd80b31 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/HeatmapChartGenerateServiceImpl.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HeatmapChartGenerateServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/HeatmapChartGenerateServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization.impl.generator; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.model.modeling.echart.HeatmapChartBody; +import org.opengauss.admin.plugin.domain.model.modeling.echart.constructor.HeatmapSeriesConstructor; +import org.opengauss.admin.plugin.vo.modeling.component.*; +import org.opengauss.admin.plugin.vo.modeling.Dimension; +import org.opengauss.admin.plugin.vo.modeling.HeatmapChartParamsBody; +import org.opengauss.admin.plugin.vo.modeling.component.*; +import org.springframework.stereotype.Component; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Component("heatmap") +public class HeatmapChartGenerateServiceImpl extends BaseGenerateServiceImpl { + + private HeatmapChartParamsBody heatmapChartParamsBody; + + @Override + public String generate() throws SQLException, ClassNotFoundException { + parseParams(); + heatmapChartParamsBody = JSON.parseObject(JSON.toJSONString(visualizationParams), HeatmapChartParamsBody.class); + + //prepare dimension data + Dimension xDimension = formatDimension(heatmapChartParamsBody.getX()); + Dimension yDimension = formatDimension(heatmapChartParamsBody.getY()); + + List allKeys = new ArrayList<>(queryResult.get(0).keySet()); + if (!allKeys.contains(heatmapChartParamsBody.getX().getField())) { + throw new RuntimeException("The field in the parameter is not found in the query result, please check whether the query table or condition has been replaced."); + } + + HeatmapChartBody heatmapChartBody = new HeatmapChartBody(); + heatmapChartBody.setTitle(new Title().setText(heatmapChartParamsBody.getTitle())); + heatmapChartBody.setTooltip(new Tooltip().setTrigger("axis")); + heatmapChartBody.setXAxis(new XAxis().setBoundaryGap(true).setData(xDimension.getCategoryName()).setType("category").setSplitArea(new SplitArea().setShow(true))); + heatmapChartBody.setYAxis(new XAxis().setBoundaryGap(true).setData(yDimension.getCategoryName()).setType("category").setSplitArea(new SplitArea().setShow(true))); + int heatMin = heatmapChartParamsBody.getRange().stream() + .findFirst().orElse(0); + int heatMax = heatmapChartParamsBody.getRange().stream() + .skip(1).findFirst().orElse(100); + heatmapChartBody.setVisualMap(new VisualMap().setBottom("5").setCalculable(true).setMin(heatMin).setMax(heatMax).setLeft("center").setOrient("horizontal")); + heatmapChartBody.setSeries(genSeries(xDimension,yDimension)); + + return JSONObject.toJSONString(heatmapChartBody); + } + + public List genSeries(Dimension xDimension, Dimension yDimension){ + HeatmapSeriesConstructor heatmapSeriesConstructor = new HeatmapSeriesConstructor(heatmapChartParamsBody.getIndicator(), xDimension, yDimension, queryResult); + return heatmapSeriesConstructor.getSeriesData(); + } + + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/LineChartGenerateServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/LineChartGenerateServiceImpl.java new file mode 100644 index 000000000..7b9c8b669 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/LineChartGenerateServiceImpl.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * LineChartGenerateServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/LineChartGenerateServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization.impl.generator; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationCustomDimensionsEntity; +import org.opengauss.admin.plugin.domain.model.modeling.echart.LineChartBody; +import org.opengauss.admin.plugin.domain.model.modeling.echart.constructor.LineSeriesConstructor; +import org.opengauss.admin.plugin.vo.modeling.Categories; +import org.opengauss.admin.plugin.vo.modeling.Dimension; +import org.opengauss.admin.plugin.vo.modeling.LineChartParamsBody; +import org.opengauss.admin.plugin.vo.modeling.component.*; +import org.opengauss.admin.plugin.vo.modeling.component.*; +import org.springframework.stereotype.Component; + +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Component("line") +public class LineChartGenerateServiceImpl extends BaseGenerateServiceImpl { + + private LineChartParamsBody lineChartParamsBody; + private LineSeriesConstructor lineSeriesConstructor; + + @Override + public String generate() throws SQLException, ClassNotFoundException { + parseParams(); + lineChartParamsBody = JSON.parseObject(JSON.toJSONString(visualizationParams), LineChartParamsBody.class); + + LineChartBody lineChartBody = new LineChartBody(); + lineChartBody.setTitle(new Title().setText(lineChartParamsBody.getTitle())); + lineChartBody.setTooltip(new Tooltip().setTrigger("axis")); + lineChartBody.setGrid(new Grid().setBottom("40").setRight("3%").setLeft("3%").setContainLabel(true)); + + //set dataZoom + ArrayList dataZoom = new ArrayList<>(); + dataZoom.add(new DataZoomItem().setStart(0).setEnd(10).setType("inside")); + dataZoom.add(new DataZoomItem().setStart(0).setEnd(10)); + lineChartBody.setDataZoom(dataZoom); + + List allKeys = new ArrayList<>(queryResult.get(0).keySet()); + if (!allKeys.contains(lineChartParamsBody.getShowType().getDimension())) { + throw new RuntimeException("The field in the parameter is not found in the query result, please check whether the query table or condition has been replaced."); + } + + //generate X axis config + Dimension xDimension; + if (lineChartParamsBody.getShowType().getKey() == 1) { + xDimension = genXAxis(); + } else { + xDimension = genXAxisByTime(); + } + + lineChartBody.setXAxis(new XAxis().setType("category").setBoundaryGap(true).setData(xDimension.getCategoryName())); + + //generate series data + genSeries(xDimension); + + List series = new ArrayList<>(); + Map> seriesData = lineSeriesConstructor.getSeriesData(); + float minValue = Float.MAX_VALUE; + + for(String key : seriesData.keySet()){ + List value = seriesData.get(key); + //add y index for each series + Series s = new Series().setType("line").setData(value).setYAxisIndex(0); + if (lineChartParamsBody.getDimension().size() > 0) { + s.setName(key); + } + series.add(s); + + if (fullParams.getJSONObject("paramsData").getInteger("showNumber") == 1) { + s.setLabel(new Label().setShow(true)); + } + + //get min value for y axis + float currentMinValue = Collections.min(value); + if (currentMinValue < minValue) { + minValue = currentMinValue; + } + } + + if (minValue > 0 && minValue < Float.MAX_VALUE) + { + minValue = Math.round(minValue * 0.8f); + } else { + minValue = 0; + } + + lineChartBody.setSeries(series); + + lineChartBody.setLegend(new Legend("scroll").setData(lineSeriesConstructor.getSeriesKey()).setTop(10).setWidth("70%")); + + lineChartBody.setYAxis(new YAxis().setMin(minValue).setType("value").setAxisLabel(new AxisLabel(lineSeriesConstructor.getYUnit()))); + + return JSONObject.toJSONString(lineChartBody); + } + + public Dimension genXAxis(){ + Dimension xDimension = new Dimension(); + //get display dimension of X Axis + String xDimensionKey = lineChartParamsBody.getShowType().getDimension(); + //if dimension is from custom + if (xDimensionKey.contains("custom|")) { + //get custom dimension record in database + String xDimensionId = xDimensionKey.replace("custom|",""); + ModelingVisualizationCustomDimensionsEntity cd = modelingVisualizationCustomDimensionsService.getById(xDimensionId); + xDimension.setField(cd.getField()); + //enum value by field + JSONArray categoriesJson = JSONArray.parseArray(cd.getCategoriesJson()); + xDimension.setNum(categoriesJson.size()); + categoriesJson.forEach(categoryJson->{ + Categories category = JSON.parseObject(JSON.toJSONString(categoryJson), Categories.class); + xDimension.addCategoryBody(category.toBody(cd.getField())); + }); + } else { + xDimension.setField(xDimensionKey); + //enum value in query data + queryResult.forEach(item->{ + String filedValue = item.get(xDimensionKey) == null ? "null" : item.get(xDimensionKey).toString(); + if (filedValue != null) { + Categories category = new Categories(); + category.setName(filedValue); + category.setValue(List.of(filedValue)); + xDimension.addCategoryBody(category.toBody(xDimensionKey)); + } + }); + } + + return xDimension; + } + + public Dimension genXAxisByTime(){ + Dimension xDimension = new Dimension(); + //get display dimension of X Axis + String xDimensionKey = lineChartParamsBody.getShowType().getDateField(); + Integer particle = lineChartParamsBody.getShowType().getParticle(); + + xDimension.setField(xDimensionKey); + //enum value by field + queryResult.forEach(item->{ + String filedValue = null; + if (item.get(xDimensionKey) != null && item.get(xDimensionKey).getClass() == Timestamp.class) { + filedValue = formatDateTimeDimension((Timestamp)item.get(xDimensionKey),particle); + item.put(xDimensionKey,filedValue); + } + if (filedValue != null) { + Categories category = new Categories(); + category.setName(filedValue); + category.setValue(List.of(filedValue)); + xDimension.addCategoryBody(category.toBody(xDimensionKey)); + } + }); + + return xDimension; + } + + public void genSeries(Dimension xDimension){ + List dimensions = lineChartParamsBody.getDimension(); + dimensions.forEach(dim->{ + if (dim.getField().contains("custom|")) + { + String customId = dim.getField().replace("custom|",""); + ModelingVisualizationCustomDimensionsEntity cd = modelingVisualizationCustomDimensionsService.getById(customId); + dim.setField(cd.getField()); + dim.setCustomFlag(true); + + JSONArray categoriesJson = JSONArray.parseArray(cd.getCategoriesJson()); + dim.setNum(categoriesJson.size()); + categoriesJson.forEach(categoryJson->{ + Categories category = JSON.parseObject(JSON.toJSONString(categoryJson), Categories.class); + dim.addCategoryBody(category.toBody(cd.getField())); + }); + } + }); + + lineSeriesConstructor = new LineSeriesConstructor(xDimension, + lineChartParamsBody.getIndicator(), + dimensions, + queryResult); + + lineSeriesConstructor.getDimensionValue(); + lineSeriesConstructor.getDescartes(); + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/MixChartGenerateServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/MixChartGenerateServiceImpl.java new file mode 100644 index 000000000..e14dab7fe --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/MixChartGenerateServiceImpl.java @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MixChartGenerateServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/MixChartGenerateServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization.impl.generator; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationCustomDimensionsEntity; +import org.opengauss.admin.plugin.domain.model.modeling.echart.MixChartBody; +import org.opengauss.admin.plugin.domain.model.modeling.echart.constructor.BarSeriesConstructor; +import org.opengauss.admin.plugin.domain.model.modeling.echart.constructor.LineSeriesConstructor; +import org.opengauss.admin.plugin.vo.modeling.Categories; +import org.opengauss.admin.plugin.vo.modeling.Dimension; +import org.opengauss.admin.plugin.vo.modeling.MixChartParamsBody; +import org.opengauss.admin.plugin.vo.modeling.component.*; +import org.springframework.stereotype.Component; + +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Component("mix") +public class MixChartGenerateServiceImpl extends BaseGenerateServiceImpl { + + private MixChartParamsBody mixChartParamsBody; + private List barSeriesConstructors; + private List lineSeriesConstructors; + private LinkedHashSet yUnits; + + @Override + public String generate() throws SQLException, ClassNotFoundException { + parseParams(); + mixChartParamsBody = JSON.parseObject(JSON.toJSONString(visualizationParams), MixChartParamsBody.class); + + MixChartBody mixChartBody = new MixChartBody(); + mixChartBody.setTitle(new Title().setText(mixChartParamsBody.getTitle())); + mixChartBody.setTooltip(new Tooltip().setTrigger("axis")); + mixChartBody.setGrid(new Grid().setBottom("40").setRight("3%").setLeft("3%").setContainLabel(true)); + + //set dataZoom + ArrayList dataZoom = new ArrayList<>(); + dataZoom.add(new DataZoomItem().setStart(0).setEnd(10).setType("inside")); + dataZoom.add(new DataZoomItem().setStart(0).setEnd(10)); + mixChartBody.setDataZoom(dataZoom); + + List allKeys = new ArrayList<>(queryResult.get(0).keySet()); + if (!allKeys.contains(mixChartParamsBody.getShowType().getDimension())) { + throw new RuntimeException("The field in the parameter is not found in the query result, please check whether the query table or condition has been replaced."); + } + //generate X axis config + Dimension xDimension; + if (mixChartParamsBody.getShowType().getKey() == 1) { + xDimension = genXAxis(); + } else { + xDimension = genXAxisByTime(); + } + + mixChartBody.setXAxis(new XAxis().setType("category").setBoundaryGap(true).setData(xDimension.getCategoryName())); + + genSeries(xDimension); + + List series = new ArrayList<>(); + List seriesKeys = new ArrayList<>(); + + ArrayList yUnitsList = new ArrayList<>(yUnits); + + AtomicInteger chartColorIndex = new AtomicInteger(0); + + //process bar chart info + barSeriesConstructors.forEach(barSeriesConstructor -> { + Map> seriesData = barSeriesConstructor.getSeriesData(); + for(String key : seriesData.keySet()){ + List value = seriesData.get(key); + //get index of Y Axis + int yIndex = yUnitsList.indexOf(barSeriesConstructor.getYUnit()); + Series s = new Series(); + if (Objects.equals(key, "Total")) { + chartColorIndex.addAndGet(1); + s.setName(key + "-" + chartColorIndex).setType("bar").setData(value).setYAxisIndex(yIndex); + } else { + chartColorIndex.addAndGet(1); + s.setName(key + "-" + barSeriesConstructor.getIndicators().get(0).getField()).setType("bar").setData(value).setYAxisIndex(yIndex); + } + + if (fullParams.getJSONObject("paramsData").getInteger("showNumber") == 1) { + s.setLabel(new Label().setShow(true)); + } + + series.add(s); + //merge categories + seriesKeys.addAll(barSeriesConstructor.getSeriesKey()); + } + }); + + //process line chart info + lineSeriesConstructors.forEach(lineSeriesConstructor -> { + Map> seriesData = lineSeriesConstructor.getSeriesData(); + for(String key : seriesData.keySet()){ + List value = seriesData.get(key); + //get index of Y Axis + int yIndex = yUnitsList.indexOf(lineSeriesConstructor.getYUnit()); + Series s = new Series(); + if (Objects.equals(key, "Total")) { + chartColorIndex.addAndGet(1); + s.setName(key + "-" + chartColorIndex).setType("line").setData(value).setYAxisIndex(yIndex); + } else { + chartColorIndex.addAndGet(1); + s.setName(key + "-" + lineSeriesConstructor.getIndicators().get(0).getField()).setType("line").setData(value).setYAxisIndex(yIndex); + } + + if (fullParams.getJSONObject("paramsData").getInteger("showNumber") == 1) { + s.setLabel(new Label().setShow(true)); + } + + series.add(s); + //merge categories + seriesKeys.addAll(lineSeriesConstructor.getSeriesKey()); + } + }); + + + mixChartBody.setSeries(series); + + mixChartBody.setLegend(new Legend("scroll").setData(seriesKeys).setTop(10).setWidth("70%")); + + //add Y Axis index , first y Axis at left and others place at right + List yAxes = new ArrayList<>(); + yUnitsList.forEach(yUnit->{ + if (yUnitsList.indexOf(yUnit) == 0) { + yAxes.add(new YAxis().setType("value").setAxisLabel(new AxisLabel(yUnit)).setPosition("left")); + } else { + yAxes.add(new YAxis().setType("value").setAxisLabel(new AxisLabel(yUnit)).setPosition("right")); + } + }); + mixChartBody.setYAxis(yAxes); + + return JSONObject.toJSONString(mixChartBody); + } + + public Dimension genXAxis(){ + Dimension xDimension = new Dimension(); + String xDimensionKey = mixChartParamsBody.getShowType().getDimension(); + if (xDimensionKey.contains("custom|")) { + String xDimensionId = xDimensionKey.replace("custom|",""); + ModelingVisualizationCustomDimensionsEntity cd = modelingVisualizationCustomDimensionsService.getById(xDimensionId); + xDimension.setField(cd.getField()); + JSONArray categoriesJson = JSONArray.parseArray(cd.getCategoriesJson()); + xDimension.setNum(categoriesJson.size()); + categoriesJson.forEach(categoryJson->{ + Categories category = JSON.parseObject(JSON.toJSONString(categoryJson), Categories.class); + xDimension.addCategoryBody(category.toBody(cd.getField())); + }); + } else { + xDimension.setField(xDimensionKey); + queryResult.forEach(item->{ + String filedValue = item.get(xDimensionKey) == null ? "null" : item.get(xDimensionKey).toString(); + if (filedValue != null) { + Categories category = new Categories(); + category.setName(filedValue); + category.setValue(List.of(filedValue)); + xDimension.addCategoryBody(category.toBody(xDimensionKey)); + } + }); + } + + return xDimension; + } + + public Dimension genXAxisByTime(){ + Dimension xDimension = new Dimension(); + //get display dimension of X Axis + String xDimensionKey = mixChartParamsBody.getShowType().getDateField(); + Integer particle = mixChartParamsBody.getShowType().getParticle(); + + xDimension.setField(xDimensionKey); + //enum value by field + queryResult.forEach(item->{ + String filedValue = null; + if (item.get(xDimensionKey) != null && item.get(xDimensionKey).getClass() == Timestamp.class) { + filedValue = formatDateTimeDimension((Timestamp)item.get(xDimensionKey),particle); + item.put(xDimensionKey,filedValue); + } + if (filedValue != null) { + Categories category = new Categories(); + category.setName(filedValue); + category.setValue(List.of(filedValue)); + xDimension.addCategoryBody(category.toBody(xDimensionKey)); + } + }); + + return xDimension; + } + + public void genSeries(Dimension xDimension){ + barSeriesConstructors = new ArrayList<>(); + lineSeriesConstructors = new ArrayList<>(); + yUnits = new LinkedHashSet<>(); + + mixChartParamsBody.getSubCharts().forEach(subCharts -> { + List dimensions = subCharts.getDimension(); + dimensions.forEach(dim->{ + if (dim.getField().contains("custom|")) + { + String customId = dim.getField().replace("custom|",""); + ModelingVisualizationCustomDimensionsEntity cd = modelingVisualizationCustomDimensionsService.getById(customId); + dim.setField(cd.getField()); + dim.setCustomFlag(true); + + JSONArray categoriesJson = JSONArray.parseArray(cd.getCategoriesJson()); + categoriesJson.forEach(categoryJson->{ + Categories category = JSON.parseObject(JSON.toJSONString(categoryJson), Categories.class); + dim.addCategoryBody(category.toBody(cd.getField())); + }); + dim.setNum(categoriesJson.size()); + } + }); + + if (Objects.equals(subCharts.getChartType(), "bar")) { + BarSeriesConstructor barSeriesConstructor = new BarSeriesConstructor(xDimension, + subCharts.getIndicator(), + dimensions, + queryResult); + barSeriesConstructor.getDimensionValue(); + barSeriesConstructor.getDescartes(); + + yUnits.add(barSeriesConstructor.getYUnit()); + barSeriesConstructors.add(barSeriesConstructor); + } + + if (Objects.equals(subCharts.getChartType(), "line")) { + LineSeriesConstructor lineSeriesConstructor = new LineSeriesConstructor(xDimension, + subCharts.getIndicator(), + dimensions, + queryResult); + lineSeriesConstructor.getDimensionValue(); + lineSeriesConstructor.getDescartes(); + + yUnits.add(lineSeriesConstructor.getYUnit()); + lineSeriesConstructors.add(lineSeriesConstructor); + } + }); + + } + +} + + + + diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/PieChartGenerateServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/PieChartGenerateServiceImpl.java new file mode 100644 index 000000000..9e503d41e --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/PieChartGenerateServiceImpl.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * PieChartGenerateServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/PieChartGenerateServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization.impl.generator; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; + +import org.opengauss.admin.plugin.domain.model.modeling.echart.PieChartBody; +import org.opengauss.admin.plugin.domain.model.modeling.echart.constructor.PieSeriesConstructor; +import org.opengauss.admin.plugin.vo.modeling.Dimension; +import org.opengauss.admin.plugin.vo.modeling.PieChartParamsBody; +import org.opengauss.admin.plugin.vo.modeling.component.Legend; +import org.opengauss.admin.plugin.vo.modeling.component.Title; +import org.opengauss.admin.plugin.vo.modeling.component.Tooltip; +import org.springframework.stereotype.Component; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Component("pie") +public class PieChartGenerateServiceImpl extends BaseGenerateServiceImpl { + + private PieChartParamsBody pieChartParamsBody; + private PieSeriesConstructor pieSeriesConstructor; + + @Override + public String generate() throws SQLException, ClassNotFoundException { + parseParams(); + pieChartParamsBody = JSON.parseObject(JSON.toJSONString(visualizationParams), PieChartParamsBody.class); + + PieChartBody pieChartBody = new PieChartBody(); + pieChartBody.setTitle(new Title().setText(pieChartParamsBody.getTitle())); + pieChartBody.setTooltip(new Tooltip().setTrigger("item").setFormatter("{b} {c}")); + + List allKeys = new ArrayList<>(queryResult.get(0).keySet()); + if (!allKeys.contains(pieChartParamsBody.getDimension().getField())) { + throw new RuntimeException("The field in the parameter is not found in the query result, please check whether the query table or condition has been replaced."); + } + + genSeries(); + + pieChartBody.setSeries(pieSeriesConstructor.getSeriesData()); + + pieChartBody.setLegend(new Legend("scroll").setData(pieSeriesConstructor.getSeriesKey()).setBottom(10)); + + return JSONObject.toJSONString(pieChartBody); + } + + public void genSeries(){ + //format dimension data + Dimension dimension = formatDimension(pieChartParamsBody.getDimension()); + Dimension subDimension = null; + //if inner circle is config + if (pieChartParamsBody.getShowType().getField() != null) { + subDimension = new Dimension(); + subDimension.setNum(10); + subDimension.setField(pieChartParamsBody.getShowType().getField()); + subDimension = formatDimension(subDimension); + } + + pieSeriesConstructor = new PieSeriesConstructor( pieChartParamsBody.getIndicator(), dimension, subDimension, queryResult,pieChartParamsBody.getShowType().getPercentage()); + + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/ScatterChartGenerateServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/ScatterChartGenerateServiceImpl.java new file mode 100644 index 000000000..f8f8be6d9 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/ScatterChartGenerateServiceImpl.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ScatterChartGenerateServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/modeling/visualization/impl/generator/ScatterChartGenerateServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.modeling.visualization.impl.generator; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import org.opengauss.admin.plugin.domain.entity.modeling.ModelingVisualizationGeoFilesEntity; +import org.opengauss.admin.plugin.domain.model.modeling.echart.ScatterChartBody; +import org.opengauss.admin.plugin.domain.model.modeling.echart.constructor.ScatterSeriesConstructor; + +import org.opengauss.admin.plugin.service.modeling.IModelingVisualizationGeoFilesService; +import org.opengauss.admin.plugin.utils.GeoUtils; +import org.opengauss.admin.plugin.vo.modeling.ScatterChartParamsBody; +import org.opengauss.admin.plugin.vo.modeling.component.*; +import org.opengauss.admin.plugin.vo.modeling.component.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Component("scatter") +public class ScatterChartGenerateServiceImpl extends BaseGenerateServiceImpl { + + private ScatterChartParamsBody scatterChartParamsBody; + + private ScatterSeriesConstructor scatterSeriesConstructor; + + @Autowired + private IModelingVisualizationGeoFilesService modelingVisualizationGeoFilesService; + + @Override + public String generate() throws SQLException, ClassNotFoundException { + parseParams(); + scatterChartParamsBody = JSON.parseObject(JSON.toJSONString(visualizationParams), ScatterChartParamsBody.class); + + ScatterChartBody scatterChartBody = new ScatterChartBody(); + scatterChartBody.setTitle(new Title().setText(scatterChartParamsBody.getTitle())); + scatterChartBody.setTooltip(new Tooltip().setTrigger("item").setFormatter("{b}
{c}")); + + ModelingVisualizationGeoFilesEntity geoFile = modelingVisualizationGeoFilesService.getById(scatterChartParamsBody.getLocation().getCommonValue()); + //compute geo`s center and config + double[] center = GeoUtils.getGeoJsonCenter(geoFile.getGeoJson()); + + //if roam is config + double centerX = scatterChartParamsBody.getCenter().get(0) != 0 ? scatterChartParamsBody.getCenter().get(0) : center[0]; + double centerY = scatterChartParamsBody.getCenter().get(1) != 0 ? scatterChartParamsBody.getCenter().get(1) : center[1]; + double zoom = scatterChartParamsBody.getZoom() > 0 ? scatterChartParamsBody.getZoom() : 1.0; + + scatterChartBody.setGeo(List.of( + new Geo().setMap(geoFile.getRegisterName()) + .setZoom(zoom) + .setCenter(List.of(centerX, centerY)) + .setRoam(true))); + + List allKeys = new ArrayList<>(queryResult.get(0).keySet()); + if (!allKeys.contains(scatterChartParamsBody.getLocation().getField())) { + throw new RuntimeException("The field in the parameter is not found in the query result, please check whether the query table or condition has been replaced."); + } + + List series = genSeries(scatterChartParamsBody.getLocation()); + scatterChartBody.setSeries(series); + + scatterChartBody.setVisualMap(new VisualMap().setMax(scatterSeriesConstructor.maxValue).setMin(0).setText(List.of("high","low")) + .setInRange(new InRange().setColor(List.of(scatterChartParamsBody.getColorConfig().get(0),scatterChartParamsBody.getColorConfig().get(1),scatterChartParamsBody.getColorConfig().get(2))))); + + return JSONObject.toJSONString(scatterChartBody); + } + + public List genSeries(Location location){ + scatterSeriesConstructor = new ScatterSeriesConstructor(scatterChartParamsBody.getIndicator(), location, queryResult,scatterChartParamsBody.getLocation().getArea()); + return scatterSeriesConstructor.getSeriesData(); + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/ClusterOpsProvider.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/ClusterOpsProvider.java new file mode 100644 index 000000000..8e50988ff --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/ClusterOpsProvider.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterOpsProvider.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/ClusterOpsProvider.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.InstallContext; +import org.opengauss.admin.plugin.domain.model.ops.OpsClusterContext; +import org.opengauss.admin.plugin.domain.model.ops.SshCommandConstants; +import org.opengauss.admin.plugin.domain.model.ops.UnInstallContext; +import org.opengauss.admin.plugin.domain.model.ops.UpgradeContext; +import org.opengauss.admin.plugin.enums.ops.OpenGaussSupportOSEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.enums.ops.WdrScopeEnum; +import com.jcraft.jsch.Session; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.plugin.vo.ops.GucSettingVO; + +import java.util.List; + +/** + * Cluster Installation Service Provider Specification + * + * @author lhf + * @date 2022/8/12 09:09 + **/ +public interface ClusterOpsProvider { + /** + * Installed openGauss version + * + * @return openGauss version + */ + OpenGaussVersionEnum version(); + + /** + * get Installation dependency + * + * @param os OpenGaussSupportOSEnum + * @return installation dependency + */ + default String dependencyCommand(OpenGaussSupportOSEnum os){ + return os.getDependencyCommand(); + } + + /** + * perform installation + * + * @param installContext installation context + */ + void install(InstallContext installContext); + + /** + * perform uninstall + * + * @param unInstallContext unload context + */ + void uninstall(UnInstallContext unInstallContext); + + /** + * reboot + * + * @param opsClusterContext + */ + void restart(OpsClusterContext opsClusterContext); + + /** + * start up + * + * @param opsClusterContext + */ + void start(OpsClusterContext opsClusterContext); + + /** + * stop + * + * @param opsClusterContext + */ + void stop(OpsClusterContext opsClusterContext); + + /** + * upgrade + * @param upgradeContext context + */ + default void upgrade(UpgradeContext upgradeContext) { + // only enterprise version support + } + + /** + * Whether to enable WdrSnapshot + * + * @param session + * @param clusterEntity + * @param opsClusterNodeEntities + * @param scope + * @param dataPath + */ + void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity, List opsClusterNodeEntities, WdrScopeEnum scope, String dataPath); + + /** + * set guc param to database nodes + */ + void configGucSetting(JschUtil jschUtil, Session session, OpsClusterEntity clusterEntity, boolean isApplyToAllNode, String dataPath, GucSettingVO gucSettingVO); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/ClusterTaskProvider.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/ClusterTaskProvider.java new file mode 100644 index 000000000..e5299805b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/ClusterTaskProvider.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterOpsProvider.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/ClusterOpsProvider.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; + +import org.opengauss.admin.plugin.domain.model.ops.InstallContext; +import org.opengauss.admin.plugin.domain.model.ops.OpsClusterContext; +import org.opengauss.admin.plugin.enums.ops.OpenGaussSupportOSEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; + +/** + * Cluster Installation Service Provider Specification + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +public interface ClusterTaskProvider { + /** + * Installed openGauss version + * + * @return openGauss version + */ + OpenGaussVersionEnum version(); + + /** + * get Installation dependency + * + * @param os OpenGaussSupportOSEnum + * @return installation dependency + */ + default String dependencyCommand(OpenGaussSupportOSEnum os){ + return os.getDependencyCommand(); + } + + /** + * perform installation + * + * @param installContext installation context + */ + void install(InstallContext installContext); + + /** + * reboot + * + * @param opsClusterContext + */ + void restart(OpsClusterContext opsClusterContext); + + /** + * start up + * + * @param opsClusterContext + */ + void start(OpsClusterContext opsClusterContext); + + /** + * stop + * + * @param opsClusterContext + */ + void stop(OpsClusterContext opsClusterContext); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IHostService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IHostService.java new file mode 100644 index 000000000..71360479d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IHostService.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IHostService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IHostService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; + +import java.util.List; + +/** + * @author lhf + * @date 2023/2/14 14:28 + **/ +public interface IHostService { + boolean pathEmpty(String id, String path, String rootPassword); + + boolean portUsed(String id, Integer port, String rootPassword); + + boolean fileExist(String id, String file, String rootPassword); + + /** + * query lun path by nvme protocol + * + * @param id host id + * @param rootPassword root password + * @return list of query result + */ + List nvmeLunQuery(String id, String rootPassword); + + /** + * query lun path by scsi protocol + * + * @param id host id + * @param rootPassword root password + * @return list of query result + */ + List scsiLunQuery(String id, String rootPassword); + /** + * query lun path + * + * @param id host id + * @param rootPassword root password + * @return list of query result + */ + List multiPathQuery(String id, String rootPassword); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsBackupService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsBackupService.java new file mode 100644 index 000000000..99997f172 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsBackupService.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsBackupService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsBackupService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.ops.OpsBackupEntity; +import org.opengauss.admin.plugin.vo.ops.BackupInputDto; +import org.opengauss.admin.plugin.vo.ops.BackupVO; +import org.opengauss.admin.plugin.vo.ops.RecoverInputDto; + +/** + * @author lhf + * @date 2022/11/5 09:48 + **/ +public interface IOpsBackupService extends IService { + + void backup(BackupInputDto backup); + + void recover(String id, RecoverInputDto recover); + + void del(String id); + + Page pageBackup(Page page, String clusterId); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsCheckService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsCheckService.java new file mode 100644 index 000000000..e80ed012d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsCheckService.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsCheckService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsCheckService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.ops.OpsCheckEntity; + +/** + * @author lhf + * @date 2022/11/13 17:17 + **/ +public interface IOpsCheckService extends IService { + OpsCheckEntity getLastResByClusterId(String clusterId); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterLogService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterLogService.java new file mode 100644 index 000000000..950da44d6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterLogService.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsClusterLogService.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterLogService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterOperateLog; + +/** + * IOpsClusterLogService + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +public interface IOpsClusterLogService { + + /** + * Save Operlog + */ + void insertOperateLog(OpsClusterOperateLog operateLog); + + /** + * Query the collection of system operation logs + * + * @param operateLog operateLog + */ + IPage selectOperateLogList(IPage page, OpsClusterOperateLog operateLog); + + /** + * Delete Cluster Operation Logs in Batches + * + * @param operateIds operateIds + */ + int deleteOperateLogByIds(String[] operateIds); + + /** + * Delete Cluster Operation Logs by ClusterId + * + * @param clusterId clusterId + * @return delete count + */ + int deleteOperateLogByClusterId(String clusterId); + + /** + * Query operation log details + * + * @param operateId operateId + */ + OpsClusterOperateLog selectOperateLogById(String operateId); + + /** + * Query the operation log of the cluster + * + * @param clusterId clusterId + * @return log + */ + String queryClusterOperateLog(String clusterId); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterNodeService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterNodeService.java new file mode 100644 index 000000000..a5d577422 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterNodeService.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsClusterNodeService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterNodeService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; + +import java.util.List; + +/** + * @author lhf + * @date 2022/8/18 09:15 + **/ +public interface IOpsClusterNodeService extends IService { + List listClusterNodeByClusterId(String clusterId); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterService.java new file mode 100644 index 000000000..a4e2eeee9 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterService.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsClusterService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.model.ops.*; +import org.opengauss.admin.plugin.domain.model.ops.env.HostEnv; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussSupportOSEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.vo.ops.SessionVO; +import org.opengauss.admin.plugin.vo.ops.SlowSqlVO; +import org.opengauss.admin.plugin.vo.ops.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Map; + +/** + * @author lhf + * @date 2022/8/6 17:37 + **/ +public interface IOpsClusterService extends IService { + void download(DownloadBody downloadBody); + + void install(InstallBody installBody); + + UpgradeOsCheckVO upgradeOsCheck(String clusterId, String rootPassword); + + void upgrade(UpgradeBody upgradeBody); + + void upgradeCommit(UpgradeBody upgradeBody); + + void upgradeRollback(UpgradeBody upgradeBody); + + void quickInstall(InstallBody installBody); + + void ssh(SSHBody sshBody); + + List ls(String hostId, String path); + + OpsNodeLogVO logPath(String clusterId, String hostId); + + List auditLog(Page page, String clusterId, String hostId, String start, String end); + + List listSession(String clusterId, String hostId); + + List slowSql(Page page, String clusterId, String hostId, String start, String end); + + void download(String hostId, String path, String filename, HttpServletResponse response); + + void uninstall(UnInstallBody unInstallBody); + + void restart(OpsClusterBody restartBody); + + void start(OpsClusterBody startBody); + + void stop(OpsClusterBody stopBody); + + List listEnterpriseCluster(); + + ClusterSummaryVO summary(); + + /** + * monitor + * + * @param clusterId clusterId + * @param hostId hostId + * @param businessId businessId + * @param role role + * @return String + */ + String monitor(String clusterId, String hostId, String businessId, ClusterRoleEnum role); + + List listClusterHost(String clusterId); + + boolean hasName(String name); + + void generateconf(String clusterId, String hostId, String businessId); + + void switchover(String clusterId, String hostId, String businessId); + + void build(String clusterId, String hostId, String businessId); + + ListDir listInstallPackage(OpenGaussVersionEnum openGaussVersionEnum, Integer userId); + + HostEnv env(String hostId, OpenGaussSupportOSEnum expectedOs, String rootPassword); + + Map threadPoolMonitor(); + + void removeCluster(String clusterId); + + List getGucSettingList(String clusterId, String hostId) throws OpsException; + + void batchConfigGucSetting(GucSettingDto gucSettingDto); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterTaskNodeService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterTaskNodeService.java new file mode 100644 index 000000000..f2b0ebfbf --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterTaskNodeService.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsClusterTaskService.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterTaskNodeService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.plugin.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.dto.OpsClusterTaskNodeDTO; + +import java.util.List; + +/** + * IOpsClusterTaskNodeService + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +public interface IOpsClusterTaskNodeService extends IService { + + /** + * List OpsClusterTaskNodeEntity by cluster task id. + * + * @param taskId task id. + * @return List of OpsClusterTaskNodeEntity. + */ + List listByClusterTaskId(String taskId); + + /** + * list task node by host and user + * + * @param hostId hostId + * @param hostUserId hostUserId + * @return list + */ + List listByInstallHostUser(String hostId, String hostUserId); + + /** + * Remove OpsClusterTaskNodeEntity by cluster task id. + * + * @param taskId task id. + */ + void removeByClusterId(String taskId); + + /** + * Check cm port of host and port. + * + * @param taskId taskId + * @param hostId host id. + * @param hostPort host port. + * @return result + */ + boolean checkHostPortUsedByCm(String taskId, String hostId, Integer hostPort); + + /** + * Delete cluster task node. + * + * @param taskId taskId + * @param clusterNodeId cluster node id + * @return result + */ + String deleteClusterTaskNode(String taskId, String clusterNodeId); + + /** + * Update cluster task node. + * + * @param updateDto update dto + * @return result + */ + String updateClusterTaskNode(OpsClusterTaskNodeDTO updateDto); + + /** + * Save cluster task node. + * + * @param insertDto insert dto + * @return result + */ + String saveClusterTaskNode(OpsClusterTaskNodeDTO insertDto); + + /** + * query cluster node and node port list. + * + * @param clusterId clusterId + * @return list + */ + List queryClusterNodeAndPortList(String clusterId); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterTaskService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterTaskService.java new file mode 100644 index 000000000..929962031 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterTaskService.java @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsClusterTaskService.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsClusterTaskService.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.plugin.service.ops; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.enums.ops.OpsClusterTaskStatusEnum; +import org.opengauss.admin.common.enums.ops.OpsHostPortUsedStatusEnum; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsClusterTaskVO; +import org.opengauss.admin.plugin.domain.model.ops.dto.OpsClusterTaskQueryParamDTO; +import org.opengauss.admin.plugin.domain.model.ops.dto.OpsClusterTaskDTO; +import org.opengauss.admin.plugin.vo.ops.ClusterEnvCheck; +import org.opengauss.admin.plugin.vo.ops.TaskStatusVo; +import org.opengauss.admin.plugin.vo.ops.ClusterPortVo; + +import java.util.List; +import java.util.Map; + +/** + * IOpsClusterTaskService + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +public interface IOpsClusterTaskService extends IService { + + /** + * page query ops cluster task entity by condition + * + * @param page page condition + * @param dto query condition + * @return IPage + */ + IPage pageByCondition(Page page, OpsClusterTaskQueryParamDTO dto); + + /** + * delete ops cluster task entity by task id + * + * @param taskIds task id list + * @return Map + */ + Map deleteClusterTask(List taskIds); + + /** + * query ops cluster task detail + * + * @param taskId task id + * @return OpsClusterTaskVO + */ + OpsClusterTaskVO queryClusterTaskDetail(String taskId); + + /** + * create ops cluster task + * + * @param dto dto + * @return task id + */ + String createClusterTask(OpsClusterTaskDTO dto); + + /** + * update ops cluster task + * + * @param dto dto + */ + void updateClusterTask(OpsClusterTaskDTO dto); + + /** + * confirm ops cluster task + * + * @param taskId task id + */ + void confirmClusterTask(String taskId); + + /** + * check cluster install environment + * + * @param taskId task id + * @return List + */ + ClusterEnvCheck checkClusterInstallEnvironment(String taskId); + + /** + * get cluster install environment + * + * @param taskId task id + * @return List + */ + ClusterEnvCheck getClusterInstallEnvironment(String taskId); + + /** + * check cluster install environment + * + * @param taskId task id + * @return List + */ + String copyClusterTask(String taskId); + + /** + * check cluster name exist + * + * @param clusterId cluster id + * @param clusterName cluster name + * @return boolean + */ + boolean checkClusterNameExist(String clusterId, String clusterName); + + /** + * get host user by host id + * + * @param hostId host id + * @return List + */ + List listHostUserByHostId(String hostId); + + /** + * get host user by host id + * + * @param hostIp host ip + * @return List + */ + OpsHostEntity getHostByIp(String hostIp); + + /** + * check host port + * + * @param taskId taskId + * @param hostId host id + * @param hostPort host port + * @return OpsHostPortUsedStatusEnum + */ + OpsHostPortUsedStatusEnum checkHostPort(String taskId, String hostId, Integer hostPort); + + /** + * check cluster install prot info + * + * @param clusterId cluster id + * @return port used info + */ + Map checkTaskHostPort(String clusterId); + + /** + * batch install cluster + * + * @param taskIds task ids + * @return Map + * @return Map + */ + Map batchInstallCluster(List taskIds); + + /** + * reinstall cluster + * + * @param taskId taskId + * @return String + */ + void reInstallCluster(String taskId); + + /** + * get install cluster status by task ids + * + * @param taskIds taskIds + * @return status + */ + Map getBatchInstallTaskStatus(List taskIds); + + /** + * update cluster task status + * + * @param clusterId clusterId + * @param opsClusterTaskStatusEnum opsClusterTaskStatusEnum + * @param remark remark + */ + void updateClusterTaskStatus(String clusterId, OpsClusterTaskStatusEnum opsClusterTaskStatusEnum, String remark); + + /** + * get host list + * + * @param os os + * @param osVersion osVersion + * @param cpuArch cpuArch + * @return list + */ + List getHostList(String os, String osVersion, String cpuArch); + + /** + * full check cluster task : node nums, disk space, host port ,deploy type + * + * @param clusterId cluster id + * @return check result + */ + boolean checkClusterTask(String clusterId); + + /** + * check host user can install cluster node + * + * @param hostIp host ip + * @param hostUsername username + * @return can install + */ + boolean checkHostInstanceCanInstallClusterNode(String hostIp, String hostUsername); + + /** + * check host paths disk space + * + * @param hostId host id + * @param paths host path + * @return check result + */ + Map checkHostDiskSpace(String hostId, List paths); + + /** + * check host and user can install cluster + * + * @param clusterId clusterId + * @param hostId hostId + * @param hostUserId hostUserId + * @return check result + */ + boolean checkHostAndUserInstallCluster(String clusterId, String hostId, String hostUserId); + + /** + * modify cluster task of node change + * + * @param clusterId clusterId + * @param hostId hostId + * @param hostUserId hostUserId + * @param nodeCount nodeCount + */ + void modifyClusterTaskOfNodeChange(String clusterId, String hostId, String hostUserId, int nodeCount); + + /** + * delete cluster task force + * + * @param taskId task id + * @return result + */ + String deleteClusterTaskForce(String taskId); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsOlkService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsOlkService.java new file mode 100644 index 000000000..69771be82 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsOlkService.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsOlkService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsOlkService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.ops.OpsOlkEntity; +import org.opengauss.admin.plugin.domain.model.ops.olk.InstallOlkBody; +import org.opengauss.admin.plugin.domain.model.ops.olk.OlkConfig; +import org.opengauss.admin.plugin.domain.model.ops.olk.OlkPageVO; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.ShardingRuleDto; + +public interface IOpsOlkService extends IService { + void install(InstallOlkBody installBody); + + void remove(String id); + + void destroy(String id, String bid); + + String generateRuleYaml(OlkConfig config); + + void start(String id, String bid); + + void stop(String id, String bid); + + IPage pageOlk(Page page, String name); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsPackageManagerService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsPackageManagerService.java new file mode 100644 index 000000000..458bfb202 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsPackageManagerService.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsPackageManagerService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsPackageManagerService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.domain.UploadInfo; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackageManagerEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackageVO; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.springframework.web.multipart.MultipartFile; + +/** + * @author lhf + * @date 2022/12/11 16:14 + **/ +public interface IOpsPackageManagerService extends IService { + String getCpuArchByPackagePath(String installPackagePath, OpenGaussVersionEnum version); + + void savePackage(OpsPackageManagerEntity pkg, Integer userId) throws OpsException; + + void updatePackage(OpsPackageManagerEntity pkg, Integer userId) throws OpsException; + + void delPackage(String id); + + OpsPackageVO analysisPkg(String pkgName, String pkgType); + + @Deprecated + UploadInfo upload(MultipartFile file, Integer userId) throws OpsException; + + boolean deletePkgTar(String path, String id); + + String getSysUploadPath(Integer userId); + + boolean checkUploadPath(String path, Integer userId); + + boolean hasName(String name); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsPackageManagerV2Service.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsPackageManagerV2Service.java new file mode 100644 index 000000000..bbc11e358 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsPackageManagerV2Service.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsPackageManagerV2Service.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsPackageManagerV2Service.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.dto.ops.PackageDto; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackageManagerEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackageVO; + +import java.util.List; + +/** + * @author wangchao + * @date 2024/06/15 09:26 + */ +public interface IOpsPackageManagerV2Service extends IService { + /** + * Query the list of installed packages + * + * @param packageDict packageDict + * @return List + */ + List queryOpsPackageList(PackageDto packageDict); + + /** + * Query the list of packages version number + * + * @return List + */ + List listVersionNumber(); + + /** + * checking package list + * + * @param packageIds packageIds + */ + void checkingPackageList(List packageIds); + + /** + * checking package + * + * @param packageId packageId + * @return boolean + */ + boolean checkingPackage(String packageId); + + /** + * download package and save package info + * + * @param entity entity + * @param userId + * @param wsBusinessId wsBusinessId + */ + void saveOnlinePackage(OpsPackageManagerEntity entity, Integer userId, String wsBusinessId); + + /** + * update package info + * + * @param packageEntity packageEntity + * @param userId userId + * @param wsBusinessId wsBusinessId + */ + void updateOnlinePackage(OpsPackageManagerEntity packageEntity, Integer userId, String wsBusinessId); + + /** + * checking package name is exist + * + * @param packageId packageId + * @param name name + * @return boolean + */ + boolean hasName(String packageId, String name); + + /** + * save package info + * + * @param pkg pkg + * @param userId userId + */ + void saveUploadPackage(OpsPackageManagerEntity pkg, Integer userId); + + /** + * update package info + * + * @param pkg pkg + * @param userId userId + */ + void updateUploadPackage(OpsPackageManagerEntity pkg, Integer userId); + + /** + * del package list + * + * @param packageIds packageIds + */ + void delPackage(List packageIds); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsPackagePathDictService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsPackagePathDictService.java new file mode 100644 index 000000000..487bdfb2a --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsPackagePathDictService.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsPackagePathDictService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsPackagePathDictService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.common.core.dto.ops.PackageDto; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackagePathDictEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackagePathDictVO; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackageVO; + +import java.util.List; + +/** + * @author wangchao + * @date 2024/06/15 09:26 + */ +public interface IOpsPackagePathDictService extends IService { + + List listPackagePathDict(); + + /** + * Check if the OS exists + * + * @param os os + * @return boolean + */ + boolean checkOsExists(String os); + + /** + * Check if the cpuArch exists + * + * @param cpuArch cpuArch + * @return boolean + */ + boolean checkCpuArchExists(String cpuArch); + + /** + * query package path dict + * + * @param packageDto packageDto + * @return OpsPackagePathDictVO + */ + OpsPackagePathDictVO queryPackagePathDict(PackageDto packageDto); + + /** + * Build and check package url is valid + * + * @param installPackageUrlPrefix installPackageUrlPrefix + * @param packageVersionNum packageVersionNum + * @param packageDict packageDict + * @return OpsPackageVO + */ + OpsPackageVO buildOpsPackage(String installPackageUrlPrefix, String packageVersionNum, + OpsPackagePathDictVO packageDict); + + /** + * check current environment is online + * + * @return boolean + */ + boolean checkCurrentEnvironmentIsOnline(); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsWdrService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsWdrService.java new file mode 100644 index 000000000..0f629be35 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsWdrService.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * IOpsWdrService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/IOpsWdrService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.opengauss.admin.plugin.domain.entity.ops.OpsWdrEntity; +import org.opengauss.admin.plugin.domain.model.ops.WdrGeneratorBody; +import org.opengauss.admin.plugin.enums.ops.WdrScopeEnum; +import org.opengauss.admin.plugin.enums.ops.WdrTypeEnum; +import org.opengauss.admin.plugin.vo.ops.DwrSnapshotVO; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.Date; +import java.util.List; + +/** + * @author lhf + * @date 2022/10/13 15:13 + **/ +public interface IOpsWdrService extends IService { + List listWdr(String clusterId, WdrScopeEnum wdrScope, WdrTypeEnum wdrType, String hostId, Date start, Date end); + + void generate(WdrGeneratorBody wdrGeneratorBody); + + Page listSnapshot(Page page, String clusterId, String hostId); + + void createSnapshot(String clusterId, String hostId); + + void del(String id); + + void downloadWdr(String wdrId, HttpServletResponse response); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/InstallPackageAutoUpgrade.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/InstallPackageAutoUpgrade.java new file mode 100644 index 000000000..bc84b6c92 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/InstallPackageAutoUpgrade.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * InstallPackageAutoUpgrade.java + * + * IDENTIFICATION + * org.opengauss.admin.plugin.service.ops.InstallPackageAutoUpgrade.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; + +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackageManagerEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackagePathDictEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackagePathDictVO; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import jakarta.annotation.Resource; + +/** + * openGauss InstallPackageAutoUpgrade + * + * @author: wangchao + * @Date: 2025/4/22 16:27 + * @Description: PackageAutoUpgrade + * @since 7.0.0-RC1 + **/ +@Slf4j +@Component +public class InstallPackageAutoUpgrade implements ApplicationRunner { + private static final Snowflake SNOWFLAKE = IdUtil.getSnowflake(1, 1); + private static final String TAR_GZ = ".tar.gz"; + private static final String TAR_BZ_2 = ".tar.bz2"; + + @Resource + private IOpsPackageManagerV2Service opsPackageManagerV2Service; + @Resource + private IOpsPackagePathDictService opsPackagePathDictService; + @Value("${install.package.upgrade.history-version}") + private String historyVersion; + @Value("${install.package.upgrade.old-history-version}") + private String oldHistoryVersion; + @Value("${installPackage.urlPrefix}") + private String pkgUrlPrefix; + + @Override + public void run(ApplicationArguments args) throws Exception { + log.info("InstallPackageAutoUpgrade run"); + List list = opsPackagePathDictService.listPackagePathDict(); + List versionDictList = list.stream() + .filter(dict -> !StrUtil.equalsIgnoreCase(dict.getPkgTmpUseVersion(), oldHistoryVersion)) + .collect(Collectors.toList()); + upgradePackageDict(versionDictList); + upgradePackageManager(versionDictList); + log.info("InstallPackageAutoUpgrade finished"); + } + + private void upgradePackageDict(List versionDictList) { + OpsPackagePathDictVO dictVO = versionDictList.get(0); + if (Objects.equals(dictVO.getPkgTmpUseVersion(), historyVersion)) { + log.info("InstallPackageAutoUpgrade version is already latest"); + return; + } + List dictIds = versionDictList.stream().map(OpsPackagePathDictVO::getId).collect(Collectors.toList()); + opsPackagePathDictService.update(Wrappers.lambdaUpdate() + .set(OpsPackagePathDictEntity::getPkgTmpUseVersion, historyVersion) + .in(OpsPackagePathDictEntity::getId, dictIds)); + log.info("InstallPackageAutoUpgrade version dict is upgrade to: {}", historyVersion); + } + + private void upgradePackageManager(List versionDictList) { + List packageManagerList = opsPackageManagerV2Service.list(); + Set packageNumSet = packageManagerList.stream() + .map(OpsPackageManagerEntity::getPackageVersionNum) + .collect(Collectors.toSet()); + String[] historyVersions = historyVersion.split(";"); + for (String version : historyVersions) { + if (!packageNumSet.contains(version)) { + upgradePackageManager(versionDictList, version); + log.info("InstallPackageAutoUpgrade package manager version: {}", version); + } + } + } + + private void upgradePackageManager(List versionDictList, String version) { + versionDictList.forEach(dict -> { + OpsPackageManagerEntity entity = new OpsPackageManagerEntity(); + entity.setPackageId(SNOWFLAKE.nextIdStr()); + entity.setOs(dict.getOs()); + entity.setOsVersion(dict.getOsVersion()); + entity.setCpuArch(dict.getCpuArch()); + entity.setName(buildPackageManagerName(dict.getPackageNameTmp(), version)); + entity.setPackageVersion(dict.getVersion()); + entity.setPackageVersionNum(version); + entity.setPackageVersionNum(version); + entity.setType("openGauss"); + entity.setPackageUrl(dict.buildFullPackageUrl(pkgUrlPrefix, version, false)); + entity.setCreateBy("admin"); + entity.setUpdateBy("admin"); + entity.setCreateTime(new Date()); + entity.setUpdateTime(new Date()); + opsPackageManagerV2Service.save(entity); + }); + } + + private String buildPackageManagerName(String packageNameTmp, String version) { + if (packageNameTmp.contains(TAR_GZ)) { + return packageNameTmp.replace("%s", version).replace(TAR_GZ, ""); + } else if (packageNameTmp.contains(TAR_BZ_2)) { + return packageNameTmp.replace("%s", version).replace(TAR_BZ_2, ""); + } else { + return packageNameTmp.replace("%s", version); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/BatchImportClusterService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/BatchImportClusterService.java new file mode 100644 index 000000000..45479e75c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/BatchImportClusterService.java @@ -0,0 +1,568 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BatchImportClusterService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/BatchImportClusterService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import static org.opengauss.admin.common.constant.ops.SshCommandConstants.CHECK_ENTERPRISE_VERSION; +import static org.opengauss.admin.common.constant.ops.SshCommandConstants.CHECK_LITE_VERSION; +import static org.opengauss.admin.common.constant.ops.SshCommandConstants.CHECK_MINILIST_VERSION; +import static org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum.MASTER; +import static org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum.SLAVE; +import static org.opengauss.admin.plugin.enums.ops.DeployTypeEnum.CLUSTER; +import static org.opengauss.admin.plugin.enums.ops.DeployTypeEnum.SINGLE_NODE; +import static org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum.ENTERPRISE; +import static org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum.LITE; +import static org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum.MINIMAL_LIST; + +import com.alibaba.excel.EasyExcel; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.jcraft.jsch.Session; + +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.enums.SysLanguage; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsImportEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsImportSshEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsParseExcelEntity; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.listener.OpsImportEntityListener; +import org.opengauss.admin.plugin.mapper.ops.OpsClusterMapper; +import org.opengauss.admin.plugin.mapper.ops.OpsImportSshMapper; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.plugin.utils.DBUtil; +import org.opengauss.admin.plugin.utils.OpsAssert; +import org.opengauss.admin.system.plugin.beans.SshLogin; +import org.opengauss.admin.system.plugin.facade.JschExecutorFacade; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; + +/** + * BatchImportClusterService 原有集群批量导入功能重构 + *

+ * @author: wangchao + * @Date: 2024/11/4 12:02 + * @Description: ImportClusterService + * + * @since 7.0.0 + **/ +@Slf4j +@Service +public class BatchImportClusterService extends ServiceImpl { + private static final String ENTER_CONSTANT = "ENTERPRISE"; + private static final String LITE_CONSTANT = "LITE"; + private static final String MINI_CONSTANT = "MINIMAL_LIST"; + private static final String OTHER_CONSTANT = "OTHER"; + private static final String NODE_PRIMARY = "Primary"; + private static final String NODE_STANDBY = "Standby"; + + private final OpsClusterEntity opsClusterEntity = new OpsClusterEntity(); + private final List opsClusterEntityList = new ArrayList<>(); + private final List opsClusterNodeEntityList = new ArrayList<>(); + + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private JschExecutorFacade jschExecutorFacade; + @Resource + private DBUtil dbUtil; + @Resource + private IOpsClusterNodeService opsClusterNodeService; + @Resource + private OpsImportSshMapper opsImportSshMapper; + private int importSuccessCount; + private boolean isInfoAndConn; + private boolean isMasterNormal; + @Resource + private OpsClusterMapper opsClusterMapper; + + /** + * downloadImportFile + * + * @param response response + * @param currentLocale currentLocale + */ + public void downloadImportFile(HttpServletResponse response, String currentLocale) { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + String fileName = null; + fileName = URLEncoder.encode("模板", StandardCharsets.UTF_8).replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); + List usersList = new ArrayList<>(); + OpsImportEntity opsImportEntity = new OpsImportEntity("1", "ip1,ip2", "ip1,ip2", "omm", "gaussdb", "12345", + 5432, "/home/omm/cluster_2024.bashrc", null, null); + usersList.add(opsImportEntity); + writeZhOrEnInfo(response, currentLocale, usersList); + } + + /** + * dwonloadErrorFile + * + * @param response response + * @param usersList usersList + * @param currentLocale currentLocale + */ + public void downloadErrorFile(HttpServletResponse response, List usersList, String currentLocale) { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + String fileName = null; + fileName = URLEncoder.encode("模板", StandardCharsets.UTF_8).replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); + writeZhOrEnInfo(response, currentLocale, usersList); + } + + /** + * import success count + * + * @return count + */ + public int responseImportSuccessCount() { + return importSuccessCount; + } + + private void markErrorInfo(OpsImportEntity opsImportEntity) { + isInfoAndConn = false; + opsImportEntity.setImportStatus("fail"); + } + + /** + * upload import file + * + * @param file file + * @return opsImportEntityList + */ + public List uploadImportFile(MultipartFile file) { + List opsImportEntityList = new ArrayList<>(); + OpsImportEntityListener userDataListener = new OpsImportEntityListener(); + try { + opsImportEntityList = EasyExcel.read(file.getInputStream(), OpsImportEntity.class, userDataListener) + .sheet() + .doReadSync(); + } catch (IOException e) { + log.error("upload fail!" + e); + throw new OpsException("upload fail!"); + } + return opsImportEntityList; + } + + /** + * parse excel + * + * @param list list + * @return list + */ + public List parseExcel(List list) { + importSuccessCount = 0; + if (list == null || list.isEmpty()) { + return list; + } + Session session = null; + for (int i = 0; i < list.size(); i++) { + OpsImportEntity clusterInfo = list.get(i); + try { + clusterInfo.checkConfig(); + } catch (OpsException e) { + clusterInfo.setImportStatus("fail"); + clusterInfo.setErrorInfo("please fill in all the blanks!" + e); + continue; + } + isInfoAndConn = true; + isMasterNormal = false; + String[] hostIps = clusterInfo.getPublicIp().split(","); + for (int j = 0; j < hostIps.length; j++) { + try { + String hostIp = hostIps[j]; + OpsImportSshEntity hostUserInfos = getExistHostUserInfos(hostIp, clusterInfo); + OpsAssert.nonNull(hostUserInfos, + "hostUser is not exit : " + hostIp + ":" + clusterInfo.getInstallUsername()); + SshLogin sshLogin = new SshLogin(hostIps[0], hostUserInfos.getPort(), + clusterInfo.getInstallUsername(), encryptionUtils.decrypt(hostUserInfos.getPassword())); + String versionType = judgeVersionType(sshLogin, clusterInfo); + OpsParseExcelEntity opsParseExcelEntity = new OpsParseExcelEntity(versionType, sshLogin, + clusterInfo, hostUserInfos, hostIps[j], j, hostIps.length); + selectClusterInfo(opsParseExcelEntity); + } catch (OpsException opsException) { + markErrorInfo(clusterInfo); + clusterInfo.setErrorInfo("selectClusterInfo opsException:" + opsException); + } finally { + closeSession(session); + } + } + boolean isSameCluster = isClusterExist(hostIps, clusterInfo); + saveCluster(isSameCluster, clusterInfo); + } + return list; + } + + private void saveCluster(boolean isSameCluster, OpsImportEntity opsImportEntity) { + if (!isSameCluster && isInfoAndConn && isMasterNormal) { + saveBatch(opsClusterEntity, opsClusterNodeEntityList, opsImportEntity); + } + opsClusterEntityList.clear(); + opsClusterNodeEntityList.clear(); + } + + /** + * batch save cluster + * + * @param opsClusterEntity opsClusterEntity + * @param opsClusterNodeEntityList opsClusterNodeEntityList + * @param opsImportEntity opsImportEntity + */ + @Transactional + public void saveBatch(OpsClusterEntity opsClusterEntity, List opsClusterNodeEntityList, + OpsImportEntity opsImportEntity) { + try { + save(opsClusterEntity); + opsClusterNodeService.saveBatch(opsClusterNodeEntityList); + importSuccessCount++; + opsImportEntity.setImportStatus("success"); + } catch (OpsException e) { + log.error("database saveBatch error!"); + opsImportEntity.setImportStatus("fail"); + opsImportEntity.setErrorInfo("please change clusterId that already exists!"); + } + } + + private boolean isClusterExist(String[] hosts, OpsImportEntity opsImportEntity) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsClusterEntity.class) + .eq(OpsClusterEntity::getClusterId, opsImportEntity.getClusterName()); + List getSameNameCluster = opsClusterMapper.selectList(queryWrapper); + opsClusterEntity.setDeployType(hosts.length > 1 ? CLUSTER : SINGLE_NODE); + if (getSameNameCluster.size() > 0) { + opsImportEntity.setImportStatus("fail"); + opsImportEntity.setErrorInfo("The clusterName is already exist."); + return true; + } + for (String host : hosts) { + List clusterId = opsImportSshMapper.checkPublicIpAndPort(host, opsImportEntity.getPort() + ""); + if (clusterId.size() > 0) { + opsImportEntity.setImportStatus("fail"); + opsImportEntity.setErrorInfo("The public IP and port that are inputted already exist, " + + "Similar to it are :" + clusterId); + return true; + } + } + return false; + } + + private void selectClusterInfo(OpsParseExcelEntity opsParseExcelEntity) { + String envpath = opsParseExcelEntity.getOpsImportEntity().getEnvPath(); + String enterAndLiteGaussHome = String.format("source %s;echo $GAUSSHOME", envpath); + String versionNum = String.format("source %s;gsql -V|awk '{print $3}'", envpath); + String command = null; + if (opsParseExcelEntity.getVersionType().equals(ENTER_CONSTANT)) { + String enterPGDATA = String.format("source %s;gs_om -t view|awk '/^sshChannel 1:%s/," + + "/^============================================================/'" + + "|grep datanodeLocalDataPath|awk -F ':' '{print $2}'", envpath, opsParseExcelEntity.getPublicIp()); + String enterJudgeMasterOrSlave = String.format( + "source %s;gs_om -t query|grep %s|" + "awk '{print $7}'", envpath, opsParseExcelEntity.getPublicIp()); + command = enterAndLiteGaussHome + ";" + enterPGDATA + ";" + versionNum + ";" + enterJudgeMasterOrSlave; + } else if (opsParseExcelEntity.getVersionType().equals(LITE_CONSTANT)) { + String liteGaussData = String.format("source %s;echo $GAUSSDATA", envpath); + command = enterAndLiteGaussHome + ";" + liteGaussData + ";" + versionNum; + } else if (opsParseExcelEntity.getVersionType().equals(MINI_CONSTANT)) { + versionNum = "gsql -V|awk '{print $3}'"; + String miniGaussHome = String.format("source %s;echo $GAUSSHOME", envpath); + String miniPgData = String.format("source %s;echo $PGDATA", envpath); + command = miniGaussHome + ";" + miniPgData + ";" + versionNum; + } else { + throw new OpsException("It is not any of the openGauss versions."); + } + List resultList = new ArrayList<>(); + try { + String result = jschExecutorFacade.execCommand(opsParseExcelEntity.getSshLogin(), command); + String regex = "(\\S+)"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(result); + while (matcher.find()) { + resultList.add(matcher.group()); + } + } catch (OpsException e) { + log.error("command fail, please check the excel info and database envFile!"); + } + if (opsParseExcelEntity.getVersionType().equals(ENTER_CONSTANT)) { + checkEnterNodesNum(opsParseExcelEntity, opsParseExcelEntity.getSshLogin()); + } + packingClusterInfo(resultList, opsParseExcelEntity); + } + + private void checkEnterNodesNum(OpsParseExcelEntity opsParseExcelEntity, SshLogin sshLogin) { + String enterNodesCommand = String.format("source %s;gs_om -t query|grep %s|awk '{print $3}'", + opsParseExcelEntity.getOpsImportEntity().getEnvPath(), opsParseExcelEntity.getOpsImportEntity().getPort()); + List nodesList = new ArrayList<>(); + try { + String enterNodesIp = jschExecutorFacade.execCommand(sshLogin, enterNodesCommand); + String regex = "(\\S+)"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(enterNodesIp); + while (matcher.find()) { + nodesList.add(matcher.group()); + } + if (nodesList.size() != opsParseExcelEntity.getIpNum()) { + markErrorInfo(opsParseExcelEntity.getOpsImportEntity()); + opsParseExcelEntity.getOpsImportEntity().setErrorInfo("please check nodeNum is or not enough!"); + } + } catch (OpsException e) { + log.error("command fail, please check the excel info and database env file!"); + } + } + + private void packingClusterInfo(List resultList, OpsParseExcelEntity opsParseExcelEntity) { + opsClusterEntity.setClusterId(opsParseExcelEntity.getOpsImportEntity().getClusterName()); + opsClusterEntity.setVersion(opsParseExcelEntity.getVersionType().equals(ENTER_CONSTANT) + ? ENTERPRISE + : opsParseExcelEntity.getVersionType().equals(LITE_CONSTANT) ? LITE : MINIMAL_LIST); + if (opsParseExcelEntity.getVersionType().equals(MINI_CONSTANT) && resultList.size() < 3) { + opsClusterEntity.setVersionNum(resultList.get(1)); + } else { + opsClusterEntity.setVersionNum(resultList.get(2)); + } + opsClusterEntity.setDatabasePassword( + encryptionUtils.encrypt(opsParseExcelEntity.getOpsImportEntity().getDatabasePassword())); + opsClusterEntity.setDatabaseUsername(opsParseExcelEntity.getOpsImportEntity().getDatabaseUsername()); + opsClusterEntity.setPort(opsParseExcelEntity.getOpsImportEntity().getPort()); + opsClusterEntity.setEnvPath(opsParseExcelEntity.getOpsImportEntity().getEnvPath()); + opsClusterEntity.setInstallPath(resultList.get(0)); + OpsClusterNodeEntity opsClusterNodeEntity = new OpsClusterNodeEntity(); + opsClusterNodeEntity.setClusterNodeId(StrUtil.uuid()); + String localRole = judgeLocalRole(resultList, opsParseExcelEntity); + try { + ClusterRoleEnum clusterRole = localRole.equals(NODE_PRIMARY) ? MASTER : SLAVE; + opsClusterNodeEntity.setClusterRole(clusterRole); + } catch (NullPointerException e) { + log.error("lack of clusterInfo!"); + } + judgeNodeConnSuccess(opsParseExcelEntity.getPublicIp(), opsParseExcelEntity.getOpsImportEntity(), localRole); + opsClusterNodeEntity.setHostId(opsParseExcelEntity.getHostAndUserId().getHostId() + ""); + opsClusterNodeEntity.setInstallUserId(opsParseExcelEntity.getHostAndUserId().getHostUserId() + ""); + opsClusterNodeEntity.setInstallPath(resultList.get(0)); + opsClusterNodeEntity.setDataPath(getDataPath(opsParseExcelEntity, resultList)); + opsClusterNodeEntity.setClusterId(opsParseExcelEntity.getOpsImportEntity().getClusterName()); + opsClusterNodeEntityList.add(opsClusterNodeEntity); + } + + private String getDataPath(OpsParseExcelEntity opsParseExcelEntity, List resultList) { + if (resultList.size() > 2) { + return resultList.get(1); + } + String dataPath = resultList.get(0) + "/data"; + if (opsParseExcelEntity.getVersionType().equals(MINI_CONSTANT)) { + if (opsParseExcelEntity.getIpNum() > 1) { + if (opsParseExcelEntity.getIpSequence() == 0) { + dataPath = dataPath + "/master"; + } else { + dataPath = dataPath + "/slave"; + } + } else { + dataPath = dataPath + "/single_node"; + } + } + return dataPath; + } + + private void judgeNodeConnSuccess(String publicIp, OpsImportEntity opsImportEntity, String localRole) { + Connection connection = null; + try { + connection = dbUtil.getSession(publicIp, opsImportEntity.getPort(), opsImportEntity.getDatabaseUsername(), + opsImportEntity.getDatabasePassword()) + .orElseThrow(() -> new OpsException("please check databaseUser is or not a origianl User")); + } catch (OpsException | SQLException | ClassNotFoundException e) { + if (localRole == null || localRole.equals("")) { + opsImportEntity.setImportStatus("fail"); + } else { + if (localRole.equals(NODE_PRIMARY)) { + opsImportEntity.setImportStatus("fail"); + isInfoAndConn = false; + } + } + opsImportEntity.setErrorInfo(publicIp + " Connection fail :" + e); + } finally { + dbUtil.closeConn(connection); + } + } + + private String judgeLocalRole(List resultList, OpsParseExcelEntity opsParseExcelEntity) { + String localRole = null; + if (opsParseExcelEntity.getVersionType().equals(ENTER_CONSTANT)) { + int selectCommandLength = 4; + if (resultList.size() != selectCommandLength) { + markErrorInfo(opsParseExcelEntity.getOpsImportEntity()); + opsParseExcelEntity.getOpsImportEntity() + .setErrorInfo("There is a command that failed to execute. Please check the logs."); + for (int i = 0; i < resultList.size(); i++) { + log.error("result " + i + ":" + resultList.get(i)); + } + return NODE_PRIMARY; + } + if (resultList.get(selectCommandLength - 1).equals(NODE_PRIMARY)) { + localRole = resultList.get(selectCommandLength - 1); + isMasterNormal = true; + } else if (resultList.get(selectCommandLength - 1).equals(NODE_STANDBY)) { + localRole = resultList.get(selectCommandLength - 1); + } else { + localRole = resultList.get(selectCommandLength - 1); + opsParseExcelEntity.getOpsImportEntity().setErrorInfo("check node status error: " + localRole); + } + } else if (opsParseExcelEntity.getVersionType().equals(LITE_CONSTANT)) { + try { + isMasterNormal = true; + String selectLocal = String.format( + "source %s;gs_ctl query -D %s|grep local_role|awk '{print $3}'|head -n 1", + opsParseExcelEntity.getOpsImportEntity().getEnvPath(), resultList.get(2)); + localRole = jschExecutorFacade.execCommand(opsParseExcelEntity.getSshLogin(), selectLocal); + localRole = opsParseExcelEntity.getIpNum() == 1 ? NODE_PRIMARY : localRole; + } catch (OpsException e) { + markErrorInfo(opsParseExcelEntity.getOpsImportEntity()); + opsParseExcelEntity.getOpsImportEntity().setErrorInfo("lite localRole Exception:" + e); + } + } else { + isMasterNormal = true; + localRole = opsParseExcelEntity.getIpSequence() == 0 ? NODE_PRIMARY : NODE_STANDBY; + } + return localRole; + } + + private String judgeVersionType(SshLogin sshLogin, OpsImportEntity opsImportEntity) { + String omCommand = String.format(CHECK_ENTERPRISE_VERSION, opsImportEntity.getEnvPath(), + opsImportEntity.getEnvPath()); + String liteCommand = String.format(CHECK_LITE_VERSION, opsImportEntity.getEnvPath(), + opsImportEntity.getEnvPath()); + String miniCommand = String.format(CHECK_MINILIST_VERSION, opsImportEntity.getEnvPath(), + opsImportEntity.getEnvPath()); + List commandList = new ArrayList<>(); + commandList.add(omCommand); + commandList.add(liteCommand); + commandList.add(miniCommand); + return getOpenGaussVersion(sshLogin, commandList, opsImportEntity); + } + + private String getOpenGaussVersion(SshLogin sshLogin, List commandList, OpsImportEntity opsImportEntity) { + try { + for (String s : commandList) { + String result = jschExecutorFacade.execCommand(sshLogin, s); + if (result.contains("datanodeLocalDataPath")) { + return ENTER_CONSTANT; + } else if (result.contains("openGauss-lite")) { + return LITE_CONSTANT; + } else if (result.contains("openGauss")) { + return MINI_CONSTANT; + } else { + log.warn("{} could not find the corresponding result!", s); + } + } + } catch (OpsException e) { + log.error("get openGauss version command error:", e); + } + markErrorInfo(opsImportEntity); + opsImportEntity.setErrorInfo("get OpenGauss Version error!"); + return OTHER_CONSTANT; + } + + private OpsImportSshEntity getExistHostUserInfos(String hostIp, OpsImportEntity opsImportEntity) { + OpsImportSshEntity opsImportSshInfo = opsImportSshMapper.queryHostInfo(opsImportEntity.getInstallUsername(), + hostIp); + if (Objects.isNull(opsImportSshInfo)) { + markErrorInfo(opsImportEntity); + opsImportEntity.setErrorInfo("please import hostUser information."); + } + return opsImportSshInfo; + } + + private void writeZhOrEnInfo(HttpServletResponse response, String currentLocale, List usersList) { + OutputStream out = null; + try { + out = response.getOutputStream(); + if (SysLanguage.ZH.getInfo().equals(currentLocale)) { + EasyExcel.write(out, OpsImportEntity.class) + .head(createChineseHeaders()) + .sheet("用户信息") + .doWrite(usersList); + } else { + EasyExcel.write(out, OpsImportEntity.class) + .head(createEnglishHeaders()) + .sheet("userInfo") + .doWrite(usersList); + } + } catch (IOException e) { + throw new OpsException("download fail!"); + } finally { + try { + out.flush(); + } catch (IOException e) { + log.error("flush fail"); + } + try { + out.close(); + } catch (IOException e) { + log.error("close fail"); + } + } + } + + private void closeSession(Session session) { + if (Objects.nonNull(session) && session.isConnected()) { + session.disconnect(); + } + } + + private List> createEnglishHeaders() { + return new ArrayList<>( + Arrays.asList(List.of("clusterName"), List.of("privateIp"), List.of("publicIp"), List.of("installUsername"), + List.of("databaseUsername"), List.of("databasePassword"), List.of("port"), List.of("envPath"), + List.of("importStatus"), List.of("errorInfo"))); + } + + private List> createChineseHeaders() { + return new ArrayList<>( + Arrays.asList(List.of("集群名称"), List.of("内网IP"), List.of("外网IP"), List.of("安装用户"), + List.of("数据库用户名"), List.of("数据库密码"), List.of("端口号"), List.of("环境变量路径"), + List.of("执行状态"), List.of("报错信息"))); + } +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/ClusterOpsProviderManager.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/ClusterOpsProviderManager.java new file mode 100644 index 000000000..b7fbd3cf2 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/ClusterOpsProviderManager.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterOpsProviderManager.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/ClusterOpsProviderManager.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.service.ops.ClusterOpsProvider; +import org.springframework.stereotype.Component; + +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Cluster Installation Service Provider Manager + * + * @author lhf + * @date 2022/8/12 09:08 + **/ +@Component +public class ClusterOpsProviderManager { + private static final ConcurrentHashMap REGISTRY = new ConcurrentHashMap<>(); + + public static void registry(OpenGaussVersionEnum version, ClusterOpsProvider provider) { + REGISTRY.put(version.name(), provider); + } + + + public Optional provider(OpenGaussVersionEnum version) { + return Optional.ofNullable(REGISTRY.get(version.name())); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/ClusterTaskProviderManager.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/ClusterTaskProviderManager.java new file mode 100644 index 000000000..68dd47621 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/ClusterTaskProviderManager.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterTaskProviderManager.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/ClusterTaskProviderManager.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.service.ops.ClusterTaskProvider; +import org.springframework.stereotype.Component; + +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Cluster Installation Service Provider Manager + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Component +public class ClusterTaskProviderManager { + private static final ConcurrentHashMap REGISTRY = new ConcurrentHashMap<>(); + + /** + * registry the provider of the specified version + * + * @param version version + * @param provider provider + */ + public static void registry(OpenGaussVersionEnum version, ClusterTaskProvider provider) { + REGISTRY.put(version, provider); + } + + /** + * get the provider of the specified version + * + * @param version version + * @return provider + */ + public Optional provider(OpenGaussVersionEnum version) { + return Optional.ofNullable(REGISTRY.get(version)); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/HostServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/HostServiceImpl.java new file mode 100644 index 000000000..609f78c3d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/HostServiceImpl.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * HostServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/HostServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import com.jcraft.jsch.Session; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.enums.ops.DiskQueryMethodEnum; +import org.opengauss.admin.plugin.domain.model.ops.JschResult; +import org.opengauss.admin.plugin.domain.model.ops.LunPathManager; +import org.opengauss.admin.plugin.domain.model.ops.SshCommandConstants; +import org.opengauss.admin.plugin.service.ops.IHostService; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * @author lhf + * @date 2023/2/14 14:28 + **/ +@Slf4j +@Service +public class HostServiceImpl implements IHostService { + @Autowired + private JschUtil jschUtil; + + @Resource + private OpsHostRemoteService opsHostRemoteService; + + @Override + public boolean pathEmpty(String id, String path, String rootPassword) { + return opsHostRemoteService.pathEmpty(id, path); + } + + @Override + public boolean portUsed(String id, Integer port, String rootPassword) { + return opsHostRemoteService.portUsed(id, port, rootPassword); + } + + @Override + public boolean fileExist(String id, String file, String rootPassword) { + return opsHostRemoteService.checkFileExist(id, file); + } + + private List nvmeLunQuery(Session rootSession, String command) { + List res = new ArrayList<>(); + try { + JschResult result = jschUtil.executeCommand(command, rootSession); + if (result.getExitCode() != 0 || result.getResult().contains("command not found")) { + log.warn(result.getResult()); + return res; + } + String[] parts = result.getResult().split(System.lineSeparator()); + res = Arrays.asList(parts); + return res; + } catch (Exception e) { + log.warn("Can't execute command ", e); + } + return res; + } + + private List nvmeLunQuery(Session rootSession) { + List res = nvmeLunQuery(rootSession, SshCommandConstants.UPADMIN); + if (!res.isEmpty()) { + LunPathManager.lunQueryMethod = DiskQueryMethodEnum.UPADMIN; + return res; + } + res = nvmeLunQuery(rootSession, SshCommandConstants.UPADMIN_PLUS); + if (!res.isEmpty()) { + LunPathManager.lunQueryMethod = DiskQueryMethodEnum.UPADMIN_PLUS; + return res; + + } + return res; + } + + private List scsiLunQuery(Session rootSession) { + List res = new ArrayList<>(); + try { + JschResult result = jschUtil.executeCommand(SshCommandConstants.LS_SCSI, rootSession); + if (result.getExitCode() != 0 || result.getResult().contains("command not found")) { + log.error(result.getResult()); + return res; + } + String[] parts = result.getResult().split(System.lineSeparator()); + for (String part : parts) { + if (part.split(" ")[1].length() == LunPathManager.WWN_LEN_IN_HEX + 1) { + res.add(part); + } + } + return res; + } catch (Exception e) { + log.error("Can't execute command ", e); + return res; + } + } + + @Override + public List scsiLunQuery(String id, String rootPassword) { + Session userSession = opsHostRemoteService.createPluginSessionWithRootOrNormalUser(id); + try { + return scsiLunQuery(userSession); + } finally { + if (Objects.nonNull(userSession) && userSession.isConnected()) { + userSession.disconnect(); + } + } + } + + @Override + public List nvmeLunQuery(String id, String rootPassword) { + Session userSession = opsHostRemoteService.createPluginSessionWithRootOrNormalUser(id); + try { + return nvmeLunQuery(userSession); + } finally { + if (Objects.nonNull(userSession) && userSession.isConnected()) { + userSession.disconnect(); + } + } + } + + @Override + public List multiPathQuery(String id, String rootPassword) { + try { + List res = nvmeLunQuery(id, rootPassword); + if (!res.isEmpty()) { + return res; + } else { + res = scsiLunQuery(id, rootPassword); + if (!res.isEmpty()) { + LunPathManager.lunQueryMethod = DiskQueryMethodEnum.SCSI; + return res; + } + } + log.error("Can't obtain the disk info by {}, {} and {} ", SshCommandConstants.UPADMIN, + SshCommandConstants.UPADMIN_PLUS, SshCommandConstants.LS_SCSI); + throw new OpsException("Failed to obtain the disk info"); + } catch (Exception e) { + log.error("Can't execute command ", e); + throw new OpsException("Failed to obtain the disk info"); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/ImportClusterService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/ImportClusterService.java new file mode 100644 index 000000000..906861c11 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/ImportClusterService.java @@ -0,0 +1,310 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ImportClusterService.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/ImportClusterService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.EnterpriseInstallConfig; +import org.opengauss.admin.plugin.domain.model.ops.ImportClusterBody; +import org.opengauss.admin.plugin.domain.model.ops.LiteInstallConfig; +import org.opengauss.admin.plugin.domain.model.ops.MinimalistInstallConfig; +import org.opengauss.admin.plugin.domain.model.ops.node.EnterpriseInstallNodeConfig; +import org.opengauss.admin.plugin.domain.model.ops.node.LiteInstallNodeConfig; +import org.opengauss.admin.plugin.domain.model.ops.node.MinimalistInstallNodeConfig; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.mapper.ops.OpsClusterMapper; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.plugin.utils.DBUtil; +import org.opengauss.admin.plugin.utils.DecryptionUtil; +import org.opengauss.admin.system.plugin.beans.SshLogin; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; +import org.opengauss.admin.system.plugin.facade.JschExecutorFacade; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.Objects; + +import jakarta.annotation.Resource; + +/** + * ImportClusterService + * + * @author: wangchao + * @Date: 2024/11/4 12:02 + * @Description: ImportClusterService + * @since 7.0.0 + **/ +@Slf4j +@Service +public class ImportClusterService extends ServiceImpl { + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostUserFacade hostUserFacade; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostFacade hostFacade; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + @Autowired + private DecryptionUtil decryptionUtil; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private JschExecutorFacade jschExecutorFacade; + @Resource + private DBUtil dbUtil; + @Resource + private IOpsClusterNodeService opsClusterNodeService; + + /** + * importCluster + * + * @param importClusterBody importClusterBody + */ + public void importCluster(ImportClusterBody importClusterBody) { + importClusterBody.checkConfig(); + decryptionUtil.decryptDatabasePassword(importClusterBody); + OpenGaussVersionEnum openGaussVersion = importClusterBody.getOpenGaussVersion(); + ClusterConfig clusterConfig = getClusterConfig(importClusterBody, openGaussVersion); + OpsHostUserEntity masterNodeInstallUser = getMasterNodeInstallUser(clusterConfig.masterNodeInstallUserId); + OpsHostEntity hostEntity = getHostEntity(clusterConfig.masterHostId); + try (Connection connection = createConnection(hostEntity, clusterConfig.port, clusterConfig.databaseUsername, + clusterConfig.databasePassword)) { + SshLogin sshLogin = new SshLogin(hostEntity.getPublicIp(), hostEntity.getPort(), + masterNodeInstallUser.getUsername(), encryptionUtils.decrypt(masterNodeInstallUser.getPassword())); + String versionNum = getVersionNum(sshLogin, importClusterBody.getEnvPath()); + importClusterBody.setOpenGaussVersionNum(versionNum); + Integer majorVersion = Integer.valueOf(versionNum.substring(0, 1)); + OpenGaussVersionEnum actualVersion = judgeOpenGaussVersion(majorVersion, sshLogin, connection, + importClusterBody.getEnvPath()); + validateVersion(majorVersion, importClusterBody.getOpenGaussVersion(), actualVersion); + } catch (OpsException e) { + log.error("ops exception ", e); + throw e; + } catch (Exception e) { + log.error("get connection fail", e); + throw new OpsException("connection fail:" + e.getMessage()); + } + OpsClusterEntity opsClusterEntity = importClusterBody.toOpsClusterEntity(); + opsClusterEntity.setDatabasePassword(encryptionUtils.encrypt(opsClusterEntity.getDatabasePassword())); + save(opsClusterEntity); + saveClusterNodes(importClusterBody, opsClusterEntity); + } + + private OpsHostUserEntity getMasterNodeInstallUser(String masterNodeInstallUserId) { + OpsHostUserEntity masterNodeInstallUser = hostUserFacade.getById(masterNodeInstallUserId); + if (Objects.isNull(masterNodeInstallUser)) { + throw new OpsException("install user not found"); + } + return masterNodeInstallUser; + } + + private OpsHostEntity getHostEntity(String masterHostId) { + OpsHostEntity hostEntity = hostFacade.getById(masterHostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host not found"); + } + return hostEntity; + } + + private Connection createConnection(OpsHostEntity hostEntity, Integer port, String databaseUsername, + String databasePassword) throws OpsException, SQLException, ClassNotFoundException { + return dbUtil.getSession(hostEntity.getPublicIp(), port, databaseUsername, databasePassword) + .orElseThrow(() -> new OpsException("Connection failed")); + } + + private String getVersionNum(SshLogin sshLogin, String envPath) { + return jschExecutorFacade.getOpenGaussMainVersionNum(sshLogin, envPath); + } + + private OpenGaussVersionEnum judgeOpenGaussVersion(Integer majorVersion, SshLogin sshLogin, Connection connection, + String envPath) { + boolean isEnterprise = enterpriseVersion(sshLogin, envPath); + if (isEnterprise) { + return OpenGaussVersionEnum.ENTERPRISE; + } + if (majorVersion >= 5) { + boolean isLite = liteVersion(connection); + if (isLite) { + return OpenGaussVersionEnum.LITE; + } + return OpenGaussVersionEnum.MINIMAL_LIST; + } else { + return OpenGaussVersionEnum.MINIMAL_LIST; + } + } + + private boolean enterpriseVersion(SshLogin sshLogin, String envPath) { + String command = "which gs_om"; + try { + String jschResult = jschExecutorFacade.execCommand(sshLogin, command, envPath); + if (jschResult.contains("script/gs_om")) { + return true; + } + if (jschResult.contains("command not found")) { + throw new OpsException("Failed to check openGauss version, detail: " + jschResult); + } + } catch (OpsException opsException) { + log.error("select enterprise version command error:", opsException); + } + return false; + } + + private boolean liteVersion(Connection connection) { + String sql = "select version()"; + try (PreparedStatement preparedStatement = connection.prepareStatement(sql); + ResultSet resultSet = preparedStatement.executeQuery()) { + if (resultSet.next()) { + String version = resultSet.getString("version"); + return version.contains("lite"); + } + } catch (SQLException e) { + log.error("query version fail", e); + throw new OpsException("query version fail:" + e); + } + return false; + } + + private ClusterConfig getClusterConfig(ImportClusterBody importClusterBody, OpenGaussVersionEnum openGaussVersion) { + if (Objects.equals(openGaussVersion, OpenGaussVersionEnum.MINIMAL_LIST)) { + return new ClusterConfig(importClusterBody.getMinimalistInstallConfig()); + } else if (Objects.equals(openGaussVersion, OpenGaussVersionEnum.LITE)) { + return new ClusterConfig(importClusterBody.getLiteInstallConfig()); + } else if (Objects.equals(openGaussVersion, OpenGaussVersionEnum.ENTERPRISE)) { + return new ClusterConfig(importClusterBody.getEnterpriseInstallConfig()); + } else { + throw new OpsException("Unsupported OpenGauss version"); + } + } + + private void validateVersion(Integer majorVersion, OpenGaussVersionEnum selectedVersion, + OpenGaussVersionEnum actualVersion) throws OpsException { + boolean isVersionMatch = false; + if (majorVersion >= 5) { + if (Objects.equals(selectedVersion, actualVersion)) { + isVersionMatch = true; + } + } else { + if (Objects.equals(selectedVersion, OpenGaussVersionEnum.ENTERPRISE)) { + if (Objects.equals(actualVersion, OpenGaussVersionEnum.ENTERPRISE)) { + isVersionMatch = true; + } + } else { + if (!Objects.equals(actualVersion, OpenGaussVersionEnum.ENTERPRISE)) { + isVersionMatch = true; + } + } + } + if (!selectedVersion.equals(actualVersion)) { + log.error("The selected version does not match the actual version, select version:{}, actual version:{}", + selectedVersion, actualVersion); + throw new OpsException("The selected version does not match the actual version"); + } + } + + private void saveClusterNodes(ImportClusterBody importClusterBody, OpsClusterEntity opsClusterEntity) { + List opsClusterNodeEntities = toOpsClusterNodeEntityList(importClusterBody, + opsClusterEntity); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + opsClusterNodeEntity.setClusterId(opsClusterEntity.getClusterId()); + } + opsClusterNodeService.saveBatch(opsClusterNodeEntities); + } + + private List toOpsClusterNodeEntityList(ImportClusterBody importClusterBody, + OpsClusterEntity opsClusterEntity) { + if (Objects.equals(OpenGaussVersionEnum.LITE, importClusterBody.getOpenGaussVersion())) { + return importClusterBody.getLiteInstallConfig().toOpsClusterNodeEntityList(); + } else if (Objects.equals(OpenGaussVersionEnum.MINIMAL_LIST, importClusterBody.getOpenGaussVersion())) { + return importClusterBody.getMinimalistInstallConfig().toOpsClusterNodeEntityList(opsClusterEntity); + } else if (Objects.equals(OpenGaussVersionEnum.ENTERPRISE, importClusterBody.getOpenGaussVersion())) { + return importClusterBody.getEnterpriseInstallConfig().toOpsClusterNodeEntityList(); + } else { + throw new OpsException("Unsupported openGauss version: " + importClusterBody.getOpenGaussVersion()); + } + } + + private class ClusterConfig { + Integer port; + String databaseUsername; + String databasePassword; + String masterHostId; + String masterNodeInstallUserId; + + ClusterConfig(MinimalistInstallConfig config) { + this.port = config.getPort(); + this.databaseUsername = config.getDatabaseUsername(); + this.databasePassword = config.getDatabasePassword(); + MinimalistInstallNodeConfig masterNode = config.getNodeConfigList() + .stream() + .filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER) + .findFirst() + .orElseThrow(() -> new OpsException("masternode not found")); + this.masterHostId = masterNode.getHostId(); + this.masterNodeInstallUserId = masterNode.getInstallUserId(); + } + + ClusterConfig(LiteInstallConfig config) { + this.port = config.getPort(); + this.databaseUsername = config.getDatabaseUsername(); + this.databasePassword = config.getDatabasePassword(); + LiteInstallNodeConfig masterNode = config.getNodeConfigList() + .stream() + .filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER) + .findFirst() + .orElseThrow(() -> new OpsException("masternode not found")); + this.masterHostId = masterNode.getHostId(); + this.masterNodeInstallUserId = masterNode.getInstallUserId(); + } + + ClusterConfig(EnterpriseInstallConfig config) { + this.port = config.getPort(); + this.databaseUsername = config.getDatabaseUsername(); + this.databasePassword = config.getDatabasePassword(); + EnterpriseInstallNodeConfig masterNode = config.getNodeConfigList() + .stream() + .filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER) + .findFirst() + .orElseThrow(() -> new OpsException("masternode not found")); + this.masterHostId = masterNode.getHostId(); + this.masterNodeInstallUserId = masterNode.getInstallUserId(); + } + } +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsBackupService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsBackupService.java new file mode 100644 index 000000000..9a553ec61 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsBackupService.java @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsBackupService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsBackupService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsBackupEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.JschResult; +import org.opengauss.admin.plugin.domain.model.ops.OpsClusterBody; +import org.opengauss.admin.plugin.domain.model.ops.SshCommandConstants; +import org.opengauss.admin.plugin.domain.model.ops.WsSession; +import org.opengauss.admin.plugin.domain.model.ops.cache.TaskManager; +import org.opengauss.admin.plugin.domain.model.ops.cache.WsConnectorManager; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.mapper.ops.OpsBackupMapper; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterService; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.plugin.utils.WsUtil; +import org.opengauss.admin.plugin.vo.ops.BackupInputDto; +import org.opengauss.admin.plugin.vo.ops.BackupVO; +import org.opengauss.admin.plugin.vo.ops.RecoverInputDto; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import com.jcraft.jsch.Session; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.plugin.service.ops.IOpsBackupService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +import java.text.MessageFormat; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Future; + +/** + * @author lhf + * @date 2022/11/5 09:48 + **/ +@Slf4j +@Service +public class OpsBackupService extends ServiceImpl implements IOpsBackupService { + @Autowired + private IOpsClusterService clusterService; + @Autowired + private IOpsClusterNodeService clusterNodeService; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostFacade hostFacade; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostUserFacade hostUserFacade; + @Autowired + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + @Autowired + private OpsBackupMapper backupMapper; + @Autowired + private WsUtil wsUtil; + @Autowired + private JschUtil jschUtil; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + @Autowired + private WsConnectorManager wsConnectorManager; + @Override + public void backup(BackupInputDto backup) { + String clusterId = backup.getClusterId(); + + OpsClusterEntity clusterEntity = clusterService.getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + + List opsClusterNodeEntities = clusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("Cluster node information does not exist"); + } + + OpsClusterNodeEntity masterNodeEntity = opsClusterNodeEntities.stream().filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER).findFirst().orElseThrow(() -> new OpsException("Node information not found")); + String hostId = masterNodeEntity.getHostId(); + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("Master node host information not found"); + } + + OpsHostUserEntity installUserEntity = hostUserFacade.getById(masterNodeEntity.getInstallUserId()); + if (Objects.isNull(installUserEntity)) { + throw new OpsException("Master node installation user information not found"); + } + + WsSession retWsSession = wsConnectorManager.getSession(backup.getBusinessId()).orElseThrow(()->new OpsException("websocket session not exist")); + RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + RequestContextHolder.setRequestAttributes(context); + + try { + String name = StrUtil.uuid() + ".sql"; + doBackUp(clusterEntity, hostEntity, installUserEntity, backup, retWsSession, name,clusterEntity.getEnvPath()); + wsUtil.sendText(retWsSession, "FINAL_EXECUTE_EXIT_CODE:0"); + + OpsBackupEntity opsBackupEntity = new OpsBackupEntity(); + opsBackupEntity.setClusterId(clusterId); + opsBackupEntity.setHostId(hostId); + opsBackupEntity.setClusterNodeId(masterNodeEntity.getClusterNodeId()); + opsBackupEntity.setRemark(backup.getRemark()); + opsBackupEntity.setBackupPath(backup.getBackupPath() + "/" + name); + opsBackupEntity.setCreateTime(new Date()); + save(opsBackupEntity); + } catch (Exception e) { + log.error("Backup failed:", e); + wsUtil.sendText(retWsSession, "FINAL_EXECUTE_EXIT_CODE:-1"); + } + }); + + TaskManager.registry(backup.getBusinessId(), future); + } + + @Override + public void recover(String id, RecoverInputDto recover) { + OpsBackupEntity backupEntity = getById(id); + if (Objects.isNull(backupEntity)) { + throw new OpsException("Backup information does not exist"); + } + + String clusterId = backupEntity.getClusterId(); + OpsClusterEntity clusterEntity = clusterService.getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + + String hostId = backupEntity.getHostId(); + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + + String clusterNodeId = backupEntity.getClusterNodeId(); + OpsClusterNodeEntity clusterNodeEntity = clusterNodeService.getById(clusterNodeId); + if (Objects.isNull(clusterNodeEntity)) { + throw new OpsException("Cluster node information does not exist"); + } + + OpsHostUserEntity rootUserEntity = hostUserFacade.getRootUserByHostId(hostId); + if (Objects.isNull(rootUserEntity)) { + throw new OpsException("root user information not found"); + } + + OpsHostUserEntity installUserEntity = hostUserFacade.getById(clusterNodeEntity.getInstallUserId()); + if (Objects.isNull(installUserEntity)) { + throw new OpsException("Installation user does not exist"); + } + + WsSession retWsSession = wsConnectorManager.getSession(recover.getBusinessId()).orElseThrow(()->new OpsException("websocket session not exist")); + RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + RequestContextHolder.setRequestAttributes(context); + + try { + doRecover(backupEntity, clusterEntity, clusterNodeEntity, hostEntity, rootUserEntity, installUserEntity,recover.getBusinessId(), retWsSession); + wsUtil.sendText(retWsSession, "FINAL_EXECUTE_EXIT_CODE:0"); + } catch (Exception e) { + log.error("Recovery failed:", e); + wsUtil.sendText(retWsSession, "FINAL_EXECUTE_EXIT_CODE:-1"); + } + }); + + TaskManager.registry(recover.getBusinessId(), future); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void del(String id) { + OpsBackupEntity backupEntity = getById(id); + if (Objects.isNull(backupEntity)) { + throw new OpsException("Backup information does not exist"); + } + + String clusterId = backupEntity.getClusterId(); + OpsClusterEntity clusterEntity = clusterService.getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + + String hostId = backupEntity.getHostId(); + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + + String clusterNodeId = backupEntity.getClusterNodeId(); + OpsClusterNodeEntity clusterNodeEntity = clusterNodeService.getById(clusterNodeId); + if (Objects.isNull(clusterNodeEntity)) { + throw new OpsException("Cluster node information does not exist"); + } + + OpsHostUserEntity installUserEntity = hostUserFacade.getById(clusterNodeEntity.getInstallUserId()); + if (Objects.isNull(installUserEntity)) { + throw new OpsException("install user information not found"); + } + + doDel(backupEntity, hostEntity, installUserEntity); + + removeById(backupEntity); + } + + @Override + public Page pageBackup(Page page, String clusterId) { + return backupMapper.pageBackup(page,clusterId); + } + + private void doDel(OpsBackupEntity backupEntity, OpsHostEntity hostEntity, OpsHostUserEntity installUserEntity) { + Session installUserSession = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), installUserEntity.getUsername(), encryptionUtils.decrypt(installUserEntity.getPassword())).orElseThrow(() -> new OpsException("Failed to establish connection with host")); + + try { + String command = "rm -rf " + backupEntity.getBackupPath(); + JschResult jschResult = jschUtil.executeCommand(command, installUserSession); + if (0 != jschResult.getExitCode()) { + log.error("Failed to delete backup,exitCode:{},msg:{}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to delete backup"); + } + } catch (Exception e) { + log.error("Failed to delete backup", e); + throw new OpsException("Failed to delete backup"); + }finally { + if (Objects.nonNull(installUserSession) && installUserSession.isConnected()){ + installUserSession.disconnect(); + } + } + } + + private void doRecover(OpsBackupEntity backupEntity, OpsClusterEntity clusterEntity, OpsClusterNodeEntity clusterNodeEntity, OpsHostEntity hostEntity, OpsHostUserEntity rootUserEntity, OpsHostUserEntity installUserEntity, String businessId, WsSession retWsSession) { + + Session installUserSession = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), installUserEntity.getUsername(), encryptionUtils.decrypt(installUserEntity.getPassword())).orElseThrow(() -> new OpsException("Failed to establish connection with host")); + try { + String chmodNewDataPath = String.format("gsql -d postgres -p %s -W %s -f %s", + clusterEntity.getPort(), encryptionUtils.decrypt(clusterEntity.getDatabasePassword()), + backupEntity.getBackupPath()); + JschResult jschResult = jschUtil.executeCommand(chmodNewDataPath, clusterEntity.getEnvPath(), installUserSession, retWsSession); + if (0 != jschResult.getExitCode()) { + log.error("Recovery failed,exitCode:{},msg:{}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Recovery failed"); + } + } catch (Exception e) { + log.error("Recovery failed", e); + throw new OpsException("Recovery failed"); + }finally { + if (Objects.nonNull(installUserSession) && installUserSession.isConnected()){ + installUserSession.disconnect(); + } + } + + OpsClusterBody opsClusterBody = new OpsClusterBody(); + opsClusterBody.setClusterId(clusterEntity.getClusterId()); + opsClusterBody.setBusinessId(businessId); + opsClusterBody.setSync(Boolean.TRUE); + clusterService.restart(opsClusterBody); + } + + private void doBackUp(OpsClusterEntity clusterEntity, OpsHostEntity hostEntity, OpsHostUserEntity installUserEntity, BackupInputDto backup, WsSession retWsSession, String name, String envPath) { + Session rootSession = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), installUserEntity.getUsername(), encryptionUtils.decrypt(installUserEntity.getPassword())).orElseThrow(() -> new OpsException("Failed to establish connection with host")); + + try { + String createPathCommand = MessageFormat.format(SshCommandConstants.CREATE_DIR_AND_CHECK_WRITE_PERM, + backup.getBackupPath(), "not allow write backup file"); + JschResult jschResult = jschUtil.executeCommand(createPathCommand, rootSession, retWsSession); + if (jschResult.getExitCode() != 0 || jschResult.getResult().contains("not allow write backup file")) { + log.error("Failed to create backup directory,exitCode:{},msg:{}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to create backup directory"); + } + } catch (Exception e) { + log.error("Failed to create backup directory", e); + throw new OpsException("Failed to create backup directory"); + } finally { + if (Objects.nonNull(rootSession) && rootSession.isConnected()) { + rootSession.disconnect(); + } + } + + Session installSession = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), installUserEntity.getUsername(), encryptionUtils.decrypt(installUserEntity.getPassword())).orElseThrow(() -> new OpsException("Failed to connect to master node host")); + try { + String backupCommand = "gs_dumpall -p " + clusterEntity.getPort() + " -f " + backup.getBackupPath() + "/" + name; + JschResult jschResult = jschUtil.executeCommand(backupCommand, envPath, installSession, retWsSession); + if (jschResult.getExitCode() != 0) { + log.error("Backup failed,exitCode:{},msg:{}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Backup failed"); + } + } catch (Exception e) { + log.error("Backup failed", e); + throw new OpsException("Backup failed"); + } finally { + if (Objects.nonNull(installSession) && installSession.isConnected()) { + installSession.disconnect(); + } + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsCheckService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsCheckService.java new file mode 100644 index 000000000..6d59e4430 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsCheckService.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsCheckService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsCheckService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.plugin.domain.entity.ops.OpsCheckEntity; +import org.opengauss.admin.plugin.mapper.ops.OpsCheckMapper; +import org.opengauss.admin.plugin.service.ops.IOpsCheckService; +import org.springframework.stereotype.Service; + +/** + * @author lhf + * @date 2022/11/13 17:17 + **/ +@Service +public class OpsCheckService extends ServiceImpl implements IOpsCheckService { + @Override + public OpsCheckEntity getLastResByClusterId(String clusterId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsCheckEntity.class) + .eq(OpsCheckEntity::getClusterId, clusterId) + .orderByDesc(OpsCheckEntity::getCreateTime); + + return getOne(queryWrapper, false); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterEnvService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterEnvService.java new file mode 100644 index 000000000..57a93da1f --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterEnvService.java @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterEnvService.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterEnvService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.util.StrUtil; + +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.jcraft.jsch.Session; + +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.StringUtils; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.constant.ops.SshCommandConstants; +import org.opengauss.admin.plugin.domain.model.ops.env.EnvProperty; +import org.opengauss.admin.plugin.domain.model.ops.env.HardwareEnv; +import org.opengauss.admin.plugin.domain.model.ops.env.HostEnv; +import org.opengauss.admin.plugin.domain.model.ops.env.SoftwareEnv; +import org.opengauss.admin.plugin.enums.ops.HostEnvStatusEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussSupportOSEnum; +import org.opengauss.admin.plugin.utils.OpsAssert; +import org.opengauss.admin.system.plugin.facade.HostMonitorFacade; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Collections; +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.stream.Collectors; + +/** + * OpsClusterEnvService + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Slf4j +@Service +public class OpsClusterEnvService { + private static final List DEPENDENCY_PACKAGE_NAMES = List.of("libaio-devel", "flex", "bison", + "ncurses-devel", "glibc-devel", "patch", "readline-devel"); + private static final List BASE_DEPENDENCY_LIST = List.of("coreutils", "procps-ng", "openssh-clients", + "unzip", "lsof", "tar"); + private static final List OPENEULER_BASE_DEPENDENCY_LIST = List.of("coreutils", "procps-ng", "openssh", + "unzip", "lsof", "tar"); + + @Resource + private OpsHostRemoteService opsHostRemoteService; + @Resource + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostMonitorFacade hostMonitorFacade; + + /** + * host env check + * + * @param hostId hostId + * @param expectedOs expectedOs + * @param rootPassword rootPassword ignored + * @return HostEnv + */ + public HostEnv env(String hostId, OpenGaussSupportOSEnum expectedOs, String rootPassword) { + OpsHostEntity hostEntity = opsHostRemoteService.getHost(hostId); + return env(hostEntity, expectedOs); + } + + /** + * host env check + * + * @param hostId hostId + * @return HostEnv + */ + public HostEnv env(String hostId) { + OpsHostEntity hostEntity = opsHostRemoteService.getHost(hostId); + OpenGaussSupportOSEnum expectedOs = OpenGaussSupportOSEnum.of(hostEntity.getOs(), hostEntity.getOsVersion(), + hostEntity.getCpuArch()); + return env(hostEntity, expectedOs); + } + + private HostEnv env(OpsHostEntity hostEntity, OpenGaussSupportOSEnum expectedOs) { + // use root check env + HostEnv hostEnv = new HostEnv(); + CountDownLatch countDownLatch = new CountDownLatch(2); + threadPoolTaskExecutor.submit(() -> { + hostEnv.setHardwareEnv(hardwareEnvDetect(hostEntity.getHostId(), expectedOs)); + countDownLatch.countDown(); + }); + threadPoolTaskExecutor.submit(() -> { + OpsHostUserEntity userEntity = opsHostRemoteService.getAnyHostUser(hostEntity.getHostId()); + if (Objects.nonNull(userEntity)) { + Session session = opsHostRemoteService.createHostUserSession(hostEntity, userEntity); + hostEnv.setSoftwareEnv(softwareEnvDetect(session, expectedOs)); + opsHostRemoteService.closeSession(session); + countDownLatch.countDown(); + } else { + countDownLatch.countDown(); + log.error("cluster env software detect failed, host user not found {}", hostEntity.getPublicIp()); + } + }); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + log.error("waiting for thread to be interrupted", e); + } + return hostEnv; + } + + private SoftwareEnv softwareEnvDetect(Session session, OpenGaussSupportOSEnum expectedOs) { + SoftwareEnv softwareEnv = new SoftwareEnv(); + List envProperties = new CopyOnWriteArrayList<>(); + softwareEnv.setEnvProperties(envProperties); + envProperties.add(dependencyPropertyDetect(session, expectedOs.getCpuArch())); + envProperties.add(firewallPropertyDetect(session)); + envProperties.add(installUserPropertyDetect(session)); + envProperties.add(otherPropertyDetect(session, expectedOs)); + envProperties.sort(Comparator.comparingInt(EnvProperty::getSortNum)); + return softwareEnv; + } + + private EnvProperty otherPropertyDetect(Session session, OpenGaussSupportOSEnum expectedOs) { + EnvProperty otherProperty; + String cpuArch = expectedOs.getCpuArch(); + if (StrUtil.equals(expectedOs.getOsId(), OpenGaussSupportOSEnum.CENTOS_X86_64.getOsId())) { + otherProperty = getEnvProperty(session, cpuArch, SshCommandConstants.BASE_DEPENDENCY, BASE_DEPENDENCY_LIST); + } else { + otherProperty = getEnvProperty(session, cpuArch, SshCommandConstants.OPENEULER_BASE_DEPENDENCY, + OPENEULER_BASE_DEPENDENCY_LIST); + } + otherProperty.setName("other"); + otherProperty.setSortNum(4); + return otherProperty; + } + + private EnvProperty installUserPropertyDetect(Session session) { + EnvProperty installUserProperty = new EnvProperty(); + installUserProperty.setName("install user"); + installUserProperty.setSortNum(3); + installUserProperty.setStatus(HostEnvStatusEnum.NORMAL); + return installUserProperty; + } + + private EnvProperty firewallPropertyDetect(Session session) { + EnvProperty firewallProperty = new EnvProperty(); + firewallProperty.setName("firewall"); + firewallProperty.setSortNum(2); + try { + String firewallStatus = opsHostRemoteService.executeCommand(SshCommandConstants.FIREWALL, session, + "firewall"); + if ("inactive".equals(firewallStatus)) { + firewallProperty.setStatus(HostEnvStatusEnum.NORMAL); + } else { + firewallProperty.setStatus(HostEnvStatusEnum.ERROR); + firewallProperty.setStatusMessage("Please turn off the firewall"); + } + } catch (Exception e) { + log.error("Parse command response error", e); + firewallProperty.setStatus(HostEnvStatusEnum.ERROR); + firewallProperty.setStatusMessage("unknown error,please retry to check the firewall status"); + } + return firewallProperty; + } + + private EnvProperty dependencyPropertyDetect(Session session, String cpuArch) { + EnvProperty dependencyProperty = getEnvProperty(session, cpuArch, SshCommandConstants.DEPENDENCY, + DEPENDENCY_PACKAGE_NAMES); + dependencyProperty.setName("software dependency"); + dependencyProperty.setSortNum(1); + return dependencyProperty; + } + + private EnvProperty getEnvProperty(Session session, String cpuArch, String queryCommand, + List dependencies) { + EnvProperty envProperty = new EnvProperty(); + List notInstalledPackages = getMissingList(session, cpuArch, queryCommand, dependencies); + if (CollectionUtils.isEmpty(notInstalledPackages)) { + envProperty.setStatus(HostEnvStatusEnum.NORMAL); + } else { + envProperty.setStatus(HostEnvStatusEnum.ERROR); + envProperty.setStatusMessage("not installed dependencies:" + StringUtils.join(notInstalledPackages, ",")); + } + return envProperty; + } + + /** + * get uninstalled dependencies + * + * @param session session + * @param cpuArch session + * @param queryCommand queryCommand + * @param dependencies dependencies + * @return uninstalled dependencies + */ + public List getMissingList(Session session, String cpuArch, String queryCommand, + List dependencies) { + try { + String queryResult = opsHostRemoteService.executeCommand(queryCommand, session, "check dependencies"); + List dependencyPackages = dependencies.stream() + .map(dependency -> dependency + "." + cpuArch) + .collect(Collectors.toList()); + List notInstalledPackages = new ArrayList<>(); + for (String dependencyPackage : dependencyPackages) { + if (!queryResult.contains(dependencyPackage)) { + notInstalledPackages.add(dependencyPackage); + } + } + return notInstalledPackages; + } catch (OpsException e) { + return Collections.singletonList(e.getMessage()); + } + } + + private HardwareEnv hardwareEnvDetect(String hostId, OpenGaussSupportOSEnum expectedOs) { + HardwareEnv hardwareEnv = new HardwareEnv(); + List envProperties = new CopyOnWriteArrayList<>(); + hardwareEnv.setEnvProperties(envProperties); + envProperties.add(osPropertyDetect(hostId, expectedOs)); + envProperties.add(osVersionPropertyDetect(hostId)); + envProperties.add(freeMemoryPropertyDetect(hostId)); + envProperties.add(cpuCoreNumPropertyDetect(hostId)); + envProperties.add(cpuFrequencyPropertyDetect(hostId)); + envProperties.add(freeHardDiskPropertyDetect(hostId)); + envProperties.sort(Comparator.comparingInt(EnvProperty::getSortNum)); + return hardwareEnv; + } + + private EnvProperty freeHardDiskPropertyDetect(String hostId) { + EnvProperty freeHardDiskProperty = new EnvProperty(); + freeHardDiskProperty.setName("free hard disk space"); + freeHardDiskProperty.setSortNum(6); + try { + String freeHardDisk = hostMonitorFacade.getAvailableDiskSpace(hostId); + OpsAssert.isTrue(StrUtil.isNotEmpty(freeHardDisk), "free disk space is empty"); + int freeHardDiskGB = calcDisk(freeHardDisk); + freeHardDiskProperty.setValue(freeHardDiskGB + "G"); + freeHardDiskProperty.setStatus(HostEnvStatusEnum.NORMAL); + int suggestedNum = 2; + if (freeHardDiskGB < suggestedNum) { + freeHardDiskProperty.setStatus(HostEnvStatusEnum.ERROR); + freeHardDiskProperty.setStatusMessage("min 2.0GB"); + } + } catch (Exception e) { + log.error("Parse command response error", e); + freeHardDiskProperty.setStatus(HostEnvStatusEnum.ERROR); + freeHardDiskProperty.setStatusMessage("unknown error,please retry to checked"); + } + return freeHardDiskProperty; + } + + private int calcDisk(String freeHardDisk) { + return (int) Float.parseFloat(freeHardDisk); + } + + private EnvProperty cpuFrequencyPropertyDetect(String hostId) { + EnvProperty cpuFrequencyProperty = new EnvProperty(); + cpuFrequencyProperty.setName("CPU frequency"); + cpuFrequencyProperty.setSortNum(5); + try { + String cpuFrequency = hostMonitorFacade.getCpuFrequency(hostId); + cpuFrequencyProperty.setValue(cpuFrequency); + OpsAssert.isTrue(StrUtil.isNotEmpty(cpuFrequency), "CPU frequency is empty"); + double cpuCoreNum = Double.parseDouble(cpuFrequency.substring(0, cpuFrequency.length() - 3)); + cpuFrequencyProperty.setStatus(HostEnvStatusEnum.NORMAL); + int suggestedNum = 2; + if (cpuCoreNum < suggestedNum) { + cpuFrequencyProperty.setStatus(HostEnvStatusEnum.WARMING); + cpuFrequencyProperty.setStatusMessage("min 2.0GHz"); + } + } catch (Exception e) { + log.error("Parse command response error", e); + cpuFrequencyProperty.setStatus(HostEnvStatusEnum.WARMING); + cpuFrequencyProperty.setStatusMessage("unknown error,please retry to checked"); + } + return cpuFrequencyProperty; + } + + private EnvProperty cpuCoreNumPropertyDetect(String hostId) { + EnvProperty cpuCoreNumProperty = new EnvProperty(); + cpuCoreNumProperty.setName("Number of CPU cores"); + cpuCoreNumProperty.setSortNum(4); + try { + String cpuCore = hostMonitorFacade.getCpuCoreNum(hostId); + OpsAssert.isTrue(StrUtil.isNotEmpty(cpuCore), "Number of CPU cores is empty"); + cpuCoreNumProperty.setValue(cpuCore); + int cpuCoreNum = Integer.parseInt(cpuCore); + int suggestedNum = 8; + int minNum = 2; + if (cpuCoreNum >= suggestedNum) { + cpuCoreNumProperty.setStatus(HostEnvStatusEnum.NORMAL); + } else if (cpuCoreNum >= minNum && cpuCoreNum < suggestedNum) { + cpuCoreNumProperty.setStatus(HostEnvStatusEnum.WARMING); + cpuCoreNumProperty.setStatusMessage("In performance tests and commercial deployment, " + + "it is recommended that one 16-core 2.0 GHz CPU be used."); + } else { + cpuCoreNumProperty.setStatus(HostEnvStatusEnum.ERROR); + cpuCoreNumProperty.setStatusMessage("For individual developers, the minimum configuration is 2 cores ," + + " and the recommended configuration is 4 cores ."); + } + } catch (Exception e) { + log.error("Parse command response error", e); + cpuCoreNumProperty.setStatus(HostEnvStatusEnum.ERROR); + cpuCoreNumProperty.setStatusMessage("unknown error, retry to checked environment"); + } + return cpuCoreNumProperty; + } + + private EnvProperty freeMemoryPropertyDetect(String hostId) { + EnvProperty freeMemoryProperty = new EnvProperty(); + freeMemoryProperty.setName("available memory"); + freeMemoryProperty.setSortNum(3); + try { + String freeMemory = hostMonitorFacade.getRemainingMemory(hostId); + OpsAssert.isTrue(StrUtil.isNotEmpty(freeMemory), "available memory is empty"); + freeMemoryProperty.setValue(freeMemory + "GB"); + int freeMemoryGB = (int) Float.parseFloat(freeMemory); + int suggestedGB = 32; + if (freeMemoryGB >= suggestedGB) { + freeMemoryProperty.setStatus(HostEnvStatusEnum.NORMAL); + } else if (freeMemoryGB > 1 && freeMemoryGB < suggestedGB) { + freeMemoryProperty.setStatus(HostEnvStatusEnum.WARMING); + freeMemoryProperty.setStatusMessage("32GB or more is recommended"); + } else { + freeMemoryProperty.setStatus(HostEnvStatusEnum.ERROR); + freeMemoryProperty.setStatusMessage("2GB or more is recommended"); + } + } catch (Exception e) { + log.error("Parse command response error", e); + freeMemoryProperty.setStatus(HostEnvStatusEnum.ERROR); + freeMemoryProperty.setStatusMessage("unknown error,please retry later"); + } + return freeMemoryProperty; + } + + private EnvProperty osVersionPropertyDetect(String hostId) { + EnvProperty osVersionProperty = new EnvProperty(); + osVersionProperty.setName("operating system version"); + osVersionProperty.setSortNum(2); + try { + String osVersion = hostMonitorFacade.getOsVersion(hostId); + osVersionProperty.setValue(osVersion); + osVersionProperty.setStatus(HostEnvStatusEnum.NORMAL); + } catch (Exception e) { + log.error("Parse command response error", e); + osVersionProperty.setValue("unknown"); + osVersionProperty.setStatus(HostEnvStatusEnum.ERROR); + osVersionProperty.setStatusMessage("Only supports openEuler 20.03LTS and CentOS 7.6 operating systems"); + } + return osVersionProperty; + } + + private EnvProperty osPropertyDetect(String hostId, OpenGaussSupportOSEnum expectedOs) { + EnvProperty osProperty = new EnvProperty(); + osProperty.setName("operating system"); + osProperty.setSortNum(1); + try { + String os = hostMonitorFacade.getOs(hostId); + String cpuArch = hostMonitorFacade.getCpuArch(hostId); + osProperty.setValue(os); + if (expectedOs.match(os, cpuArch)) { + osProperty.setStatus(HostEnvStatusEnum.NORMAL); + } else { + osProperty.setStatus(HostEnvStatusEnum.ERROR); + osProperty.setStatusMessage("The operating system does not match the installation package information"); + } + if (!"centos".equalsIgnoreCase(os.trim())) { + osProperty.setStatus(HostEnvStatusEnum.WARMING); + osProperty.setStatusMessage("Please check if the umask value is 0022"); + } + } catch (Exception e) { + log.error("Parse command response error:", e); + osProperty.setStatus(HostEnvStatusEnum.ERROR); + osProperty.setStatusMessage("Only supports openEuler 20.03LTS and CentOS 7.6 operating systems"); + } + return osProperty; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterLogServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterLogServiceImpl.java new file mode 100644 index 000000000..453caf7ad --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterLogServiceImpl.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterLogServiceImpl.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/OpsClusterLogServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterOperateLog; +import org.opengauss.admin.plugin.enums.ops.ClusterOperateTypeEnum; +import org.opengauss.admin.plugin.mapper.ops.OpsClusterOperateLogMapper; +import org.opengauss.admin.plugin.service.ops.IOpsClusterLogService; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.Objects; + +/** + * OpsClusterLogServiceImpl + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Service("opsClusterLogService") +public class OpsClusterLogServiceImpl implements IOpsClusterLogService { + + @Resource + private OpsClusterOperateLogMapper opsClusterOperateLogMapper; + + @Override + public void insertOperateLog(OpsClusterOperateLog operateLog) { + opsClusterOperateLogMapper.insert(operateLog); + } + + @Override + public IPage selectOperateLogList(IPage page, OpsClusterOperateLog operateLog) { + LambdaQueryWrapper query = new QueryWrapper().lambda(); + query.select(OpsClusterOperateLog::getOperateId, + OpsClusterOperateLog::getClusterId, + OpsClusterOperateLog::getOperateType, + OpsClusterOperateLog::getCreateTime); + query.eq(StrUtil.isNotBlank(operateLog.getClusterId()), OpsClusterOperateLog::getClusterId, operateLog.getClusterId()); + query.eq(Objects.nonNull(operateLog.getOperateType()), OpsClusterOperateLog::getOperateType, operateLog.getOperateType()); + query.orderByDesc(OpsClusterOperateLog::getClusterId); + query.orderByDesc(OpsClusterOperateLog::getCreateTime); + return opsClusterOperateLogMapper.selectPage(page, query); + } + + @Override + public int deleteOperateLogByIds(String[] operateIds) { + return opsClusterOperateLogMapper.deleteBatchIds(List.of(operateIds)); + } + + @Override + public int deleteOperateLogByClusterId(String clusterId) { + LambdaQueryWrapper deleteWrapper = Wrappers.lambdaQuery(OpsClusterOperateLog.class); + deleteWrapper.eq(OpsClusterOperateLog::getClusterId, clusterId); + return opsClusterOperateLogMapper.delete(deleteWrapper); + } + + @Override + public OpsClusterOperateLog selectOperateLogById(String operateId) { + return opsClusterOperateLogMapper.selectById(operateId); + } + + private static final String OPERATE_LOG_SEPARATOR = " -- "; + private static final String OPERATE_LOG_LINE_SEPARATOR = "\\\\r\\\\n"; + + @Override + public String queryClusterOperateLog(String clusterId) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(OpsClusterOperateLog.class); + wrapper.eq(OpsClusterOperateLog::getClusterId, clusterId); + List logList = opsClusterOperateLogMapper.selectList(wrapper); + StringBuffer logBuffer = new StringBuffer(); + logList.forEach(operateLog -> { + logBuffer.append(operateLog.getOperateTime()).append(OPERATE_LOG_SEPARATOR) + .append(operateLog.getOperateType()).append(OPERATE_LOG_SEPARATOR) + .append(convertOperateLog(operateLog.getOperateType(), operateLog.getOperateLog())) + .append(System.lineSeparator()); + }); + return logBuffer.toString(); + } + + private String convertOperateLog(ClusterOperateTypeEnum operateType, String operateLog) { + if (Objects.equals(operateType, ClusterOperateTypeEnum.CHECK_ENVIRONMENT)) { + return formatJsonOperateLog(operateLog); + } else if (Objects.equals(operateType, ClusterOperateTypeEnum.INSTALL)) { + return operateLog.replaceAll(OPERATE_LOG_LINE_SEPARATOR, System.lineSeparator()); + } else { + return operateLog; + } + } + + private static String formatJsonOperateLog(String operateLog) { + try { + return JSONObject.toJSONString(JSONObject.parseObject(operateLog), true); + } catch (Exception e) { + return operateLog; + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterNodeServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterNodeServiceImpl.java new file mode 100644 index 000000000..f3bf88990 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterNodeServiceImpl.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterNodeServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterNodeServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.mapper.ops.OpsClusterNodeMapper; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; + +/** + * @author lhf + * @date 2022/8/18 09:16 + **/ +@Service +public class OpsClusterNodeServiceImpl extends ServiceImpl implements IOpsClusterNodeService { + @Override + public List listClusterNodeByClusterId(String clusterId) { + if (StrUtil.isEmpty(clusterId)) { + return Collections.emptyList(); + } + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsClusterNodeEntity.class) + .eq(OpsClusterNodeEntity::getClusterId, clusterId); + return list(queryWrapper); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterServiceImpl.java new file mode 100644 index 000000000..19d62511a --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterServiceImpl.java @@ -0,0 +1,2366 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Data; + +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.jcraft.jsch.ChannelShell; +import com.jcraft.jsch.Session; + +import org.opengauss.admin.common.constant.CommonConstants; +import org.opengauss.admin.common.core.domain.entity.SysSettingEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.*; +import org.opengauss.admin.plugin.domain.model.ops.*; +import org.opengauss.admin.plugin.domain.model.ops.cache.SSHChannelManager; +import org.opengauss.admin.plugin.domain.model.ops.cache.TaskManager; +import org.opengauss.admin.plugin.domain.model.ops.cache.WsConnectorManager; +import org.opengauss.admin.plugin.domain.model.ops.env.HostEnv; +import org.opengauss.admin.plugin.domain.model.ops.node.EnterpriseInstallNodeConfig; +import org.opengauss.admin.plugin.domain.model.ops.node.LiteInstallNodeConfig; +import org.opengauss.admin.plugin.domain.model.ops.node.MinimalistInstallNodeConfig; +import org.opengauss.admin.plugin.enums.ops.*; +import org.opengauss.admin.plugin.mapper.ops.OpsClusterMapper; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterService; +import org.opengauss.admin.plugin.service.ops.IOpsPackageManagerService; +import org.opengauss.admin.plugin.utils.DBUtil; +import org.opengauss.admin.plugin.utils.DownloadUtil; +import org.opengauss.admin.plugin.utils.DecryptionUtil; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.plugin.utils.OpsAssert; +import org.opengauss.admin.plugin.utils.WsUtil; +import org.opengauss.admin.plugin.vo.ops.*; +import org.opengauss.admin.plugin.vo.ops.SessionVO; +import org.opengauss.admin.plugin.vo.ops.SlowSqlVO; +import org.opengauss.admin.system.plugin.beans.SshLogin; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.opengauss.admin.system.plugin.facade.HostMonitorFacade; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; +import org.opengauss.admin.system.plugin.facade.JschExecutorFacade; +import org.opengauss.admin.system.plugin.facade.OpsFacade; +import org.opengauss.admin.system.plugin.facade.SysSettingFacade; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.File; +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +/** + * OpsClusterServiceImpl + * + * @author lhf + * @date 2022/8/6 17:38 + **/ +@Service +public class OpsClusterServiceImpl extends ServiceImpl + implements IOpsClusterService { + private static final List MONITOR_DEPENDENCY_LIST = List.of("procps-ng", "grep", "coreutils"); + private static final Logger log = LoggerFactory.getLogger(OpsClusterServiceImpl.class); + + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostFacade hostFacade; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostUserFacade hostUserFacade; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostMonitorFacade hostMonitorFacade; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private OpsFacade opsFacade; + @Autowired + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + @Autowired + private IOpsClusterNodeService opsClusterNodeService; + @Autowired + private WsUtil wsUtil; + @Autowired + private DownloadUtil downloadUtil; + @Autowired + private JschUtil jschUtil; + @Autowired + private DBUtil DBUtil; + @Autowired + private SSHChannelManager sshChannelManager; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + @Autowired + private DecryptionUtil decryptionUtil; + @Autowired + private WsConnectorManager wsConnectorManager; + @Autowired + private ClusterOpsProviderManager clusterOpsProviderManager; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private SysSettingFacade sysSettingFacade; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private JschExecutorFacade jschExecutorFacade; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private IOpsPackageManagerService pkgManagerService; + + @Override + public void download(DownloadBody downloadBody) { + WsSession wsSession = wsConnectorManager.getSession(downloadBody.getBusinessId()) + .orElseThrow(() -> new OpsException("No output websocket session found")); + Future future = threadPoolTaskExecutor.submit( + () -> downloadUtil.download(downloadBody.getResourceUrl(), downloadBody.getTargetPath(), + downloadBody.getFileName(), wsSession)); + TaskManager.registry(downloadBody.getBusinessId(), future); + } + + @Override + public void install(InstallBody installBody) { + log.info("install:{}", JSON.toJSONString(installBody)); + decryptionUtil.decryptDatabasePassword(installBody); + InstallContext installContext = installBody.getInstallContext(); + WsSession wsSession = wsConnectorManager.getSession(installBody.getBusinessId()) + .orElseThrow(() -> new OpsException("websocket session not exist")); + installContext.setRetSession(wsSession); + populateHostInfo(installContext); + installContext.setOs(checkOS(installBody.getInstallContext().getHostInfoHolders(), true)); + if (Boolean.TRUE.equals(installBody.getQuickInstall())) { + downloadInstallPackage(installBody); + } + checkInstallConfig(installContext); + InstallContext clone = installContext.clone(); + RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + RequestContextHolder.setRequestAttributes(context); + doInstall(clone); + }); + TaskManager.registry(installBody.getBusinessId(), future); + } + + @Override + public UpgradeOsCheckVO upgradeOsCheck(String clusterId, String rootPassword) { + OpsClusterEntity clusterEntity = getById(clusterId); + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("Cluster node information cannot be empty"); + } + OpsClusterNodeEntity nodeEntity = opsClusterNodeEntities.stream() + .filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER) + .findFirst() + .orElseThrow(() -> new OpsException("Masternode information not found")); + String hostId = nodeEntity.getHostId(); + String installUserId = nodeEntity.getInstallUserId(); + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + OpsHostUserEntity hostUserEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(hostUserEntity)) { + throw new OpsException("Installation user information does not exist"); + } + OpsHostUserEntity rootUserEntity = hostUserFacade.getRootUserByHostId(hostId); + if (Objects.isNull(rootUserEntity)) { + throw new OpsException("User root was not found"); + } + if (StrUtil.isEmpty(rootUserEntity.getPassword()) && StrUtil.isEmpty(rootPassword)) { + throw new OpsException("root password cannot be empty"); + } + if (StrUtil.isNotEmpty(rootUserEntity.getPassword())) { + rootPassword = rootUserEntity.getPassword(); + } + Session rootSession = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), "root", + encryptionUtils.decrypt(rootPassword)) + .orElseThrow( + () -> new OpsException("with the host[" + hostEntity.getPublicIp() + "]Failed to establish session")); + String checkOsRes = upgradeCheckOs(rootSession, hostUserEntity.getUsername()); + String diskInfo = getDiskInfo(rootSession); + UpgradeOsCheckVO upgradeOsCheckVO = new UpgradeOsCheckVO(); + upgradeOsCheckVO.setOsInfo(checkOsRes); + upgradeOsCheckVO.setDiskUsed(calcDiskUsed(diskInfo, clusterEntity.getInstallPath())); + return upgradeOsCheckVO; + } + + private String getDiskInfo(Session session) { + String command = "df -h"; + try { + JschResult jschResult = jschUtil.executeCommand(command, session); + if (0 != jschResult.getExitCode()) { + log.error("Query disk fail, exit code: {}, message: {}", jschResult.getExitCode(), + jschResult.getResult()); + throw new OpsException("Query disk size failed"); + } else { + return jschResult.getResult(); + } + } catch (Exception e) { + log.error("Failed to query disk size", e); + throw new OpsException("Failed to query disk size"); + } + } + + private String upgradeCheckOs(Session session, String installUsername) { + String command = "cd /root/gauss_om/" + installUsername + "/script/ && ./gs_checkos -i A"; + try { + JschResult jschResult = jschUtil.executeCommand(command, session); + if (0 != jschResult.getExitCode()) { + log.error("checkos fail, exit code: {}, message: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("checkos failed"); + } else { + return jschResult.getResult(); + } + } catch (Exception e) { + log.error("Failed to checkos", e); + return StrUtil.EMPTY; + } + } + + private OpenGaussSupportOSEnum checkOS(List hostInfoHolderList, boolean root) { + Set os = new HashSet<>(); + Set osVersion = new HashSet<>(); + Set cpuArch = new HashSet<>(); + for (HostInfoHolder hostInfoHolder : hostInfoHolderList) { + OpsHostEntity hostEntity = hostInfoHolder.getHostEntity(); + os.add(getOS(hostEntity.getHostId())); + osVersion.add(getOSVersion(hostEntity.getHostId())); + cpuArch.add(getCpuArch(hostEntity.getHostId())); + } + OpsAssert.isTrue(os.size() == 1, "The system information of hosts is inconsistent"); + OpsAssert.isTrue(osVersion.size() == 1, "System version information is inconsistent"); + OpsAssert.isTrue(cpuArch.size() == 1, "CPU architecture information is inconsistent"); + String osInfo = null; + for (String osInfo0 : os) { + osInfo = osInfo0; + break; + } + String osVersionInfo = null; + for (String osVersionInfo0 : osVersion) { + osVersionInfo = osVersionInfo0; + break; + } + String cpuArchInfo = null; + for (String cpuArchInfo0 : cpuArch) { + cpuArchInfo = cpuArchInfo0; + break; + } + OpenGaussSupportOSEnum osInfoEnum = OpenGaussSupportOSEnum.of(osInfo, osVersionInfo, cpuArchInfo); + if (osInfoEnum == null) { + throw new OpsException( + "unsupported operating system,system id " + osInfo + ",system version id" + osVersionInfo + + ", cpu Architecture " + cpuArchInfo); + } + return osInfoEnum; + } + + private String getCpuArch(String hostId) { + return hostMonitorFacade.getCpuArch(hostId); + } + + private String getOSVersion(String hostId) { + return hostMonitorFacade.getOsVersion(hostId); + } + + private String getOS(String hostId) { + return hostMonitorFacade.getOs(hostId); + } + + @Override + public void upgrade(UpgradeBody upgradeBody) { + log.info("upgrade:{}", JSON.toJSONString(upgradeBody)); + OpsClusterEntity clusterEntity = getById(upgradeBody.getClusterId()); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + if (clusterEntity.getVersion() != OpenGaussVersionEnum.ENTERPRISE) { + throw new OpsException("Only supports Enterprise Edition upgrades"); + } + if (Objects.isNull(upgradeBody.getUpgradeType())) { + throw new OpsException("Please select an upgrade type"); + } + if (StrUtil.isEmpty(upgradeBody.getUpgradePackagePath()) || !FileUtil.exist( + upgradeBody.getUpgradePackagePath())) { + throw new OpsException("Upgrade package not found"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId( + upgradeBody.getClusterId()); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("No cluster node information found"); + } + OpsClusterNodeEntity opsClusterNodeEntity = opsClusterNodeEntities.stream() + .filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER) + .findFirst() + .orElseThrow(() -> new OpsException("The node information corresponding to the host is not found")); + String installUserId = opsClusterNodeEntity.getInstallUserId(); + OpsHostUserEntity installUserEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(installUserEntity)) { + throw new OpsException("Installation user information not found"); + } + OpsHostEntity hostEntity = hostFacade.getById(opsClusterNodeEntity.getHostId()); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information not found"); + } + OpsHostUserEntity rootUserEntity = hostUserFacade.getRootUserByHostId(opsClusterNodeEntity.getHostId()); + if (Objects.isNull(rootUserEntity)) { + throw new OpsException("User root was not found"); + } + if (StrUtil.isEmpty(rootUserEntity.getPassword()) && StrUtil.isEmpty(upgradeBody.getHostRootPassword())) { + throw new OpsException("root password cannot be empty"); + } + if (StrUtil.isNotEmpty(rootUserEntity.getPassword())) { + upgradeBody.setHostRootPassword(rootUserEntity.getPassword()); + } + UpgradeContext upgradeContext = new UpgradeContext(); + upgradeContext.setVersionNum(upgradeBody.getVersionNum()); + upgradeContext.setHostPublicIp(hostEntity.getPublicIp()); + upgradeContext.setRootPassword(upgradeBody.getHostRootPassword()); + WsSession wsSession = wsConnectorManager.getSession(upgradeBody.getBusinessId()) + .orElseThrow(() -> new OpsException("websocket session not exist")); + upgradeContext.setRetSession(wsSession); + upgradeContext.setClusterConfigXmlPath(clusterEntity.getXmlConfigPath()); + upgradeContext.setUpgradeType(upgradeBody.getUpgradeType()); + upgradeContext.setUpgradePackagePath(upgradeBody.getUpgradePackagePath()); + upgradeContext.setSepEnvFile(clusterEntity.getEnvPath()); + upgradeContext.setInstallUsername(installUserEntity.getUsername()); + upgradeContext.setInstallUserPassword(installUserEntity.getPassword()); + upgradeContext.setHostPort(hostEntity.getPort()); + upgradeContext.setClusterEntity(clusterEntity); + upgradeContext.setOpsClusterNodeEntity(opsClusterNodeEntity); + HostInfoHolder hostInfoHolder = new HostInfoHolder(); + hostInfoHolder.setHostEntity(hostEntity); + hostInfoHolder.setHostUserEntities(List.of(rootUserEntity)); + upgradeContext.setOs(checkOS(List.of(hostInfoHolder), true)); + RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + RequestContextHolder.setRequestAttributes(context); + doUpgrade(upgradeContext); + }); + TaskManager.registry(upgradeBody.getBusinessId(), future); + } + + @Override + public void upgradeCommit(UpgradeBody upgradeBody) { + log.info("upgradeCommit:{}", JSON.toJSONString(upgradeBody)); + OpsClusterEntity clusterEntity = getById(upgradeBody.getClusterId()); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId( + upgradeBody.getClusterId()); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("No cluster node information found"); + } + OpsClusterNodeEntity opsClusterNodeEntity = opsClusterNodeEntities.stream() + .filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER) + .findFirst() + .orElseThrow(() -> new OpsException("The node information corresponding to the host is not found")); + String installUserId = opsClusterNodeEntity.getInstallUserId(); + OpsHostUserEntity installUserEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(installUserEntity)) { + throw new OpsException("Installation user information not found"); + } + OpsHostEntity hostEntity = hostFacade.getById(opsClusterNodeEntity.getHostId()); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information not found"); + } + OpsHostUserEntity rootUserEntity = hostUserFacade.getRootUserByHostId(opsClusterNodeEntity.getHostId()); + if (Objects.isNull(rootUserEntity)) { + throw new OpsException("User root was not found"); + } + if (StrUtil.isEmpty(rootUserEntity.getPassword()) && StrUtil.isEmpty(upgradeBody.getHostRootPassword())) { + throw new OpsException("root password cannot be empty"); + } + if (StrUtil.isNotEmpty(rootUserEntity.getPassword())) { + upgradeBody.setHostRootPassword(rootUserEntity.getPassword()); + } + Session ommSession = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), + installUserEntity.getUsername(), encryptionUtils.decrypt(installUserEntity.getPassword())) + .orElseThrow(() -> new OpsException("Install user connection failed")); + RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + RequestContextHolder.setRequestAttributes(context); + WsSession wsSession = wsConnectorManager.getSession(upgradeBody.getBusinessId()) + .orElseThrow(() -> new OpsException("websocket session not exist")); + doUpgradeCommit(ommSession, wsSession, clusterEntity, upgradeBody.getVersionNum()); + if (Objects.nonNull(ommSession) && ommSession.isConnected()) { + ommSession.disconnect(); + } + }); + TaskManager.registry(upgradeBody.getBusinessId(), future); + } + + private void doUpgradeCommit(Session ommSession, WsSession wsSession, OpsClusterEntity clusterEntity, + String versionNum) { + wsUtil.sendText(wsSession, "COMMIT_UPGRADE"); + try { + String command = "gs_upgradectl -t commit-upgrade -X " + clusterEntity.getXmlConfigPath(); + JschResult jschResult = jschUtil.executeCommand(clusterEntity.getEnvPath(), command, ommSession, wsSession, + false); + if (5 != jschResult.getExitCode()) { + log.error("upgradeCommit failed, exit code: {}, error message: {}", jschResult.getExitCode(), + jschResult.getResult()); + throw new OpsException("upgradeCommit failed"); + } + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(OpsClusterEntity.class) + .set(OpsClusterEntity::getVersionNum, versionNum) + .eq(OpsClusterEntity::getClusterId, clusterEntity.getClusterId()); + update(updateWrapper); + wsUtil.sendText(wsSession, "FINAL_EXECUTE_EXIT_CODE:0"); + } catch (Exception e) { + log.error("upgradeCommit failed", e); + wsUtil.sendText(wsSession, "FINAL_EXECUTE_EXIT_CODE:-1"); + throw new OpsException("upgradeCommit failed"); + } + } + + @Override + public void upgradeRollback(UpgradeBody upgradeBody) { + log.info("upgradeRollback:{}", JSON.toJSONString(upgradeBody)); + OpsClusterEntity clusterEntity = getById(upgradeBody.getClusterId()); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId( + upgradeBody.getClusterId()); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("No cluster node information found"); + } + OpsClusterNodeEntity opsClusterNodeEntity = opsClusterNodeEntities.stream() + .filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER) + .findFirst() + .orElseThrow(() -> new OpsException("The node information corresponding to the host is not found")); + String installUserId = opsClusterNodeEntity.getInstallUserId(); + OpsHostUserEntity installUserEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(installUserEntity)) { + throw new OpsException("Installation user information not found"); + } + OpsHostEntity hostEntity = hostFacade.getById(opsClusterNodeEntity.getHostId()); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information not found"); + } + Session ommSession = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), + installUserEntity.getUsername(), encryptionUtils.decrypt(installUserEntity.getPassword())) + .orElseThrow(() -> new OpsException("Install user connection failed")); + RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + RequestContextHolder.setRequestAttributes(context); + WsSession wsSession = wsConnectorManager.getSession(upgradeBody.getBusinessId()) + .orElseThrow(() -> new OpsException("websocket session not exist")); + doUpgradeRollback(ommSession, wsSession, clusterEntity.getEnvPath(), clusterEntity.getXmlConfigPath()); + if (Objects.nonNull(ommSession) && ommSession.isConnected()) { + ommSession.disconnect(); + } + }); + TaskManager.registry(upgradeBody.getBusinessId(), future); + } + + private void doUpgradeRollback(Session ommSession, WsSession wsSession, String envPath, + String clusterConfigXmlPath) { + try { + String command = "gs_upgradectl -t auto-rollback -X " + clusterConfigXmlPath + " --force"; + JschResult jschResult = jschUtil.executeCommand(envPath, command, ommSession, wsSession, false); + if (2 != jschResult.getExitCode()) { + log.error("Failed to create xml configuration file, exit code: {}, error message: {}", + jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to create xml configuration file"); + } + wsUtil.sendText(wsSession, "FINAL_EXECUTE_EXIT_CODE:0"); + } catch (Exception e) { + log.error("Failed to create xml configuration file:", e); + wsUtil.sendText(wsSession, "FINAL_EXECUTE_EXIT_CODE:-1"); + } finally { + if (Objects.nonNull(ommSession) && ommSession.isConnected()) { + ommSession.disconnect(); + } + } + } + + private void doUpgrade(UpgradeContext upgradeContext) { + try { + clusterOpsProviderManager.provider(OpenGaussVersionEnum.ENTERPRISE) + .orElseThrow(() -> new OpsException("The current version does not support")) + .upgrade(upgradeContext); + wsUtil.sendText(upgradeContext.getRetSession(), "FINAL_EXECUTE_EXIT_CODE:0"); + } catch (Exception e) { + log.error("Upgrade fail:", e); + wsUtil.sendText(upgradeContext.getRetSession(), e.getMessage()); + wsUtil.sendText(upgradeContext.getRetSession(), "FINAL_EXECUTE_EXIT_CODE:-1"); + } + } + + @Override + public void quickInstall(InstallBody installBody) { + WsSession wsSession = wsConnectorManager.getSession(installBody.getBusinessId()) + .orElseThrow(() -> new OpsException("No output websocket session found")); + wsUtil.sendText(wsSession, "START"); + installBody.setQuickInstall(Boolean.TRUE); + if (StrUtil.isEmpty(installBody.getQuickInstallResourceUrl())) { + throw new OpsException("installation package path cannot be empty"); + } + List nodeConfigList = installBody.getInstallContext() + .getMinimalistInstallConfig() + .getNodeConfigList(); + if (CollUtil.isNotEmpty(nodeConfigList)) { + MinimalistInstallNodeConfig minimalistInstallNodeConfig = nodeConfigList.get(0); + String installUserId = minimalistInstallNodeConfig.getInstallUserId(); + if (StrUtil.isEmpty(installUserId)) { + List opsHostUserEntities = hostUserFacade.listHostUserByHostId( + minimalistInstallNodeConfig.getHostId()); + if (CollUtil.isNotEmpty(opsHostUserEntities)) { + OpsHostUserEntity opsHostUserEntity = opsHostUserEntities.stream() + .filter(user -> !"root".equals(user.getUsername())) + .findFirst() + .orElse(null); + if (Objects.nonNull(opsHostUserEntity)) { + minimalistInstallNodeConfig.setInstallUserId(opsHostUserEntity.getHostUserId()); + } + } + } + } + install(installBody); + } + + private void downloadInstallPackage(InstallBody installBody) { + String installPackagePath = installBody.getInstallContext().getInstallPackagePath(); + String resourceUrl = installBody.getQuickInstallResourceUrl(); + String fileName = resourceUrl.substring(resourceUrl.lastIndexOf("/") + 1); + WsSession wsSession = wsConnectorManager.getSession(installBody.getBusinessId()) + .orElseThrow(() -> new OpsException("No output websocket session found")); + downloadUtil.download(resourceUrl, installPackagePath, fileName, wsSession); + installBody.getInstallContext().setInstallPackagePath(installPackagePath + "/" + fileName); + } + + private void populateHostInfo(InstallContext installContext) { + int hostLen; + List hostIdList; + Map hostPasswdMap = new HashMap<>(); + OpenGaussVersionEnum openGaussVersion = installContext.getOpenGaussVersion(); + if (openGaussVersion == OpenGaussVersionEnum.ENTERPRISE) { + List nodeConfigList = installContext.getEnterpriseInstallConfig() + .getNodeConfigList(); + hostLen = nodeConfigList.size(); + hostIdList = nodeConfigList.stream() + .map(EnterpriseInstallNodeConfig::getHostId) + .collect(Collectors.toList()); + for (EnterpriseInstallNodeConfig enterpriseInstallNodeConfig : nodeConfigList) { + hostPasswdMap.put(enterpriseInstallNodeConfig.getHostId(), + enterpriseInstallNodeConfig.getRootPassword()); + } + } else if (openGaussVersion == OpenGaussVersionEnum.MINIMAL_LIST) { + List nodeConfigList = installContext.getMinimalistInstallConfig() + .getNodeConfigList(); + hostLen = nodeConfigList.size(); + hostIdList = nodeConfigList.stream() + .map(MinimalistInstallNodeConfig::getHostId) + .collect(Collectors.toList()); + for (MinimalistInstallNodeConfig minimalistInstallNodeConfig : nodeConfigList) { + hostPasswdMap.put(minimalistInstallNodeConfig.getHostId(), + minimalistInstallNodeConfig.getRootPassword()); + } + } else if (openGaussVersion == OpenGaussVersionEnum.LITE) { + List nodeConfigList = installContext.getLiteInstallConfig().getNodeConfigList(); + hostLen = nodeConfigList.size(); + hostIdList = nodeConfigList.stream().map(LiteInstallNodeConfig::getHostId).collect(Collectors.toList()); + for (LiteInstallNodeConfig liteInstallNodeConfig : nodeConfigList) { + hostPasswdMap.put(liteInstallNodeConfig.getHostId(), liteInstallNodeConfig.getRootPassword()); + } + } else { + throw new OpsException("unsupported version:[" + installContext.getOpenGaussVersion() + "]"); + } + if (CollUtil.isNotEmpty(hostIdList)) { + List opsHostEntities = hostFacade.listByIds(hostIdList); + if (CollUtil.isEmpty(opsHostEntities)) { + log.info("Host information not found,hostIds{}", hostIdList); + throw new OpsException("Host information not found"); + } + if (opsHostEntities.size() != hostLen) { + String errMsg = "Cannot select the same host as the installation target"; + log.info(errMsg); + throw new OpsException(errMsg); + } + List hostUserEntities = hostUserFacade.listHostUserByHostIdList(hostIdList); + List hostInfoHolderList = opsHostEntities.stream() + .map(host -> new HostInfoHolder(host, hostUserEntities.stream() + .filter(hostUser -> hostUser.getHostId().equals(host.getHostId())) + .collect(Collectors.toList()))) + .collect(Collectors.toList()); + if (!Objects.equals(OpenGaussVersionEnum.MINIMAL_LIST, openGaussVersion)) { + for (HostInfoHolder hostInfoHolder : hostInfoHolderList) { + List userEntities = hostInfoHolder.getHostUserEntities(); + OpsHostUserEntity rootUserEntity = userEntities.stream() + .filter(userEntity -> "root".equals(userEntity.getUsername())) + .findFirst() + .orElseThrow(() -> new OpsException( + "Host not found[" + hostInfoHolder.getHostEntity().getPublicIp() + + "]root user information")); + if (StrUtil.isEmpty(rootUserEntity.getPassword())) { + if (StrUtil.isNotEmpty(hostPasswdMap.get(rootUserEntity.getHostId()))) { + rootUserEntity.setPassword(hostPasswdMap.get(rootUserEntity.getHostId())); + } else { + throw new OpsException("root password does not exist"); + } + } + } + } + installContext.setHostInfoHolders(hostInfoHolderList); + } else { + throw new OpsException("Node host configuration information cannot be empty"); + } + } + + private void checkInstallConfig(InstallContext installContext) { + String clusterId = installContext.getClusterId(); + clusterIdUniqueCheck(clusterId); + installContext.checkConfig(); + } + + private void clusterIdUniqueCheck(String clusterId) { + if (StrUtil.isEmpty(clusterId)) { + throw new OpsException("Cluster ID error"); + } + OpsClusterEntity clusterEntity = getById(clusterId); + if (Objects.nonNull(clusterEntity)) { + throw new OpsException("Cluster ID already exists"); + } + } + + private void doInstall(InstallContext installContext) { + try { + clusterOpsProviderManager.provider(installContext.getOpenGaussVersion()) + .orElseThrow(() -> new OpsException("The current version does not support")) + .install(installContext); + wsUtil.sendText(installContext.getRetSession(), "FINAL_EXECUTE_EXIT_CODE:0"); + } catch (Exception e) { + log.error("Installation exception:", e); + wsUtil.sendText(installContext.getRetSession(), e.getMessage()); + wsUtil.sendText(installContext.getRetSession(), "FINAL_EXECUTE_EXIT_CODE:-1"); + } + } + + @Override + public void ssh(SSHBody sshBody) { + OpsHostEntity opsHostEntity = hostFacade.getById(sshBody.getHostId()); + if (Objects.isNull(opsHostEntity)) { + throw new OpsException("host information does not exist"); + } + if (StrUtil.isEmpty(sshBody.getSshUsername())) { + sshBody.setSshUsername(CommonConstants.ROOT_USER); + } + List hostUserList = hostUserFacade.listHostUserByHostId(opsHostEntity.getHostId()); + OpsHostUserEntity sshUserEntity = hostUserList.stream() + .filter(opsHostUserEntity -> StrUtil.equalsIgnoreCase(sshBody.getSshUsername(), + opsHostUserEntity.getUsername())) + .findFirst() + .orElseThrow(() -> new OpsException( + "[" + opsHostEntity.getHostname() + " user" + sshBody.getSshUsername() + "] user not found")); + OpsAssert.isTrue(StrUtil.isNotEmpty(sshUserEntity.getPassword()), "user password not found"); + WsSession wsSession = wsConnectorManager.getSession(sshBody.getBusinessId()) + .orElseThrow(() -> new OpsException("websocket session not exist")); + ChannelShell channelShell = sshChannelManager.initChannelShell(sshBody, opsHostEntity, sshUserEntity) + .orElseThrow(() -> new OpsException("Connection establishment failed")); + Future future = threadPoolTaskExecutor.submit(() -> jschUtil.channelToWsSession(channelShell, wsSession)); + TaskManager.registry(sshBody.getBusinessId(), future); + } + + @Override + public List ls(String hostId, String path) { + OpsHostEntity opsHostEntity = hostFacade.getById(hostId); + if (Objects.isNull(opsHostEntity)) { + throw new OpsException("host information does not exist"); + } + List hostUserList = hostUserFacade.listHostUserByHostId(opsHostEntity.getHostId()); + OpsHostUserEntity installUserEntity = hostUserList.stream() + .filter(opsHostUserEntity -> !"root".equalsIgnoreCase(opsHostUserEntity.getUsername())) + .findFirst() + .orElseThrow(() -> new OpsException("[" + opsHostEntity.getHostname() + "] user not found")); + Session session = jschUtil.getSession(opsHostEntity.getPublicIp(), opsHostEntity.getPort(), + installUserEntity.getUsername(), encryptionUtils.decrypt(installUserEntity.getPassword())) + .orElseThrow(() -> new OpsException("user failed to establish connection")); + List ls = jschUtil.ls(session, path); + if (Objects.nonNull(session) && session.isConnected()) { + session.disconnect(); + } + return ls; + } + + @Override + public OpsNodeLogVO logPath(String clusterId, String hostId) { + OpsNodeLogVO opsNodeLogVO = new OpsNodeLogVO(); + OpsClusterEntity clusterEntity = getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("The current cluster node was not found on the host"); + } + OpsClusterNodeEntity clusterNode = opsClusterNodeEntities.stream() + .filter(opsClusterNodeEntity -> opsClusterNodeEntity.getHostId().equals(hostId)) + .findFirst() + .orElseThrow(() -> new OpsException("The current cluster node was not found on the host")); + String installUserId = clusterNode.getInstallUserId(); + OpsHostUserEntity hostUserEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(hostUserEntity)) { + throw new OpsException("Installation user information does not exist"); + } + if (OpenGaussVersionEnum.ENTERPRISE == clusterEntity.getVersion()) { + opsNodeLogVO.setSystemLogPath(clusterEntity.getLogPath()); + opsNodeLogVO.setDumpLogPath(clusterEntity.getCorePath()); + Session session = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), + hostUserEntity.getUsername(), encryptionUtils.decrypt(hostUserEntity.getPassword())).orElse(null); + try { + String command = "echo $GAUSSLOG"; + JschResult jschResult = jschUtil.executeCommand(command, session); + if (0 != jschResult.getExitCode()) { + log.error("Failed to get log path, exit code: {}, log: {}", jschResult.getExitCode(), + jschResult.getResult()); + throw new OpsException("Failed to get log path"); + } + String result = jschResult.getResult(); + if (StrUtil.isNotEmpty(result)) { + opsNodeLogVO.setOpLogPath(result); + opsNodeLogVO.setPerformanceLogPath(result + "/gs_profile"); + } + } catch (Exception e) { + log.error("Failed to get operation log path", e); + } finally { + if (Objects.nonNull(session) && session.isConnected()) { + session.disconnect(); + } + } + } else { + String dataPath = clusterNode.getDataPath(); + if (OpenGaussVersionEnum.MINIMAL_LIST == clusterEntity.getVersion()) { + if (DeployTypeEnum.CLUSTER == clusterEntity.getDeployType()) { + ClusterRoleEnum clusterRole = clusterNode.getClusterRole(); + if (clusterRole == ClusterRoleEnum.MASTER) { + dataPath = dataPath + "/master"; + } else { + dataPath = dataPath + "/slave"; + } + } else { + dataPath = dataPath + "/single_node"; + } + } + opsNodeLogVO.setSystemLogPath(dataPath + "/pg_log"); + Session session = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), + hostUserEntity.getUsername(), encryptionUtils.decrypt(hostUserEntity.getPassword())).orElse(null); + try { + String command = "echo $GAUSSLOG"; + JschResult jschResult = jschUtil.executeCommand(command, session); + if (0 != jschResult.getExitCode()) { + log.error("Failed to get log path, exit code: {}, log: {}", jschResult.getExitCode(), + jschResult.getResult()); + throw new OpsException("Failed to get log path"); + } + String result = jschResult.getResult(); + if (StrUtil.isNotEmpty(result)) { + opsNodeLogVO.setOpLogPath(result); + opsNodeLogVO.setPerformanceLogPath(result); + opsNodeLogVO.setDumpLogPath(result); + } + } catch (Exception e) { + log.error("Failed to get operation log path", e); + } finally { + if (Objects.nonNull(session) && session.isConnected()) { + session.disconnect(); + } + } + } + return opsNodeLogVO; + } + + @Override + public List auditLog(Page page, String clusterId, String hostId, String start, String end) { + OpsClusterEntity clusterEntity = getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("The current cluster node was not found on the host"); + } + OpsClusterNodeEntity clusterNode = opsClusterNodeEntities.stream() + .filter(opsClusterNodeEntity -> opsClusterNodeEntity.getHostId().equals(hostId)) + .findFirst() + .orElseThrow(() -> new OpsException("The current cluster node was not found on the host")); + String installUserId = clusterNode.getInstallUserId(); + OpsHostUserEntity hostUserEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(hostUserEntity)) { + throw new OpsException("Installation user information does not exist"); + } + Connection connection = null; + try { + connection = DBUtil.getSession(hostEntity.getPublicIp(), clusterEntity.getPort(), + clusterEntity.getDatabaseUsername(), encryptionUtils.decrypt(clusterEntity.getDatabasePassword())) + .orElseThrow(() -> new OpsException("Failed to establish connection to database")); + return querySession(connection, start, end); + } catch (Exception e) { + log.error("get connection fail"); + } finally { + if (Objects.nonNull(connection)) { + try { + connection.close(); + } catch (SQLException e) { + } + } + } + return Collections.emptyList(); + } + + @Override + public List listSession(String clusterId, String hostId) { + OpsClusterEntity clusterEntity = getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("The current cluster node was not found on the host"); + } + OpsClusterNodeEntity clusterNode = opsClusterNodeEntities.stream() + .filter(opsClusterNodeEntity -> opsClusterNodeEntity.getHostId().equals(hostId)) + .findFirst() + .orElseThrow(() -> new OpsException("The current cluster node was not found on the host")); + String installUserId = clusterNode.getInstallUserId(); + OpsHostUserEntity hostUserEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(hostUserEntity)) { + throw new OpsException("Installation user information does not exist"); + } + Connection connection = null; + try { + connection = DBUtil.getSession(hostEntity.getPublicIp(), clusterEntity.getPort(), + clusterEntity.getDatabaseUsername(), encryptionUtils.decrypt(clusterEntity.getDatabasePassword())) + .orElseThrow(() -> new OpsException("Failed to establish connection to database")); + return querySession(connection); + } catch (Exception e) { + log.error("get connection fail"); + } finally { + if (Objects.nonNull(connection)) { + try { + connection.close(); + } catch (SQLException e) { + } + } + } + return Collections.emptyList(); + } + + private List querySession(Connection connection) { + List res = new ArrayList<>(); + String sql = "SELECT * FROM pg_stat_activity"; + try (Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery(sql); + while (resultSet.next()) { + SessionVO sessionVO = new SessionVO(); + sessionVO.setDatid(resultSet.getString("datid")); + sessionVO.setDatname(resultSet.getString("datname")); + sessionVO.setPid(resultSet.getString("pid")); + sessionVO.setSessionid(resultSet.getString("sessionid")); + sessionVO.setUsesysid(resultSet.getString("usesysid")); + sessionVO.setUsename(resultSet.getString("usename")); + sessionVO.setApplication_name(resultSet.getString("application_name")); + sessionVO.setClient_addr(resultSet.getString("client_addr")); + sessionVO.setClient_hostname(resultSet.getString("client_hostname")); + sessionVO.setClient_port(resultSet.getString("client_port")); + sessionVO.setBackend_start(resultSet.getString("backend_start")); + sessionVO.setXact_start(resultSet.getString("xact_start")); + sessionVO.setQuery_start(resultSet.getString("query_start")); + sessionVO.setState_change(resultSet.getString("state_change")); + sessionVO.setWaiting(resultSet.getString("waiting")); + sessionVO.setEnqueue(resultSet.getString("enqueue")); + sessionVO.setState(resultSet.getString("state")); + sessionVO.setResource_pool(resultSet.getString("resource_pool")); + sessionVO.setQuery_id(resultSet.getString("query_id")); + sessionVO.setQuery(resultSet.getString("query")); + sessionVO.setConnection_info(resultSet.getString("connection_info")); + sessionVO.setUnique_sql_id(resultSet.getString("unique_sql_id")); + sessionVO.setTrace_id(resultSet.getString("trace_id")); + res.add(sessionVO); + } + } catch (Exception e) { + log.error("Querying the Audit Log Exception", e); + } + return res; + } + + @Override + public List slowSql(Page page, String clusterId, String hostId, String start, String end) { + OpsClusterEntity clusterEntity = getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("The current cluster node was not found on the host"); + } + OpsClusterNodeEntity clusterNode = opsClusterNodeEntities.stream() + .filter(opsClusterNodeEntity -> opsClusterNodeEntity.getHostId().equals(hostId)) + .findFirst() + .orElseThrow(() -> new OpsException("The current cluster node was not found on the host")); + String installUserId = clusterNode.getInstallUserId(); + OpsHostUserEntity hostUserEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(hostUserEntity)) { + throw new OpsException("Installation user information does not exist"); + } + Connection connection = null; + try { + connection = DBUtil.getSession(hostEntity.getPublicIp(), clusterEntity.getPort(), + clusterEntity.getDatabaseUsername(), encryptionUtils.decrypt(clusterEntity.getDatabasePassword())) + .orElseThrow(() -> new OpsException("Failed to establish connection to database")); + return querySlowSql(connection, start, end); + } catch (Exception e) { + log.error("get connection fail"); + } finally { + if (Objects.nonNull(connection)) { + try { + connection.close(); + } catch (SQLException e) { + } + } + } + return Collections.emptyList(); + } + + private List querySlowSql(Connection connection, String start, String end) { + List res = new ArrayList<>(); + String sql = "select * from DBE_PERF.get_global_full_sql_by_timestamp(?, ?)"; + try (PreparedStatement pstmt = connection.prepareStatement(sql)) { + pstmt.setString(1, start); + pstmt.setString(2, end); + try (ResultSet resultSet = pstmt.executeQuery()) { + while (resultSet.next()) { + SlowSqlVO slowSqlVO = new SlowSqlVO(); + slowSqlVO.setNode_name(resultSet.getString("node_name")); + slowSqlVO.setDb_name(resultSet.getString("db_name")); + slowSqlVO.setStart_time(resultSet.getString("start_time")); + slowSqlVO.setFinish_time(resultSet.getString("finish_time")); + slowSqlVO.setSlow_sql_threshold(resultSet.getString("slow_sql_threshold")); + slowSqlVO.setQuery(resultSet.getString("query")); + slowSqlVO.setQuery_plan(resultSet.getString("query_plan")); + res.add(slowSqlVO); + } + } + } catch (Exception e) { + log.error("Querying the Audit Log Exception", e); + } + return res; + } + + private List querySession(Connection connection, String start, String end) { + List res = new ArrayList<>(); + String sql = "select * from pg_query_audit(?, ?)"; + try (PreparedStatement pstmt = connection.prepareStatement(sql)) { + pstmt.setString(1, start); + pstmt.setString(2, end); + try (ResultSet resultSet = pstmt.executeQuery()) { + while (resultSet.next()) { + AuditLogVO auditLogVO = new AuditLogVO(); + auditLogVO.setTime(resultSet.getString("time")); + auditLogVO.setType(resultSet.getString("type")); + auditLogVO.setResult(resultSet.getString("result")); + auditLogVO.setUserid(resultSet.getString("userid")); + auditLogVO.setUsername(resultSet.getString("username")); + auditLogVO.setDatabase(resultSet.getString("database")); + auditLogVO.setClient_conninfo(resultSet.getString("client_conninfo")); + auditLogVO.setObject_name(resultSet.getString("object_name")); + auditLogVO.setDetail_info(resultSet.getString("detail_info")); + auditLogVO.setNode_name(resultSet.getString("node_name")); + auditLogVO.setThread_id(resultSet.getString("thread_id")); + auditLogVO.setLocal_port(resultSet.getString("local_port")); + auditLogVO.setRemote_port(resultSet.getString("remote_port")); + res.add(auditLogVO); + } + } + } catch (SQLException e) { + log.error("Querying the Audit Log Exception", e); + } + return res; + } + + @Override + public void download(String hostId, String path, String filename, HttpServletResponse response) { + OpsHostEntity opsHostEntity = hostFacade.getById(hostId); + if (Objects.isNull(opsHostEntity)) { + throw new OpsException("host information does not exist"); + } + List hostUserList = hostUserFacade.listHostUserByHostId(opsHostEntity.getHostId()); + OpsHostUserEntity installUser = hostUserList.stream() + .filter(opsHostUserEntity -> !"root".equalsIgnoreCase(opsHostUserEntity.getUsername())) + .findFirst() + .orElseThrow(() -> new OpsException("[" + opsHostEntity.getHostname() + "] user not found")); + Session session = jschUtil.getSession(opsHostEntity.getPublicIp(), opsHostEntity.getPort(), + installUser.getUsername(), encryptionUtils.decrypt(installUser.getPassword())) + .orElseThrow(() -> new OpsException("root user failed to establish connection")); + jschUtil.download(session, path, filename, response); + if (Objects.nonNull(session) && session.isConnected()) { + session.disconnect(); + } + } + + @Override + public void uninstall(UnInstallBody unInstallBody) { + UnInstallContext unInstallContext = new UnInstallContext(); + OpsClusterEntity clusterEntity = getById(unInstallBody.getClusterId()); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("cluster does not exist"); + } + unInstallContext.setOpsClusterEntity(clusterEntity); + List opsClusterNodeEntityList = opsClusterNodeService.listClusterNodeByClusterId( + unInstallBody.getClusterId()); + if (CollUtil.isEmpty(opsClusterNodeEntityList)) { + throw new OpsException("Cluster node information does not exist"); + } + unInstallContext.setOpsClusterNodeEntityList(opsClusterNodeEntityList); + WsSession wsSession = wsConnectorManager.getSession(unInstallBody.getBusinessId()) + .orElseThrow(() -> new OpsException("websocket session not exist")); + unInstallContext.setRetSession(wsSession); + constructUninstallContext(opsClusterNodeEntityList, clusterEntity, unInstallBody, unInstallContext); + } + + private void constructUninstallContext(List opsClusterNodeEntityList, + OpsClusterEntity clusterEntity, UnInstallBody unInstallBody, UnInstallContext unInstallContext) { + try { + List hostIdList = collectClusterNodeHostIdList(opsClusterNodeEntityList); + if (CollUtil.isEmpty(hostIdList)) { + throw new OpsException("Node host configuration information cannot be empty"); + } + List opsHostEntities = hostFacade.listByIds(hostIdList); + List hostUserEntities = hostUserFacade.listHostUserByHostIdList(hostIdList); + List hostInfoHolderList = opsHostEntities.stream() + .map(host -> new HostInfoHolder(host, hostUserEntities.stream() + .filter(hostUser -> hostUser.getHostId().equals(host.getHostId())) + .collect(Collectors.toList()))) + .collect(Collectors.toList()); + + unInstallContext.setHostInfoHolders(hostInfoHolderList); + unInstallContext.setOs(checkOS(unInstallContext.getHostInfoHolders(), false)); + RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + RequestContextHolder.setRequestAttributes(context); + doUnInstall(unInstallContext, unInstallBody.getForce()); + }); + TaskManager.registry(unInstallBody.getBusinessId(), future); + } catch (OpsException e) { + if (Objects.nonNull(unInstallBody.getForce()) && unInstallBody.getForce()) { + OpsClusterEntity opsClusterEntity = unInstallContext.getOpsClusterEntity(); + removeById(opsClusterEntity); + opsClusterNodeService.removeByIds(opsClusterNodeEntityList); + } + log.error("Uninstall exception:", e); + wsUtil.sendText(unInstallContext.getRetSession(), "FINAL_EXECUTE_EXIT_CODE:-1"); + } + } + + @Override + public void restart(OpsClusterBody restartBody) { + OpsClusterContext opsClusterContext = new OpsClusterContext(); + opsClusterContext.setRole(restartBody.getRole()); + opsClusterContext.setOpNodeIds(restartBody.getNodeIds()); + OpsClusterEntity clusterEntity = getById(restartBody.getClusterId()); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("cluster does not exist"); + } + opsClusterContext.setOpsClusterEntity(clusterEntity); + List opsClusterNodeEntityList = opsClusterNodeService.listClusterNodeByClusterId( + restartBody.getClusterId()); + if (CollUtil.isEmpty(opsClusterNodeEntityList)) { + throw new OpsException("Cluster node information does not exist"); + } + opsClusterContext.setOpsClusterNodeEntityList(opsClusterNodeEntityList); + if (StrUtil.isNotEmpty(restartBody.getBusinessId())) { + WsSession wsSession = wsConnectorManager.getSession(restartBody.getBusinessId()) + .orElseThrow(() -> new OpsException("websocket session not exist")); + opsClusterContext.setRetSession(wsSession); + } + List hostIdList = collectClusterNodeHostIdList(opsClusterNodeEntityList); + if (CollUtil.isNotEmpty(hostIdList)) { + setClusterContextOfHostInfoHolders(hostIdList, opsClusterContext); + } else { + throw new OpsException("Node host configuration information cannot be empty"); + } + opsClusterContext.setOs(checkOS(opsClusterContext.getHostInfoHolders(), false)); + if (Objects.nonNull(restartBody.getSync()) && restartBody.getSync()) { + doRestart(opsClusterContext, true); + } else { + RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + RequestContextHolder.setRequestAttributes(context); + doRestart(opsClusterContext, false); + }); + TaskManager.registry(restartBody.getBusinessId(), future); + } + } + + private void setClusterContextOfHostInfoHolders(List hostIdList, OpsClusterContext opsClusterContext) { + List opsHostEntities = hostFacade.listByIds(hostIdList); + List hostUserEntities = hostUserFacade.listHostUserByHostIdList(hostIdList); + List hostInfoHolderList = opsHostEntities.stream() + .map(host -> new HostInfoHolder(host, hostUserEntities.stream() + .filter(hostUser -> hostUser.getHostId().equals(host.getHostId())) + .collect(Collectors.toList()))) + .collect(Collectors.toList()); + for (HostInfoHolder hostInfoHolder : hostInfoHolderList) { + List userEntities = hostInfoHolder.getHostUserEntities(); + OpsHostUserEntity rootUserEntity = userEntities.stream() + .filter(userEntity -> "root".equals(userEntity.getUsername())) + .findFirst() + .orElse(null); + if (Objects.nonNull(rootUserEntity)) { + userEntities.remove(rootUserEntity); + } + } + opsClusterContext.setHostInfoHolders(hostInfoHolderList); + } + + private static List collectClusterNodeHostIdList(List opsClusterNodeEntityList) { + return opsClusterNodeEntityList.stream().map(OpsClusterNodeEntity::getHostId).collect(Collectors.toList()); + } + + private void doRestart(OpsClusterContext opsClusterContext, boolean sync) { + try { + OpsClusterContext clone = opsClusterContext.clone(); + clusterOpsProviderManager.provider(clone.getOpsClusterEntity().getVersion()) + .orElseThrow(() -> new OpsException("The current version does not support")) + .restart(clone); + if (!sync) { + wsUtil.sendText(opsClusterContext.getRetSession(), "FINAL_EXECUTE_EXIT_CODE:0"); + } + } catch (Exception e) { + log.error("Cluster restart failed:", e); + if (!sync) { + wsUtil.sendText(opsClusterContext.getRetSession(), "FINAL_EXECUTE_EXIT_CODE:-1"); + } else { + throw new OpsException("Cluster restart failed"); + } + } + } + + @Override + public void start(OpsClusterBody startBody) { + OpsClusterContext opsClusterContext = new OpsClusterContext(); + opsClusterContext.setRole(startBody.getRole()); + opsClusterContext.setOpNodeIds(startBody.getNodeIds()); + OpsClusterEntity clusterEntity = getById(startBody.getClusterId()); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("cluster does not exist"); + } + opsClusterContext.setOpsClusterEntity(clusterEntity); + List opsClusterNodeEntityList = opsClusterNodeService.listClusterNodeByClusterId( + startBody.getClusterId()); + if (CollUtil.isEmpty(opsClusterNodeEntityList)) { + throw new OpsException("Cluster node information does not exist"); + } + opsClusterContext.setOpsClusterNodeEntityList(opsClusterNodeEntityList); + WsSession wsSession = wsConnectorManager.getSession(startBody.getBusinessId()) + .orElseThrow(() -> new OpsException("websocket session not exist")); + opsClusterContext.setRetSession(wsSession); + List hostIdList = collectClusterNodeHostIdList(opsClusterNodeEntityList); + if (CollUtil.isNotEmpty(hostIdList)) { + setClusterContextOfHostInfoHolders(hostIdList, opsClusterContext); + } else { + throw new OpsException("Node host configuration information cannot be empty"); + } + opsClusterContext.setOs(checkOS(opsClusterContext.getHostInfoHolders(), false)); + if (Objects.nonNull(startBody.getSync()) && startBody.getSync()) { + doStart(opsClusterContext, true); + } else { + RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + RequestContextHolder.setRequestAttributes(context); + doStart(opsClusterContext, false); + }); + TaskManager.registry(startBody.getBusinessId(), future); + } + } + + private void doStart(OpsClusterContext opsClusterContext, boolean sync) { + try { + OpsClusterContext clone = opsClusterContext.clone(); + clusterOpsProviderManager.provider(clone.getOpsClusterEntity().getVersion()) + .orElseThrow(() -> new OpsException("The current version does not support")) + .start(clone); + if (!sync) { + wsUtil.sendText(opsClusterContext.getRetSession(), "FINAL_EXECUTE_EXIT_CODE:0"); + } + } catch (Exception e) { + log.error("startup exception:", e); + if (!sync) { + wsUtil.sendText(opsClusterContext.getRetSession(), "FINAL_EXECUTE_EXIT_CODE:-1"); + } else { + throw new OpsException(e.getMessage()); + } + } + } + + @Override + public void stop(OpsClusterBody stopBody) { + OpsClusterContext opsClusterContext = new OpsClusterContext(); + opsClusterContext.setRole(stopBody.getRole()); + opsClusterContext.setOpNodeIds( + Objects.isNull(stopBody.getNodeIds()) ? new ArrayList<>() : stopBody.getNodeIds()); + OpsClusterEntity clusterEntity = getById(stopBody.getClusterId()); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("cluster does not exist"); + } + opsClusterContext.setOpsClusterEntity(clusterEntity); + List opsClusterNodeEntityList = opsClusterNodeService.listClusterNodeByClusterId( + stopBody.getClusterId()); + if (CollUtil.isEmpty(opsClusterNodeEntityList)) { + throw new OpsException("Cluster node information does not exist"); + } + opsClusterContext.setOpsClusterNodeEntityList(opsClusterNodeEntityList); + WsSession wsSession = wsConnectorManager.getSession(stopBody.getBusinessId()) + .orElseThrow(() -> new OpsException("websocket session not exist")); + opsClusterContext.setRetSession(wsSession); + List hostIdList = collectClusterNodeHostIdList(opsClusterNodeEntityList); + if (CollUtil.isNotEmpty(hostIdList)) { + setClusterContextOfHostInfoHolders(hostIdList, opsClusterContext); + } else { + throw new OpsException("Node host configuration information cannot be empty"); + } + opsClusterContext.setOs(checkOS(opsClusterContext.getHostInfoHolders(), false)); + if (Objects.nonNull(stopBody.getSync()) && stopBody.getSync()) { + doStop(opsClusterContext, true); + } else { + RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + RequestContextHolder.setRequestAttributes(context); + doStop(opsClusterContext, false); + }); + TaskManager.registry(stopBody.getBusinessId(), future); + } + } + + private void doStop(OpsClusterContext opsClusterContext, boolean sync) { + try { + OpsClusterContext clone = opsClusterContext.clone(); + clusterOpsProviderManager.provider(clone.getOpsClusterEntity().getVersion()) + .orElseThrow(() -> new OpsException("The current version does not support")) + .stop(clone); + if (!sync) { + wsUtil.sendText(opsClusterContext.getRetSession(), "FINAL_EXECUTE_EXIT_CODE:0"); + } + } catch (Exception e) { + log.error("stop exception:", e); + if (!sync) { + wsUtil.sendText(opsClusterContext.getRetSession(), "FINAL_EXECUTE_EXIT_CODE:-1"); + } else { + throw new OpsException(e.getMessage()); + } + } + } + + @Override + public List listEnterpriseCluster() { + List enterpriseClusterList = listAllEnterpriseClusterList(); + if (CollUtil.isEmpty(enterpriseClusterList)) { + return enterpriseClusterList; + } + List> taskResult = new ArrayList<>(); + for (OpsClusterVO cluster : enterpriseClusterList) { + OpsClusterEntity clusterEntity = getById(cluster.getClusterId()); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + OpsClusterNodeEntity clusterNode = queryOpsClusterNodeEntity(cluster); + String installUserId = clusterNode.getInstallUserId(); + OpsHostEntity hostEntity = hostFacade.getById(clusterNode.getHostId()); + if (Objects.isNull(hostEntity)) { + throw new OpsException("Node host information does not exist"); + } + OpsHostUserEntity hostUserEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(hostUserEntity)) { + throw new OpsException("Installation user information does not exist"); + } + Future future = threadPoolTaskExecutor.submit(() -> { + SshLogin sshLogin = new SshLogin(hostEntity.getPublicIp(), hostEntity.getPort(), + hostUserEntity.getUsername(), encryptionUtils.decrypt(hostUserEntity.getPassword())); + String stateResultStr = state(sshLogin, OpenGaussVersionEnum.ENTERPRISE, clusterNode.getDataPath(), + clusterEntity.getEnvPath()); + Map stateResult = JSON.parseObject(stateResultStr, Map.class); + cluster.setClusterState((String) stateResult.get("cluster_state")); + }); + taskResult.add(future); + } + for (Future future : taskResult) { + try { + future.get(); + } catch (InterruptedException | ExecutionException e) { + String errMsg = "get cluster state from task failed: " + e.getMessage(); + log.error(errMsg); + throw new OpsException(errMsg); + } + } + return enterpriseClusterList; + } + + private OpsClusterNodeEntity queryOpsClusterNodeEntity(OpsClusterVO cluster) { + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId( + cluster.getClusterId()); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("The current cluster node was not found on the host"); + } + OpsClusterNodeEntity clusterNode = opsClusterNodeEntities.stream() + .filter(opsClusterNodeEntity -> opsClusterNodeEntity.getClusterRole().equals(ClusterRoleEnum.MASTER)) + .findFirst() + .orElseThrow(() -> new OpsException("The master cluster node was not found in the cluster")); + return clusterNode; + } + + private List listAllEnterpriseClusterList() { + List allClusterList = opsFacade.listCluster(); + if (CollUtil.isEmpty(allClusterList)) { + return allClusterList; + } + return allClusterList.stream() + .filter(item -> item.getVersion().equals(OpenGaussVersionEnum.ENTERPRISE.name())) + .collect(Collectors.toList()); + } + + @Override + public ClusterSummaryVO summary() { + ClusterSummaryVO clusterSummaryVO = new ClusterSummaryVO(); + clusterSummaryVO.setClusterNum(count()); + clusterSummaryVO.setHostNum(hostFacade.count()); + clusterSummaryVO.setNodeNum(opsClusterNodeService.count()); + return clusterSummaryVO; + } + + @Override + public String monitor(String clusterId, String hostId, String businessId, ClusterRoleEnum role) { + OpsClusterEntity clusterEntity = getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("Cluster node information does not exist"); + } + OpsClusterNodeEntity nodeEntity = opsClusterNodeEntities.stream() + .filter(opsClusterNodeEntity -> opsClusterNodeEntity.getHostId().equals(hostId)) + .findFirst() + .orElseThrow(() -> new OpsException("Cluster node information does not exist")); + String installUserId = nodeEntity.getInstallUserId(); + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("Node host information does not exist"); + } + OpsHostUserEntity hostUserEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(hostUserEntity)) { + throw new OpsException("Node installation user information does not exist"); + } + WsSession wsSession = wsConnectorManager.getSession(businessId) + .orElseThrow(() -> new OpsException("response session does not exist")); + String dataPath = nodeEntity.getDataPath(); + SshLogin sshLogin = new SshLogin(hostEntity.getPublicIp(), hostEntity.getPort(), hostUserEntity.getUsername(), + encryptionUtils.decrypt(hostUserEntity.getPassword())); + Future future = getMonitorFuture(hostEntity, clusterEntity, wsSession, sshLogin, dataPath); + TaskManager.registry(businessId, future); + return checkDependencies(sshLogin); + } + + private Future getMonitorFuture(OpsHostEntity hostEntity, OpsClusterEntity clusterEntity, WsSession wsSession, + SshLogin sshLogin, String dataPath) { + return threadPoolTaskExecutor.submit(() -> { + Connection connection = null; + try { + connection = DBUtil.getSession(hostEntity.getPublicIp(), clusterEntity.getPort(), + clusterEntity.getDatabaseUsername(), + encryptionUtils.decrypt(clusterEntity.getDatabasePassword())) + .orElseThrow(() -> new OpsException("Unable to connect to the database")); + MonitorPluginParam monitorParam = new MonitorPluginParam(hostEntity.getHostId(), dataPath, + clusterEntity.getVersion(), clusterEntity.getEnvPath()); + doMonitor(wsSession, sshLogin, monitorParam, connection); + } catch (Exception e) { + log.error("get connection fail , ip:{} , port:{}, username:{}", hostEntity.getPublicIp(), + clusterEntity.getPort(), clusterEntity.getDatabaseUsername(), e); + } finally { + if (Objects.nonNull(connection)) { + try { + connection.close(); + } catch (SQLException e) { + log.error("close connection occurred an error, error message:{}", e.getMessage()); + } + } + wsUtil.close(wsSession); + } + }); + } + + /** + * MonitorPluginParam 类的功能说明 + *

+ * 该类用于存储监控插件参数。 + */ + @AllArgsConstructor + @Data + private class MonitorPluginParam { + private String hostId; + private String dataPath; + private OpenGaussVersionEnum version; + private String envPath; + } + + @Override + public List listClusterHost(String clusterId) { + OpsClusterEntity clusterEntity = getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("Cluster node information does not exist"); + } + Set hostIds = opsClusterNodeEntities.stream() + .map(OpsClusterNodeEntity::getHostId) + .collect(Collectors.toSet()); + return hostFacade.listByIds(hostIds); + } + + @Override + public boolean hasName(String name) { + OpsClusterEntity clusterEntity = getById(name); + return Objects.nonNull(clusterEntity); + } + + @Override + public void generateconf(String clusterId, String hostId, String businessId) { + OpsClusterEntity clusterEntity = getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("Cluster node information does not exist"); + } + OpsClusterNodeEntity nodeEntity = opsClusterNodeEntities.stream() + .filter(node -> node.getHostId().equals(hostId)) + .findFirst() + .orElseThrow(() -> new OpsException("Cluster node information does not exist")); + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + OpsHostUserEntity installUserEntity = hostUserFacade.getById(nodeEntity.getInstallUserId()); + if (Objects.isNull(installUserEntity)) { + throw new OpsException("Installing user information does not exist"); + } + Session session = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), + installUserEntity.getUsername(), encryptionUtils.decrypt(installUserEntity.getPassword())) + .orElseThrow(() -> new OpsException("Failed to establish connection with host")); + WsSession retWsSession = wsConnectorManager.getSession(businessId) + .orElseThrow(() -> new OpsException("websocket session not exist")); + RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + try { + RequestContextHolder.setRequestAttributes(context); + doGenerateconf(session, retWsSession, clusterEntity.getXmlConfigPath(), clusterEntity.getEnvPath()); + } finally { + if (Objects.nonNull(session) && session.isConnected()) { + session.disconnect(); + } + } + + }); + TaskManager.registry(businessId, future); + } + + @Override + public void switchover(String clusterId, String hostId, String businessId) { + OpsClusterEntity clusterEntity = getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("Cluster node information does not exist"); + } + OpsClusterNodeEntity nodeEntity = opsClusterNodeEntities.stream() + .filter(node -> node.getHostId().equals(hostId)) + .findFirst() + .orElseThrow(() -> new OpsException("Cluster node information does not exist")); + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + OpsHostUserEntity installUserEntity = hostUserFacade.getById(nodeEntity.getInstallUserId()); + if (Objects.isNull(installUserEntity)) { + throw new OpsException("Installing user information does not exist"); + } + Session session = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), + installUserEntity.getUsername(), encryptionUtils.decrypt(installUserEntity.getPassword())) + .orElseThrow(() -> new OpsException("Failed to establish connection with host")); + WsSession retWsSession = wsConnectorManager.getSession(businessId) + .orElseThrow(() -> new OpsException("websocket session not exist")); + RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + try { + RequestContextHolder.setRequestAttributes(context); + doSwitchover(session, retWsSession, nodeEntity.getDataPath(), clusterEntity.getEnvPath()); + } finally { + if (Objects.nonNull(session) && session.isConnected()) { + session.disconnect(); + } + } + + }); + TaskManager.registry(businessId, future); + } + + @Override + public void build(String clusterId, String hostId, String businessId) { + OpsClusterEntity clusterEntity = getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("Cluster node information does not exist"); + } + OpsClusterNodeEntity nodeEntity = opsClusterNodeEntities.stream() + .filter(node -> node.getHostId().equals(hostId)) + .findFirst() + .orElseThrow(() -> new OpsException("Cluster node information does not exist")); + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + OpsHostUserEntity installUserEntity = hostUserFacade.getById(nodeEntity.getInstallUserId()); + if (Objects.isNull(installUserEntity)) { + throw new OpsException("Installing user information does not exist"); + } + Session session = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), + installUserEntity.getUsername(), encryptionUtils.decrypt(installUserEntity.getPassword())) + .orElseThrow(() -> new OpsException("Failed to establish connection with host")); + WsSession retWsSession = wsConnectorManager.getSession(businessId) + .orElseThrow(() -> new OpsException("websocket session not exist")); + RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + try { + RequestContextHolder.setRequestAttributes(context); + doBuild(session, retWsSession, nodeEntity.getDataPath(), clusterEntity.getEnvPath()); + } finally { + if (Objects.nonNull(session) && session.isConnected()) { + session.disconnect(); + } + } + + }); + TaskManager.registry(businessId, future); + } + + private void doBuild(Session session, WsSession retWsSession, String dataPath, String envPath) { + String command = "gs_ctl build -D " + dataPath; + try { + JschResult jschResult = jschUtil.executeCommand(command, envPath, session, retWsSession); + if (0 != jschResult.getExitCode()) { + log.error("A build error occurred, exit code {}", jschResult.getExitCode()); + throw new OpsException("A build error occurred"); + } + wsUtil.sendText(retWsSession, "FINAL_EXECUTE_EXIT_CODE:0"); + } catch (Exception e) { + log.error("build fail", e); + wsUtil.sendText(retWsSession, "FINAL_EXECUTE_EXIT_CODE:-1"); + } + } + + @Override + public ListDir listInstallPackage(OpenGaussVersionEnum openGaussVersionEnum, Integer userId) { + SysSettingEntity entity = sysSettingFacade.getSysSetting(userId); + Assert.notNull(entity, "Failed to get installation package path"); + ListDir listDir = new ListDir(); + listDir.setPath(entity.getUploadPath()); + String[] extension = null; + String[] namePart = null; + if (OpenGaussVersionEnum.ENTERPRISE == openGaussVersionEnum) { + extension = new String[] {"-all.tar.gz"}; + } else if (OpenGaussVersionEnum.LITE == openGaussVersionEnum) { + namePart = new String[] {"-Lite-"}; + } else if (OpenGaussVersionEnum.MINIMAL_LIST == openGaussVersionEnum) { + extension = new String[] {".tar.bz2"}; + } else { + extension = new String[] {".tar.gz", ".tar.bz2"}; + } + listDir.setFiles(listFiles(entity.getUploadPath(), openGaussVersionEnum, extension, namePart)); + return listDir; + } + + private boolean nameFilter(File file, String[] namePart) { + if (namePart == null || namePart.length < 1) { + return true; + } + String name = file.getName(); + for (String s : namePart) { + if (name.contains(s)) { + return true; + } + } + return false; + } + + private boolean extensionFilter(File file, String[] extension) { + if (extension == null || extension.length < 1) { + return true; + } + String name = file.getName(); + for (String s : extension) { + if (name.endsWith(s)) { + return true; + } + } + return false; + } + + private List listFiles(String uploadPath, OpenGaussVersionEnum openGaussVersionEnum, String[] extension, + String[] namePart) { + log.info("List the files under the path: {}", uploadPath); + List pkgList = pkgManagerService.list(); + List result = new ArrayList<>(); + Assert.notEmpty(uploadPath, "Failed to enumerate files, path cannot be empty"); + File folderPath = new File(uploadPath); + if (!folderPath.isDirectory()) { + return result; + } + List files = getAllFiles(uploadPath); + if (files.size() <= 0) { + return result; + } + for (String filePath : files) { + File file = new File(filePath); + if (!file.isFile() || !file.exists()) { + continue; + } + OpsPackageManagerEntity entity = CollUtil.findOne(pkgList, + item -> item.getName() != null && filePath.contains(item.getName())); + // is managed, get info here + if (entity != null && entity.getPackageVersion().equalsIgnoreCase(openGaussVersionEnum.toString())) { + OpsPackageVO vo = entity.toVO(); + result.add(HostFile.build(file, vo)); + } else { + // not managed, filter by name + if (extensionFilter(file, extension) && nameFilter(file, namePart)) { + result.add(HostFile.build(file, new OpsPackageVO())); + } + } + } + Collections.sort(result); + return result; + } + + private String getOrDefault(String path) { + if (StrUtil.isNotEmpty(path)) { + return path; + } + return System.getProperty("user.dir"); + } + + @Resource + OpsClusterEnvService opsClusterEnvService; + + @Override + public HostEnv env(String hostId, OpenGaussSupportOSEnum expectedOs, String rootPassword) { + return opsClusterEnvService.env(hostId, expectedOs, rootPassword); + } + + @Override + public Map threadPoolMonitor() { + Map res = new HashMap<>(); + int activeCount = threadPoolTaskExecutor.getActiveCount(); + int poolSize = threadPoolTaskExecutor.getPoolSize(); + int corePoolSize = threadPoolTaskExecutor.getCorePoolSize(); + int keepAliveSeconds = threadPoolTaskExecutor.getKeepAliveSeconds(); + res.put("activeCount", activeCount); + res.put("poolSize", poolSize); + res.put("corePoolSize", corePoolSize); + res.put("keepAliveSeconds", keepAliveSeconds); + return res; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void removeCluster(String clusterId) { + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isNotEmpty(opsClusterNodeEntities)) { + opsClusterNodeService.removeBatchByIds(opsClusterNodeEntities.stream() + .map(OpsClusterNodeEntity::getClusterNodeId) + .collect(Collectors.toSet())); + } + removeById(clusterId); + } + + private void doSwitchover(Session session, WsSession retWsSession, String dataPath, String envPath) { + try { + String command = "gs_ctl switchover -D " + dataPath; + JschResult jschResult = jschUtil.executeCommand(command, envPath, session, retWsSession); + if (0 != jschResult.getExitCode()) { + log.error("Active-standby switchover failed, exit code {}", jschResult.getExitCode()); + throw new OpsException("Active-standby switchover failed"); + } + } catch (Exception e) { + log.error("Active-standby switchover failed", e); + wsUtil.sendText(retWsSession, "FINAL_EXECUTE_EXIT_CODE:-1"); + return; + } + try { + String command = "gs_om -t refreshconf"; + JschResult jschResult = jschUtil.executeCommand(command, envPath, session, retWsSession); + if (0 != jschResult.getExitCode()) { + log.error("Active-standby switchover failed, exit code {}", jschResult.getExitCode()); + throw new OpsException("Active-standby switchover failed"); + } + } catch (Exception e) { + log.error("Active-standby switchover failed", e); + wsUtil.sendText(retWsSession, "FINAL_EXECUTE_EXIT_CODE:-1"); + return; + } + wsUtil.sendText(retWsSession, "FINAL_EXECUTE_EXIT_CODE:0"); + } + + private void doGenerateconf(Session session, WsSession retWsSession, String xmlConfigPath, String envPath) { + String command = "gs_om -t generateconf -X " + xmlConfigPath + " --distribute"; + try { + JschResult jschResult = jschUtil.executeCommand(command, envPath, session, retWsSession); + if (0 != jschResult.getExitCode()) { + log.error("A generateconf error occurred, exit code {}", jschResult.getExitCode()); + throw new OpsException("A generateconf error occurred"); + } + wsUtil.sendText(retWsSession, "FINAL_EXECUTE_EXIT_CODE:0"); + } catch (Exception e) { + log.error("generateconf fail", e); + wsUtil.sendText(retWsSession, "FINAL_EXECUTE_EXIT_CODE:-1"); + } + } + + private String doCheck(OpsHostUserEntity rootUserEntity, Session session) { + log.info("One-click self-test start"); + String command = "gs_check -e inspect"; + try { + Map autoResponse = new HashMap<>(); + autoResponse.put("Please enter root privileges user[root]:", "root"); + autoResponse.put("Please enter password for user[root]:", rootUserEntity.getPassword()); + JschResult jschResult = jschUtil.executeCommand(command, session, autoResponse); + if (0 != jschResult.getExitCode()) { + log.error("A self-test error occurred, exit code {}", jschResult.getExitCode()); + } else { + String result = jschResult.getResult(); + return result; + + } + } catch (Exception e) { + log.error("One-click self-test results", e); + if (Objects.nonNull(session) && session.isConnected()) { + session.disconnect(); + } + throw new OpsException("One-click self-test results"); + } finally { + log.info("One-click self-test end"); + } + return null; + } + + private CheckVO parseCheckResToCheckVO(String result) { + CheckVO checkVO = new CheckVO(); + // cluster + CheckClusterVO checkClusterVO = new CheckClusterVO(); + checkClusterVO.setCheckClusterState(parseCheckItem(result, "CheckClusterState")); + checkClusterVO.setCheckDBParams(parseCheckItem(result, "CheckDBParams")); + checkClusterVO.setCheckDebugSwitch(parseCheckItem(result, "CheckDebugSwitch")); + checkClusterVO.setCheckDirPermissions(parseCheckItem(result, "CheckDirPermissions")); + checkClusterVO.setCheckEnvProfile(parseCheckItem(result, "CheckEnvProfile")); + checkClusterVO.setCheckReadonlyMode(parseCheckItem(result, "CheckReadonlyMode")); + checkClusterVO.setCheckDilateSysTab(parseCheckItem(result, "CheckDilateSysTab")); + checkClusterVO.setCheckProStartTime(parseCheckItem(result, "CheckProStartTime")); + checkClusterVO.setCheckMpprcFile(parseCheckItem(result, "CheckMpprcFile")); + checkVO.setCluster(checkClusterVO); + // database + CheckDbVO checkDbVO = new CheckDbVO(); + checkDbVO.setCheckCurConnCount(parseCheckItem(result, "CheckCurConnCount")); + checkDbVO.setCheckCursorNum(parseCheckItem(result, "CheckCursorNum")); + checkDbVO.setCheckPgxcgroup(parseCheckItem(result, "CheckPgxcgroup")); + checkDbVO.setCheckNodeGroupName(parseCheckItem(result, "CheckNodeGroupName")); + checkDbVO.setCheckTableSpace(parseCheckItem(result, "CheckTableSpace")); + checkDbVO.setCheckSysadminUser(parseCheckItem(result, "CheckSysadminUser")); + checkDbVO.setCheckHashIndex(parseCheckItem(result, "CheckHashIndex")); + checkDbVO.setCheckPgxcRedistb(parseCheckItem(result, "CheckPgxcRedistb")); + checkDbVO.setCheckNodeGroupName(parseCheckItem(result, "CheckNodeGroupName")); + checkDbVO.setCheckTDDate(parseCheckItem(result, "CheckTDDate")); + checkVO.setDb(checkDbVO); + // os + CheckOSVO checkOSVO = new CheckOSVO(); + checkOSVO.setCheckEncoding(parseCheckItem(result, "CheckEncoding")); + checkOSVO.setCheckFirewall(parseCheckItem(result, "CheckFirewall")); + checkOSVO.setCheckKernelVer(parseCheckItem(result, "CheckKernelVer")); + checkOSVO.setCheckMaxHandle(parseCheckItem(result, "CheckMaxHandle")); + checkOSVO.setCheckNTPD(parseCheckItem(result, "CheckNTPD")); + checkOSVO.setCheckOSVer(parseCheckItem(result, "CheckOSVer")); + checkOSVO.setCheckSysParams(parseCheckItem(result, "CheckSysParams")); + checkOSVO.setCheckTHP(parseCheckItem(result, "CheckTHP")); + checkOSVO.setCheckTimeZone(parseCheckItem(result, "CheckTimeZone")); + checkOSVO.setCheckCPU(parseCheckItem(result, "CheckCPU")); + checkOSVO.setCheckSshdService(parseCheckItem(result, "CheckSshdService")); + checkOSVO.setCheckSshdConfig(parseCheckItem(result, "CheckSshdConfig")); + checkOSVO.setCheckCrondService(parseCheckItem(result, "CheckCrondService")); + checkOSVO.setCheckStack(parseCheckItem(result, "CheckStack")); + checkOSVO.setCheckSysPortRange(parseCheckItem(result, "CheckSysPortRange")); + checkOSVO.setCheckMemInfo(parseCheckItem(result, "CheckMemInfo")); + checkOSVO.setCheckHyperThread(parseCheckItem(result, "CheckHyperThread")); + checkOSVO.setCheckMaxProcMemory(parseCheckItem(result, "CheckMaxProcMemory")); + checkOSVO.setCheckBootItems(parseCheckItem(result, "CheckBootItems")); + checkOSVO.setCheckKeyProAdj(parseCheckItem(result, "CheckKeyProAdj")); + checkOSVO.setCheckFilehandle(parseCheckItem(result, "CheckFilehandle")); + checkOSVO.setCheckDropCache(parseCheckItem(result, "CheckDropCache")); + checkVO.setOs(checkOSVO); + // device + CheckDeviceVO checkDeviceVO = new CheckDeviceVO(); + checkDeviceVO.setCheckBlockdev(parseCheckItem(result, "CheckBlockdev")); + checkDeviceVO.setCheckDiskFormat(parseCheckItem(result, "CheckDiskFormat")); + checkDeviceVO.setCheckSpaceUsage(parseCheckItem(result, "CheckSpaceUsage")); + checkDeviceVO.setCheckInodeUsage(parseCheckItem(result, "CheckInodeUsage")); + checkDeviceVO.setCheckSwapMemory(parseCheckItem(result, "CheckSwapMemory")); + checkDeviceVO.setCheckLogicalBlock(parseCheckItem(result, "CheckLogicalBlock")); + checkDeviceVO.setCheckIOrequestqueue(parseCheckItem(result, "CheckIOrequestqueue")); + checkDeviceVO.setCheckMaxAsyIOrequests(parseCheckItem(result, "CheckMaxAsyIOrequests")); + checkDeviceVO.setCheckIOConfigure(parseCheckItem(result, "CheckIOConfigure")); + checkVO.setDevice(checkDeviceVO); + // network + CheckNetworkVO checkNetworkVO = new CheckNetworkVO(); + checkNetworkVO.setCheckMTU(parseCheckItem(result, "CheckMTU")); + checkNetworkVO.setCheckPing(parseCheckItem(result, "CheckPing")); + checkNetworkVO.setCheckRXTX(parseCheckItem(result, "CheckRXTX")); + checkNetworkVO.setCheckNetWorkDrop(parseCheckItem(result, "CheckNetWorkDrop")); + checkNetworkVO.setCheckMultiQueue(parseCheckItem(result, "CheckMultiQueue")); + checkNetworkVO.setCheckRouting(parseCheckItem(result, "CheckRouting")); + checkNetworkVO.setCheckNICModel(parseCheckItem(result, "CheckNICModel")); + checkVO.setNetwork(checkNetworkVO); + return checkVO; + } + + private CheckItemVO parseCheckItem(String result, String item) { + item = item + "."; + CheckItemVO checkItemVO = new CheckItemVO(); + checkItemVO.setName(item); + int indexStart = result.indexOf(item); + int reIndex = result.indexOf("\n", indexStart); + int indexEnd = result.indexOf(".............", reIndex); + if ("CheckMpprcFile.".equals(item)) { + indexEnd = result.indexOf("Analysis the check result", reIndex); + } + if (indexEnd <= indexStart) { + log.error("index end le index start"); + return checkItemVO; + } + String substring = result.substring(indexStart, indexEnd); + if (StrUtil.isNotEmpty(substring)) { + String[] split = substring.split("\n"); + if (split.length < 2) { + return checkItemVO; + } + StringBuffer msg = new StringBuffer(); + for (int i = 0; i < split.length; i++) { + String line = split[i]; + if (i == 0) { + String status = line.substring(line.lastIndexOf(".") + 1, line.indexOf("\r")); + checkItemVO.setStatus(status); + } else { + if (StrUtil.isNotEmpty(line.trim())) { + msg.append(line).append("\n"); + } else { + break; + } + } + } + checkItemVO.setMsg(msg.toString()); + } + return checkItemVO; + } + + private void doMonitor(WsSession wsSession, SshLogin sshLogin, MonitorPluginParam monitorParam, + Connection connection) { + AtomicBoolean hasError = new AtomicBoolean(false); + while (wsSession.getSession().isOpen() && !hasError.get()) { + NodeMonitorVO nodeMonitorVO = new NodeMonitorVO(); + CountDownLatch countDownLatch = new CountDownLatch(1); + threadPoolTaskExecutor.submit(() -> { + try { + nodeMonitorVO.setTime(System.currentTimeMillis()); + nodeMonitorVO.setCpu(cpu(monitorParam.getHostId())); + nodeMonitorVO.setMemory(memory(monitorParam.getHostId())); + nodeMonitorVO.setNet(net(monitorParam.getHostId())); + nodeMonitorVO.setState( + state(sshLogin, monitorParam.version, monitorParam.dataPath, monitorParam.envPath)); + nodeMonitorVO.setLock(lock(connection)); + nodeMonitorVO.setSession(session(connection)); + nodeMonitorVO.setConnectNum(connectNum(connection)); + nodeMonitorVO.setSessionMemoryTop10(sessionMemoryTop10(connection)); + nodeMonitorVO.setKernel(kernel(monitorParam.getHostId())); + nodeMonitorVO.setMemorySize(memorySize(monitorParam.getHostId())); + } catch (Exception e) { + log.error("cpu monitor error : {}", e.getMessage()); + hasError.set(true); + } finally { + countDownLatch.countDown(); + } + }); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + log.error("waiting for thread to be interrupted {}", e.getMessage()); + } + wsUtil.sendText(wsSession, JSON.toJSONString(nodeMonitorVO)); + try { + TimeUnit.SECONDS.sleep(1L); + } catch (InterruptedException e) { + log.error("waiting for monitor thread to be interrupted {}", e.getMessage()); + } + } + } + + private String checkDependencies(SshLogin sshLogin) { + String dependencies = jschExecutorFacade.execCommand(sshLogin, SshCommandConstants.MONITOR_DEPENDENCY); + StringBuilder missDependency = new StringBuilder(); + for (String dependency : MONITOR_DEPENDENCY_LIST) { + if (!dependencies.contains(dependency)) { + missDependency.append(dependency).append(" "); + } + } + return missDependency.toString(); + } + + private List> sessionMemoryTop10(Connection connection) { + String sql = "SELECT * FROM pv_session_memory_detail() ORDER BY usedsize desc limit 10"; + List> res = new ArrayList<>(); + try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) { + while (resultSet.next()) { + HashMap model = new HashMap<>(); + model.put(resultSet.getString("sessid"), resultSet.getString("usedsize")); + res.add(model); + } + } catch (Exception e) { + log.error("Failed to query session memory top10", e); + throw new OpsException("Failed to query session memory top10"); + } + return res; + } + + private String connectNum(Connection connection) { + String sql = "SELECT count(*) FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS s"; + String res = null; + try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) { + if (resultSet.next()) { + res = resultSet.getString("count"); + } + } catch (Exception e) { + log.error("Failed to query the number of connections", e); + throw new OpsException("Failed to query the number of connections"); + } + return res; + } + + private String session(Connection connection) { + String sql = "SELECT count(*) FROM pg_stat_activity"; + String res = null; + try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) { + if (resultSet.next()) { + res = resultSet.getString("count"); + } + } catch (Exception e) { + log.error("Failed to query the number of connections", e); + throw new OpsException("Failed to query the number of connections"); + } + return res; + } + + private String lock(Connection connection) { + String sql = "SELECT count(*) FROM pg_locks"; + String res = null; + try (Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql)) { + if (resultSet.next()) { + res = resultSet.getString("count"); + } + } catch (Exception e) { + log.error("Failed to query the number of connections", e); + throw new OpsException("Failed to query the number of connections"); + } + return res; + } + + private String memorySize(String hostId) { + return hostMonitorFacade.getMemoryTotal(hostId); + } + + private String kernel(String hostId) { + return hostMonitorFacade.getCpuCoreNum(hostId); + } + + private String state(SshLogin sshLogin, OpenGaussVersionEnum version, String dataPath, String envPath) { + if (OpenGaussVersionEnum.ENTERPRISE == version) { + String command = "gs_om -t status --detail"; + try { + Map res = new HashMap<>(); + String result = jschExecutorFacade.execCommand(sshLogin, command, envPath); + parseCmStatus(result, res); + parseClusterState(result, res); + parseClusterDataNodeStateAndRole(result, res); + return JSON.toJSONString(res); + } catch (Exception e) { + log.error("Failed to get status information", e); + throw new OpsException("Failed to get status information"); + } + } else { + String command = "gs_ctl status -D " + dataPath; + String result = jschExecutorFacade.execCommand(sshLogin, command, envPath); + return result.contains("no server running") ? "false" : "true"; + } + } + + private static void parseClusterDataNodeStateAndRole(String result, Map res) { + Map nodeState = new HashMap<>(1); + Map nodeRole = new HashMap<>(1); + int datanodeStateIndex = result.indexOf("Datanode State"); + if (datanodeStateIndex >= 0) { + int splitIndex = result.indexOf("------------------", datanodeStateIndex); + String dataNodeStateStr = result.substring(splitIndex); + String[] dataNode = dataNodeStateStr.split("\n"); + parseClusterDataNodeStateAndRole(dataNode, nodeState, nodeRole); + } + res.put("nodeState", nodeState); + res.put("nodeRole", nodeRole); + } + + private static void parseClusterDataNodeStateAndRole(String[] dataNode, Map nodeState, + Map nodeRole) { + for (String s : dataNode) { + String[] s1 = s.replaceAll(" +", " ").split(" "); + String state = ""; + if (s1.length >= 9) { + for (int i = 8; i < s1.length; i++) { + state += (s1[i] + " "); + } + nodeState.put(s1[1], state.trim()); + nodeRole.put(s1[1], s1[7]); + } else if (s1.length >= 8) { + for (int i = 7; i < s1.length; i++) { + state += (s1[i] + " "); + } + nodeState.put(s1[1], state.trim()); + nodeRole.put(s1[1], s1[6]); + } + } + } + + private static void parseClusterState(String result, Map res) { + int clusterStateIndex = result.indexOf("cluster_state"); + String clusterState = null; + if (clusterStateIndex >= 0) { + int splitIndex = result.indexOf(":", clusterStateIndex); + int lineEndIndex = result.indexOf("\n", clusterStateIndex); + clusterState = result.substring(splitIndex + 1, lineEndIndex).trim(); + res.put("cluster_state", clusterState); + } + } + + private static void parseCmStatus(String result, Map res) { + Map cmState = new HashMap<>(1); + int cmIndex = result.indexOf("CMServer State"); + if (cmIndex >= 0) { + int splitIndex = result.indexOf("------------------", cmIndex); + String dataNodeStateStr = result.substring(splitIndex); + String[] dataNode = dataNodeStateStr.split("\n"); + for (String s : dataNode) { + String[] s1 = s.replaceAll(" +", " ").split(" "); + if (s1.length == 6) { + cmState.put(s1[1], s1[5].trim()); + } + } + } + res.put("cmState", cmState); + } + + private List net(String hostId) { + List res = new ArrayList<>(); + boolean hasNetName = true; + String[] split = hostMonitorFacade.getNetMonitor(hostId, hasNetName); + NodeNetMonitor nodeNetMonitor = new NodeNetMonitor(); + nodeNetMonitor.setFace(StrUtil.trim(split[0])); + nodeNetMonitor.setReceive(split[1]); + nodeNetMonitor.setTransmit(split[2]); + res.add(nodeNetMonitor); + return res; + } + + private String memory(String hostId) { + return hostMonitorFacade.getMemoryUsing(hostId); + } + + private String cpu(String hostId) { + return hostMonitorFacade.getCpuUsing(hostId); + } + + private void doUnInstall(UnInstallContext unInstallContext, Boolean force) { + try { + UnInstallContext clone = unInstallContext.clone(); + clusterOpsProviderManager.provider(clone.getOpsClusterEntity().getVersion()) + .orElseThrow(() -> new OpsException("The current version does not support")) + .uninstall(clone); + wsUtil.sendText(unInstallContext.getRetSession(), "FINAL_EXECUTE_EXIT_CODE:0"); + } catch (Exception e) { + if (Objects.nonNull(force) && force) { + OpsClusterEntity opsClusterEntity = unInstallContext.getOpsClusterEntity(); + List opsClusterNodeEntityList = unInstallContext.getOpsClusterNodeEntityList(); + removeById(opsClusterEntity); + opsClusterNodeService.removeByIds(opsClusterNodeEntityList); + } + log.error("Uninstall exception:", e); + wsUtil.sendText(unInstallContext.getRetSession(), "FINAL_EXECUTE_EXIT_CODE:-1"); + } + } + + private List getAllFiles(String path) { + List fileList = new ArrayList<>(); + File file = new File(path); + File[] files = file.listFiles(); + if (files != null) { + for (File f : files) { + if (f.isDirectory()) { + fileList.addAll(getAllFiles(f.getPath())); + } else { + try { + fileList.add(f.getCanonicalPath()); + } catch (IOException ex) { + log.error("get file path error: " + ex.getMessage()); + } + } + } + } + return fileList; + } + + @Override + public void batchConfigGucSetting(GucSettingDto gucSettingDto) { + OpsClusterEntity clusterEntity = getById(gucSettingDto.getClusterId()); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + OpsHostEntity hostEntity = hostFacade.getById(gucSettingDto.getHostId()); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId( + clusterEntity.getClusterId()); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("Cluster node information is empty"); + } + OpsClusterNodeEntity nodeEntity = opsClusterNodeEntities.stream() + .filter(node -> node.getHostId().equals(hostEntity.getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("Cluster node configuration not found")); + String installUserId = nodeEntity.getInstallUserId(); + OpsHostUserEntity userEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(userEntity)) { + throw new OpsException("Installation user information does not exist"); + } + Session session = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), userEntity.getUsername(), + encryptionUtils.decrypt(userEntity.getPassword())) + .orElseThrow(() -> new OpsException("Failed to establish session with host")); + boolean needRestart = false; + for (GucSettingVO settingVO : gucSettingDto.getSettings()) { + if (!settingVO.getHasChanged()) { + continue; + } + if (settingVO.getContext().equals(GucSettingContextEnum.POSTMASTER.getCode())) { + needRestart = true; + } + String dataPath = gucSettingDto.getDataPath(); + if (clusterEntity.getVersion().equals(OpenGaussVersionEnum.MINIMAL_LIST)) { + dataPath = opsClusterNodeEntities.stream() + .filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER) + .findFirst() + .orElseThrow(() -> new OpsException("Master node configuration not found")) + .getDataPath(); + if (clusterEntity.getDeployType() == DeployTypeEnum.CLUSTER) { + dataPath = dataPath + "/master"; + } else { + dataPath = dataPath; + } + } + clusterOpsProviderManager.provider(clusterEntity.getVersion()) + .orElseThrow(() -> new OpsException("The current version does not support")) + .configGucSetting(jschUtil, session, clusterEntity, gucSettingDto.getIsApplyToAllNode(), dataPath, + settingVO); + } + if (needRestart) { + OpsClusterBody clusterBody = new OpsClusterBody(); + clusterBody.setClusterId(clusterEntity.getClusterId()); + clusterBody.setSync(true); + List nodeIds = new ArrayList<>(); + opsClusterNodeEntities.forEach((item) -> { + nodeIds.add(item.getClusterNodeId()); + }); + clusterBody.setNodeIds(nodeIds); + try { + restart(clusterBody); + } catch (OpsException ex) { + String errMsg = "Restart cluster failed: " + ex.getMessage(); + log.error(errMsg); + throw new OpsException(errMsg); + } + } + } + + @Override + public List getGucSettingList(String clusterId, String hostId) throws OpsException { + Connection connection = null; + OpsClusterEntity clusterEntity = getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + try { + connection = DBUtil.getSession(hostEntity.getPublicIp(), clusterEntity.getPort(), + clusterEntity.getDatabaseUsername(), encryptionUtils.decrypt(clusterEntity.getDatabasePassword())) + .orElseThrow(() -> new OpsException("Unable to connect to the database")); + return queryGucSettingList(connection); + } catch (SQLException | ClassNotFoundException e) { + String errMsg = "get connection fail: " + e.getMessage(); + log.error(errMsg); + throw new OpsException(errMsg); + } finally { + if (Objects.nonNull(connection)) { + try { + connection.close(); + } catch (SQLException ex) { + log.error("close connection failed: " + ex.getMessage()); + } + } + } + } + + private List queryGucSettingList(Connection connection) throws OpsException { + List res = new ArrayList<>(); + String sql = "select * from pg_settings"; + try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) { + ResultSet resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { + GucSettingVO gucSettingVO = new GucSettingVO(); + gucSettingVO.setName(resultSet.getString("name")); + gucSettingVO.setValue(resultSet.getString("setting")); + gucSettingVO.setVarType(resultSet.getString("vartype")); + gucSettingVO.setUnit(resultSet.getString("unit")); + gucSettingVO.setMinVal(resultSet.getString("min_val")); + gucSettingVO.setMaxVal(resultSet.getString("max_val")); + gucSettingVO.setShortDesc(resultSet.getString("short_desc")); + gucSettingVO.setExtraDesc(resultSet.getString("extra_desc")); + gucSettingVO.setEnumVals(resultSet.getString("enumvals")); + gucSettingVO.setDefaultVal(resultSet.getString("boot_val")); + gucSettingVO.setContext(resultSet.getString("context")); + res.add(gucSettingVO); + } + } catch (SQLException ex) { + String errMsg = "Querying guc setting failed: " + ex.getMessage(); + log.error(errMsg); + throw new OpsException(errMsg); + } + return res; + } + + private Integer calcDiskUsed(String diskInfo, String path) { + Integer res = 0; + String[] split = diskInfo.split("\n"); + for (String s : split) { + String[] s1 = s.replaceAll("\\s+", " ").split(" "); + if ("/".equals(s1[5])) { + return Integer.parseInt(s1[4].substring(0, s1[4].length() - 1)); + } + if (path.contains(s1[5])) { + return Integer.parseInt(s1[4].substring(0, s1[4].length() - 1)); + } + } + if (res == 0) { + for (String s : split) { + String[] s1 = s.replaceAll("\\s+", " ").split(" "); + int used = Integer.parseInt(s1[4].substring(0, s1[4].length() - 1)); + if (used > res) { + res = used; + } + } + } + return res; + } +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterTaskNodeProviderService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterTaskNodeProviderService.java new file mode 100644 index 000000000..86f2a6240 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterTaskNodeProviderService.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2024-2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskNodeProviderService.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/OpsClusterTaskNodeProviderService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.util.StrUtil; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.dto.OpsClusterTaskNodeDTO; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.service.ops.IOpsClusterTaskNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterTaskService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * OpsClusterTaskNodeProviderService + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@Service("clusterTaskNodeProvider") +public class OpsClusterTaskNodeProviderService { + @Resource + private IOpsClusterTaskNodeService opsClusterTaskNodeService; + @Resource + private IOpsClusterTaskService opsClusterTaskService; + @Resource + private OpsHostRemoteService opsHostRemoteService; + + /** + * create cluster task node + * + * @param insertDto insertDto + * @return result + */ + @Transactional(rollbackFor = Exception.class) + public String createClusterTaskNode(OpsClusterTaskNodeDTO insertDto) { + Assert.isTrue(Objects.nonNull(insertDto), "cluster task node cannot be null"); + String clusterId = insertDto.getClusterId(); + OpsClusterTaskEntity taskEntity = opsClusterTaskService.getById(clusterId); + Assert.isTrue(Objects.nonNull(taskEntity), "cluster task id can not exists " + clusterId); + String clusterNodeId = opsClusterTaskNodeService.saveClusterTaskNode(insertDto); + checkHostAndUserMatched(insertDto.getHostId(), insertDto.getHostUserId()); + updateClusterTask(clusterId); + boolean canInstall = opsClusterTaskService.checkHostAndUserInstallCluster(clusterId, insertDto.getHostId(), + insertDto.getHostUserId()); + Assert.isTrue(canInstall, clusterId + " host has cluster installation task"); + return clusterNodeId; + } + + private void checkHostAndUserMatched(String hostId, String hostUserId) { + List hostUserList = opsHostRemoteService.listHostUserByHostId(hostId); + Optional checkUser = hostUserList.stream() + .filter(hostUser -> hostUser.getHostUserId().equals(hostUserId)) + .findFirst(); + Assert.isTrue(checkUser.isPresent(), "host " + hostId + " user " + hostUserId + " not exists"); + } + + /** + * update cluster task node + * + * @param updateDto updateDto + * @return result + */ + @Transactional(rollbackFor = Exception.class) + public String updateClusterTaskNode(OpsClusterTaskNodeDTO updateDto) { + Assert.isTrue(Objects.nonNull(updateDto), "cluster task update param cannot be null"); + String clusterId = updateDto.getClusterId(); + Assert.isTrue(StrUtil.isNotEmpty(clusterId), "cluster id cannot be empty"); + OpsClusterTaskEntity taskEntity = opsClusterTaskService.getById(clusterId); + Assert.isTrue(Objects.nonNull(taskEntity), "cluster task id is not exists " + clusterId); + String clusterNodeId = updateDto.getClusterNodeId(); + OpsClusterTaskNodeEntity updateNode = opsClusterTaskNodeService.getById(clusterNodeId); + Assert.isTrue(Objects.nonNull(updateNode), "cluster node is not exists " + clusterNodeId); + String msg = opsClusterTaskNodeService.updateClusterTaskNode(updateDto); + checkHostAndUserMatched(updateDto.getHostId(), updateDto.getHostUserId()); + updateClusterTask(clusterId); + boolean canInstall = opsClusterTaskService.checkHostAndUserInstallCluster(clusterId, updateDto.getHostId(), + updateDto.getHostUserId()); + Assert.isTrue(canInstall, clusterId + " host has cluster installation task"); + return msg; + } + + private void updateClusterTask(String clusterId) { + List nodeList = opsClusterTaskNodeService.listByClusterTaskId(clusterId); + OpsClusterTaskNodeEntity master = nodeList.stream() + .filter(node -> node.getNodeType().equals(ClusterRoleEnum.MASTER)) + .findFirst() + .orElseThrow(() -> new OpsException("cluster task must have master node")); + opsClusterTaskService.modifyClusterTaskOfNodeChange(clusterId, master.getHostId(), + master.getHostUserId(), nodeList.size()); + } + + /** + * delete cluster task node + * + * @param clusterId cluster id + * @param nodeId node id + * @return result + */ + @Transactional(rollbackFor = Exception.class) + public String deleteClusterTaskNode(String clusterId, String nodeId) { + Assert.isTrue(StrUtil.isNotEmpty(clusterId), "cluster id cannot be empty"); + Assert.isTrue(StrUtil.isNotEmpty(nodeId), "cluster node id cannot be empty"); + OpsClusterTaskEntity taskEntity = opsClusterTaskService.getById(clusterId); + Assert.isTrue(Objects.nonNull(taskEntity), "cluster task id can not exists " + clusterId); + String msg = opsClusterTaskNodeService.deleteClusterTaskNode(clusterId, nodeId); + updateClusterTask(clusterId); + return msg; + } + + /** + * list cluster node list + * + * @param clusterId cluster + * @return node list + */ + public List listClusterTaskNode(String clusterId) { + return opsClusterTaskNodeService.listByClusterTaskId(clusterId); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterTaskNodeServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterTaskNodeServiceImpl.java new file mode 100644 index 000000000..2c65bd749 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterTaskNodeServiceImpl.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskNodeServiceImpl.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterTaskNodeServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.dto.OpsClusterTaskNodeDTO; +import org.opengauss.admin.plugin.mapper.ops.OpsClusterTaskNodeMapper; +import org.opengauss.admin.plugin.service.ops.IOpsClusterTaskNodeService; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; + +/** + * OpsClusterTaskNodeServiceImpl + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@Slf4j +@Service +public class OpsClusterTaskNodeServiceImpl extends ServiceImpl + implements IOpsClusterTaskNodeService { + @Override + public List listByClusterTaskId(String taskId) { + return list(Wrappers.lambdaQuery(OpsClusterTaskNodeEntity.class) + .eq(OpsClusterTaskNodeEntity::getClusterId, taskId)); + } + + @Override + public List listByInstallHostUser(String hostId, String hostUserId) { + return list(Wrappers.lambdaQuery(OpsClusterTaskNodeEntity.class) + .eq(OpsClusterTaskNodeEntity::getHostId, hostId) + .eq(OpsClusterTaskNodeEntity::getHostUserId, hostUserId)); + } + + @Override + public void removeByClusterId(String taskId) { + remove(Wrappers.lambdaQuery(OpsClusterTaskNodeEntity.class).eq(OpsClusterTaskNodeEntity::getClusterId, taskId)); + } + + @Override + public boolean checkHostPortUsedByCm(String taskId, String hostId, Integer hostPort) { + List cmList = list(Wrappers.lambdaQuery(OpsClusterTaskNodeEntity.class) + .select(OpsClusterTaskNodeEntity::getClusterId) + .notIn(StrUtil.isNotEmpty(taskId), OpsClusterTaskNodeEntity::getClusterId, taskId) + .eq(OpsClusterTaskNodeEntity::getHostId, hostId) + .eq(OpsClusterTaskNodeEntity::getCmPort, hostPort)); + return CollUtil.isNotEmpty(cmList); + } + + + @Override + public String deleteClusterTaskNode(String clusterId, String clusterNodeId) { + removeById(clusterNodeId); + String delNodeMessage = "cluster %s delete node %s success"; + return String.format(delNodeMessage, clusterId, clusterNodeId); + } + + + @Override + public String updateClusterTaskNode(OpsClusterTaskNodeDTO updateDto) { + LambdaUpdateWrapper updateWrapper = + Wrappers.lambdaUpdate(OpsClusterTaskNodeEntity.class); + updateWrapper.set(OpsClusterTaskNodeEntity::getHostId, updateDto.getHostId()) + .set(OpsClusterTaskNodeEntity::getHostUserId, updateDto.getHostUserId()) + .set(OpsClusterTaskNodeEntity::getNodeType, updateDto.getNodeType()) + .set(OpsClusterTaskNodeEntity::getAzOwner, updateDto.getAzOwner()) + .set(OpsClusterTaskNodeEntity::getCmPort, updateDto.getCmPort()) + .set(OpsClusterTaskNodeEntity::getCmDataPath, updateDto.getCmDataPath()) + .set(OpsClusterTaskNodeEntity::getDataPath, updateDto.getDataPath()) + .set(OpsClusterTaskNodeEntity::getAzPriority, updateDto.getAzPriority()) + .set(OpsClusterTaskNodeEntity::getIsCmMaster, updateDto.getIsCMMaster()) + .set(OpsClusterTaskNodeEntity::getEnvCheckDetail, null) + .set(OpsClusterTaskNodeEntity::getEnvCheckResult, null) + .set(OpsClusterTaskNodeEntity::getUpdateTime, LocalDateTime.now()) + .eq(OpsClusterTaskNodeEntity::getClusterNodeId, updateDto.getClusterNodeId()); + update(updateWrapper); + String updateNodeMessage = " cluster %s update node %s success"; + return String.format(updateNodeMessage, updateDto.getClusterId(), updateDto.getClusterNodeId()); + } + + @Override + public String saveClusterTaskNode(OpsClusterTaskNodeDTO insertDto) { + OpsClusterTaskNodeEntity nodeEntity = insertDto.toEntity(); + save(nodeEntity); + return nodeEntity.getClusterNodeId(); + } + + @Override + public List queryClusterNodeAndPortList(String clusterId) { + List nodeList = list(Wrappers.lambdaQuery(OpsClusterTaskNodeEntity.class) + .select(OpsClusterTaskNodeEntity::getClusterNodeId, OpsClusterTaskNodeEntity::getHostId, + OpsClusterTaskNodeEntity::getCmPort) + .eq(OpsClusterTaskNodeEntity::getClusterId, clusterId)); + if (CollUtil.isEmpty(nodeList)) { + return Collections.emptyList(); + } + return nodeList; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterTaskServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterTaskServiceImpl.java new file mode 100644 index 000000000..d7f5f6369 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterTaskServiceImpl.java @@ -0,0 +1,991 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsClusterTaskServiceImpl.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsClusterTaskServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; + +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; + +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.core.vo.HostBean; +import org.opengauss.admin.common.enums.ops.ClusterEnvCheckResultEnum; +import org.opengauss.admin.common.enums.ops.OpsClusterTaskStatusEnum; +import org.opengauss.admin.common.enums.ops.OpsHostPortUsedStatusEnum; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.DateUtils; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskNodeEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackageManagerEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsClusterTaskVO; +import org.opengauss.admin.plugin.domain.model.ops.SshCommandConstants; +import org.opengauss.admin.plugin.domain.model.ops.OpsClusterTaskNodeVO; +import org.opengauss.admin.plugin.domain.model.ops.RetBuffer; +import org.opengauss.admin.plugin.domain.model.ops.InstallContext; +import org.opengauss.admin.plugin.domain.model.ops.cache.TaskManager; +import org.opengauss.admin.plugin.domain.model.ops.dto.OpsClusterTaskDTO; +import org.opengauss.admin.plugin.domain.model.ops.dto.OpsClusterTaskQueryParamDTO; +import org.opengauss.admin.plugin.domain.model.ops.env.EnvProperty; +import org.opengauss.admin.plugin.domain.model.ops.env.HostEnv; +import org.opengauss.admin.plugin.enums.ops.HostEnvStatusEnum; +import org.opengauss.admin.plugin.factory.OperateLogFactory; +import org.opengauss.admin.plugin.mapper.ops.OpsClusterTaskMapper; +import org.opengauss.admin.plugin.service.ops.IOpsClusterTaskService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterTaskNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsPackageManagerV2Service; +import org.opengauss.admin.plugin.service.ops.IOpsClusterLogService; +import org.opengauss.admin.plugin.service.ops.impl.function.CheckDeployTypeService; +import org.opengauss.admin.plugin.service.ops.impl.function.ClusterTaskPathFactory; +import org.opengauss.admin.plugin.service.ops.impl.provider.ProviderManager; +import org.opengauss.admin.plugin.vo.ops.ClusterEnvCheck; +import org.opengauss.admin.plugin.vo.ops.ClusterNodeEnvCheck; +import org.opengauss.admin.plugin.vo.ops.ClusterPortVo; +import org.opengauss.admin.plugin.vo.ops.TaskStatusVo; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import jakarta.annotation.Resource; + +import java.time.LocalDateTime; +import java.util.HashSet; +import java.util.Objects; +import java.util.List; +import java.util.Map; +import java.util.LinkedList; +import java.util.HashMap; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * OpsClusterTaskServiceImpl + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@Slf4j +@Service +public class OpsClusterTaskServiceImpl extends ServiceImpl + implements IOpsClusterTaskService { + private static final ObjectMapper objectMapper = new ObjectMapper(); + private static final String PASSWORD_REGEX = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*]).{8,}$"; + + @Resource + private IOpsClusterTaskNodeService opsClusterTaskNodeService; + @Resource + private OpsClusterEnvService opsClusterEnvService; + @Resource + private IOpsClusterLogService opsClusterLogService; + @Resource + private ProviderManager providerManager; + @Resource + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + @Resource + private IOpsPackageManagerV2Service opsPackageManagerV2Service; + @Resource + private OpsHostRemoteService opsHostRemoteService; + @Resource + private CheckDeployTypeService checkDeployTypeService; + @Resource + private ClusterTaskPathFactory clusterTaskPathFactory; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + + @Override + public IPage pageByCondition(Page page, OpsClusterTaskQueryParamDTO dto) { + // 查询集群任务列表 - 条件组装 + LambdaQueryWrapper pageWrapper = Wrappers.lambdaQuery(OpsClusterTaskEntity.class); + pageWrapper.eq(StrUtil.isNotEmpty(dto.getTaskId()), OpsClusterTaskEntity::getClusterId, dto.getTaskId()) + .eq(StrUtil.isNotEmpty(dto.getClusterName()), OpsClusterTaskEntity::getClusterName, dto.getClusterName()) + .eq(StrUtil.isNotEmpty(dto.getOs()), OpsClusterTaskEntity::getOs, dto.getOs()) + .eq(StrUtil.isNotEmpty(dto.getCpuArch()), OpsClusterTaskEntity::getCpuArch, dto.getCpuArch()) + .eq(StrUtil.isNotEmpty(dto.getOpenGaussVersionNum()), OpsClusterTaskEntity::getVersionNum, + dto.getOpenGaussVersionNum()) + .eq(StrUtil.isNotEmpty(dto.getHostId()), OpsClusterTaskEntity::getHostId, dto.getHostId()) + .eq(StrUtil.isNotEmpty(dto.getHostUserId()), OpsClusterTaskEntity::getHostUserId, dto.getHostUserId()) + .eq(dto.getNodeNum() > 0, OpsClusterTaskEntity::getClusterNodeNum, dto.getNodeNum()) + .eq(Objects.nonNull(dto.getOpenGaussVersion()), OpsClusterTaskEntity::getVersion, + dto.getOpenGaussVersion()); + OpsClusterTaskStatusEnum status = dto.getStatus(); + if (Objects.isNull(status)) { + pageWrapper.notIn(OpsClusterTaskEntity::getStatus, OpsClusterTaskStatusEnum.DRAFT); + } else { + pageWrapper.eq(OpsClusterTaskEntity::getStatus, status.getStatus()); + } + pageWrapper.orderByDesc(OpsClusterTaskEntity::getCreateTime); + Page pageResult = page(page, pageWrapper); + populateTaskInfo(pageResult.getRecords()); + return pageResult; + } + + private void populateTaskInfo(List records) { + if (CollectionUtils.isEmpty(records)) { + return; + } + List hostIds = new LinkedList<>(); + List hostUserIds = new LinkedList<>(); + collectClusterTaskHostInfos(records, hostIds, hostUserIds); + Map hostMap = queryHostByHostIds(hostIds); + Map hostUserMap = queryHostUsernameByHostUserIds(hostUserIds); + records.forEach(nodeVo -> { + populateTaskHostAndUserInfo(nodeVo, hostMap, hostUserMap); + }); + } + + private void populateTaskHostAndUserInfo(Object obj, Map hostMap, + Map hostUserMap) { + OpsHostEntity host = hostMap.get(getHostId(obj)); + if (Objects.nonNull(host)) { + setTaskHostAndUserInfo(obj, host, hostUserMap); + } else { + setTaskHostAndUserInfoNull(obj); + } + } + + private void setTaskHostAndUserInfo(Object obj, OpsHostEntity host, Map hostUserMap) { + if (obj instanceof OpsClusterTaskEntity) { + ((OpsClusterTaskEntity) obj).setHostIp(host.getPublicIp()); + ((OpsClusterTaskEntity) obj).setDisplayHostIp(host.getDisplayIp()); + ((OpsClusterTaskEntity) obj).setHostUsername(hostUserMap.get(getHostUserId(obj))); + } else if (obj instanceof OpsClusterTaskNodeVO) { + ((OpsClusterTaskNodeVO) obj).setHostIp(host.getPublicIp()); + ((OpsClusterTaskNodeVO) obj).setDisplayHostIp(host.getDisplayIp()); + ((OpsClusterTaskNodeVO) obj).setHostUsername(hostUserMap.get(getHostUserId(obj))); + } else { + throw new OpsException("setTaskHostAndUserInfo param type error "); + } + } + + private void setTaskHostAndUserInfoNull(Object obj) { + if (obj instanceof OpsClusterTaskEntity) { + ((OpsClusterTaskEntity) obj).setHostIp(null); + ((OpsClusterTaskEntity) obj).setDisplayHostIp(null); + ((OpsClusterTaskEntity) obj).setHostUserId(null); + ((OpsClusterTaskEntity) obj).setHostUsername(null); + } else if (obj instanceof OpsClusterTaskNodeVO) { + ((OpsClusterTaskNodeVO) obj).setHostIp(null); + ((OpsClusterTaskNodeVO) obj).setDisplayHostIp(null); + ((OpsClusterTaskNodeVO) obj).setHostUserId(null); + ((OpsClusterTaskNodeVO) obj).setHostUsername(null); + } else { + throw new OpsException("setTaskHostAndUserInfoNull param type error "); + } + } + + private String getHostId(Object obj) { + if (obj instanceof OpsClusterTaskEntity) { + return ((OpsClusterTaskEntity) obj).getHostId(); + } else if (obj instanceof OpsClusterTaskNodeVO) { + return ((OpsClusterTaskNodeVO) obj).getHostId(); + } else { + return StringUtils.EMPTY; + } + } + + private String getHostUserId(Object obj) { + if (obj instanceof OpsClusterTaskEntity) { + return ((OpsClusterTaskEntity) obj).getHostUserId(); + } else if (obj instanceof OpsClusterTaskNodeVO) { + return ((OpsClusterTaskNodeVO) obj).getHostUserId(); + } else { + return StringUtils.EMPTY; + } + } + + private Map queryHostUsernameByHostUserIds(List hostUserIds) { + if (CollectionUtils.isEmpty(hostUserIds)) { + return CollectionUtils.newHashMap(); + } + List hostUserList = opsHostRemoteService.getOpsHostUserList(hostUserIds); + return hostUserList.stream() + .collect(Collectors.toMap(OpsHostUserEntity::getHostUserId, OpsHostUserEntity::getUsername)); + } + + private Map queryHostByHostIds(List hostIds) { + if (CollectionUtils.isEmpty(hostIds)) { + return CollectionUtils.newHashMap(); + } + List hostList = opsHostRemoteService.getOpsHostList(hostIds); + return hostList.stream().collect(Collectors.toMap(OpsHostEntity::getHostId, Function.identity())); + } + + private void collectClusterTaskHostInfos(List records, List hostIds, + List hostUserIds) { + if (hostIds != null) { + hostIds.addAll( + records.stream().map(OpsClusterTaskEntity::getHostId).distinct().collect(Collectors.toList())); + } + if (hostUserIds != null) { + hostUserIds.addAll( + records.stream().map(OpsClusterTaskEntity::getHostUserId).distinct().collect(Collectors.toList())); + } + } + + @Override + public Map deleteClusterTask(List taskIds) { + Map result = new HashMap<>(); + try { + List list = listByIds(taskIds); + Assert.isTrue(CollectionUtils.isNotEmpty(list), "cluster install task not found:" + list); + Map taskMap = convertClusterTaskToMap(list); + taskIds.forEach(taskId -> { + if (taskMap.containsKey(taskId)) { + OpsClusterTaskEntity task = taskMap.get(taskId); + if (Objects.equals(task.getStatus(), OpsClusterTaskStatusEnum.RUNNING)) { + log.warn("cluster install task is running, cannot be deleted {}", taskId); + result.put(taskId, "cluster install task is running, cannot be deleted"); + return; + } + opsClusterLogService.deleteOperateLogByClusterId(taskId); + opsClusterTaskNodeService.removeByClusterId(taskId); + removeById(taskId); + log.warn("delete cluster install task:{}", taskId); + result.put(taskId, "deleted successfully"); + } else { + result.put(taskId, "cluster install task not found"); + log.warn("cluster install task not found:{}", taskId); + } + }); + } catch (Exception ex) { + throw new OpsException(ex.getMessage()); + } + return result; + } + + @Override + public String deleteClusterTaskForce(String taskId) { + OpsClusterTaskEntity entity = getById(taskId); + Assert.isTrue(Objects.nonNull(entity), "cluster install task not found"); + opsClusterLogService.deleteOperateLogByClusterId(taskId); + opsClusterTaskNodeService.removeByClusterId(taskId); + removeById(taskId); + TaskManager.remove(providerManager.getBusinessId(taskId)); + providerManager.remove(taskId); + log.warn("delete cluster install task force :{}", taskId); + return taskId; + } + + private static Map convertClusterTaskToMap(List list) { + return list.stream().collect(Collectors.toMap(OpsClusterTaskEntity::getClusterId, entity -> entity)); + } + + @Override + public OpsClusterTaskVO queryClusterTaskDetail(String taskId) { + try { + OpsClusterTaskEntity task = getById(taskId); + Assert.isTrue(Objects.nonNull(task), "集群任务不存在:" + taskId); + List nodeList = opsClusterTaskNodeService.listByClusterTaskId(taskId); + List hostIds = new LinkedList<>(); + List hostUserIds = new LinkedList<>(); + collectClusterTaskNodeHostInfos(nodeList, hostIds, hostUserIds); + Map hostMap = queryHostByHostIds(hostIds); + Map hostUserMap = queryHostUsernameByHostUserIds(hostUserIds); + populateTaskHostAndUserInfo(task, hostMap, hostUserMap); + List nodeVoList = nodeList.stream() + .map(OpsClusterTaskNodeEntity::toVo) + .peek(nodeVo -> { + populateTaskHostAndUserInfo(nodeVo, hostMap, hostUserMap); + }) + .collect(Collectors.toList()); + OpsClusterTaskVO taskVo = task.toVo(); + taskVo.setClusterNodes(nodeVoList); + return taskVo; + } catch (Exception ex) { + throw new OpsException(ex.getMessage()); + } + } + + private void collectClusterTaskNodeHostInfos(List nodeList, List hostIds, + List hostUserIds) { + if (hostIds != null) { + hostIds.addAll( + nodeList.stream().map(OpsClusterTaskNodeEntity::getHostId).distinct().collect(Collectors.toList())); + } + if (hostUserIds != null) { + hostUserIds.addAll( + nodeList.stream().map(OpsClusterTaskNodeEntity::getHostUserId).distinct().collect(Collectors.toList())); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String createClusterTask(OpsClusterTaskDTO dto) { + OpsClusterTaskEntity entity = dto.toEntity(); + entity.setDatabasePassword(encryptionUtils.decrypt(entity.getDatabasePassword())); + try { + Assert.isTrue(checkPasswordStrength(entity.getDatabasePassword()), + "database password is not strong enough"); + checkEnvironmentVariablePath(entity); + checkPackageInfo(entity); + Assert.isTrue(!checkClusterNameExist(null, entity.getClusterName()), + "cluster name " + entity.getClusterName() + " is exist"); + entity.setStatus(OpsClusterTaskStatusEnum.DRAFT); + entity.setDatabasePassword(encryptionUtils.encrypt(entity.getDatabasePassword())); + save(entity); + OperateLogFactory.operateCreate(entity.getClusterId()); + log.info("Create cluster task success, clusterId: {}", entity.getClusterId()); + } catch (Exception ex) { + log.error("Create cluster task failed", ex); + throw new OpsException(ex.getMessage()); + } + return entity.getClusterId(); + } + + @Override + public boolean checkClusterTask(String clusterId) { + // 任务ID校验 + OpsClusterTaskEntity taskEntity = getById(clusterId); + Assert.isTrue(Objects.nonNull(taskEntity), "cluster task id can not exists " + clusterId); + // 部署类型与节点信息校验 + checkDeployTypeService.check(taskEntity.getVersion(), taskEntity.getDeployType(), + taskEntity.getClusterNodeNum()); + // 节点信息校验 + List nodeEntityList = opsClusterTaskNodeService.listByClusterTaskId(clusterId); + Assert.isTrue(CollectionUtils.isNotEmpty(nodeEntityList), "cluster task node is empty"); + Map hostMap = new HashMap<>(); + localCheckClusterTaskHost(clusterId, nodeEntityList, hostMap, taskEntity); + // 节点远端服务器信息合法性检查 + remoteCheckHostEnvInfo(nodeEntityList, taskEntity, hostMap); + return true; + } + + private void localCheckClusterTaskHost(String clusterId, List nodeEntityList, + Map hostMap, OpsClusterTaskEntity taskEntity) { + Set hostInfoSet = new HashSet<>(); + // 节点本地信息合法性检查 + nodeEntityList.forEach(node -> { + // 节点安装信息检查 + String hostId = node.getHostId(); + Assert.isTrue(!hostInfoSet.contains(hostId), "HostId is repeated"); + Assert.isTrue(!hostInfoSet.contains(node.getHostUserId()), "Host user Id is repeated"); + hostInfoSet.add(hostId); + hostInfoSet.add(node.getHostUserId()); + OpsHostEntity host = opsHostRemoteService.getHost(hostId); + hostMap.put(hostId, host); + String nodePublicIp = host.getPublicIp(); + OpsHostUserEntity hostUser = opsHostRemoteService.getOpsHostUser(node.getHostUserId()); + boolean canInstall = checkHostAndUserInstallCluster(clusterId, hostId, node.getHostUserId()); + Assert.isTrue(canInstall, + clusterId + " host has cluster installation task: " + nodePublicIp + "_(" + hostUser.getUsername() + + ")"); + // 集群任务端口重复检查 + String databasePort = String.valueOf(taskEntity.getDatabasePort()); + long sameDatabasePortCount = count(Wrappers.lambdaQuery(OpsClusterTaskEntity.class) + .select(OpsClusterTaskEntity::getDatabasePort) + .notIn(OpsClusterTaskEntity::getClusterId, clusterId) + .eq(OpsClusterTaskEntity::getHostId, hostId) + .eq(OpsClusterTaskEntity::getDatabasePort, databasePort)); + Assert.isTrue(sameDatabasePortCount == 0, + "task database port is used: " + nodePublicIp + "_(" + databasePort + ")"); + if (taskEntity.getEnableCmTool()) { + // 数据库端口与CM端口检测 + String cmPort = String.valueOf(node.getCmPort()); + Assert.isTrue(!StrUtil.equals(databasePort, cmPort), "database port and cm port must not equal"); + // 集群节点CM端口检查 + boolean isNodeCmUsed = opsClusterTaskNodeService.checkHostPortUsedByCm(clusterId, hostId, + node.getCmPort()); + Assert.isTrue(!isNodeCmUsed, "host cm port is used: " + nodePublicIp + "_(" + cmPort + ")"); + } + }); + } + + private void remoteCheckHostEnvInfo(List nodeEntityList, OpsClusterTaskEntity taskEntity, + Map hostMap) { + nodeEntityList.forEach(node -> { + // 节点磁盘空间检查 + checkTaskDirDiskSpace(taskEntity, node); + // 集群任务端口重复检查 + String databasePort = String.valueOf(taskEntity.getDatabasePort()); + // 集群节点数据库端口检查 + String hostId = node.getHostId(); + boolean isHostUsed = opsHostRemoteService.portUsed(hostId, Integer.valueOf(databasePort)); + OpsHostEntity host = hostMap.get(hostId); + Assert.isTrue(!isHostUsed, "host port is used: " + host.getPublicIp() + "_(" + databasePort + ")"); + if (taskEntity.getEnableCmTool()) { + // 数据库端口与CM端口检测 + String cmPort = String.valueOf(node.getCmPort()); + // 集群节点CM端口检查 + boolean isHostCmUsed = opsHostRemoteService.portUsed(hostId, Integer.valueOf(cmPort)); + Assert.isTrue(!isHostCmUsed, "host port is used: " + host.getPublicIp() + "_(" + cmPort + ")"); + } + }); + } + + /** + * check host instance can install cluster node + * + * @param hostIp host ip + * @param hostUsername host username + * @return boolean + */ + public boolean checkHostInstanceCanInstallClusterNode(String hostIp, String hostUsername) { + OpsHostEntity host = opsHostRemoteService.getByPublicIp(hostIp); + Assert.isTrue(Objects.nonNull(host), "host is not exist: " + hostIp); + OpsHostUserEntity userEntity = opsHostRemoteService.getHostUserByUsername(host.getHostId(), hostUsername); + Assert.isTrue(Objects.nonNull(userEntity), "host user is not exist: " + hostIp + " " + hostUsername); + return checkHostAndUserInstallCluster("", host.getHostId(), userEntity.getHostUserId()); + } + + /** + * Check if the host instance can be assigned a cluster installation task + * if return true ,current host instance can be assigned a cluster installation task + * + * @param clusterId clusterId + * @param hostId hostId + * @param hostUserId hostUserId + * @return boolean + */ + public boolean checkHostAndUserInstallCluster(String clusterId, String hostId, String hostUserId) { + return opsClusterTaskNodeService.count(Wrappers.lambdaQuery(OpsClusterTaskNodeEntity.class) + .select(OpsClusterTaskNodeEntity::getClusterNodeId) + .notIn(StrUtil.isNotEmpty(clusterId), OpsClusterTaskNodeEntity::getClusterId, clusterId) + .eq(OpsClusterTaskNodeEntity::getHostId, hostId) + .eq(OpsClusterTaskNodeEntity::getHostUserId, hostUserId)) == 0; + } + + @Override + public void modifyClusterTaskOfNodeChange(String clusterId, String hostId, String hostUserId, int nodeCount) { + OpsClusterTaskEntity taskEntity = getById(clusterId); + Assert.isTrue(Objects.nonNull(taskEntity), "cluster task id not exist:" + clusterId); + update(Wrappers.lambdaUpdate(OpsClusterTaskEntity.class) + .set(OpsClusterTaskEntity::getHostId, hostId) + .set(OpsClusterTaskEntity::getHostUserId, hostUserId) + .set(OpsClusterTaskEntity::getClusterNodeNum, nodeCount) + .set(OpsClusterTaskEntity::getStatus, OpsClusterTaskStatusEnum.DRAFT) + .set(OpsClusterTaskEntity::getEnvCheckResult, null) + .set(OpsClusterTaskEntity::getUpdateTime, LocalDateTime.now()) + .set(OpsClusterTaskEntity::getRemark, "reset task draft") + .eq(OpsClusterTaskEntity::getClusterId, clusterId)); + } + + private void checkPackageInfo(OpsClusterTaskEntity entity) { + String packageId = entity.getPackageId(); + OpsPackageManagerEntity packageEntity = opsPackageManagerV2Service.getById(packageId); + OpsHostEntity host = opsHostRemoteService.getHost(entity.getHostId()); + Assert.isTrue(Objects.nonNull(host), "host " + entity.getHostId() + " not exist"); + Assert.isTrue(Objects.nonNull(packageEntity), "Package " + packageId + " not exist"); + String packageVersion = packageEntity.getPackageVersion(); + Assert.isTrue(StrUtil.equalsIgnoreCase(packageVersion, entity.getVersion().name()), + "task and package version is not match pkg: " + packageVersion + " , task: " + entity.getVersion().name()); + String cpuArch = packageEntity.getCpuArch(); + Assert.isTrue(StrUtil.equalsIgnoreCase(cpuArch, host.getCpuArch()), + "host and package cpuArch is not match pkg: " + cpuArch + " , host: " + host.getCpuArch()); + String os = packageEntity.getOs(); + Assert.isTrue(StrUtil.equalsIgnoreCase(os, host.getOs()), + "host os and package os is not match pkg: " + os + " , host: " + host.getOs()); + String osVersion = packageEntity.getOsVersion(); + Assert.isTrue(StrUtil.equalsIgnoreCase(osVersion, host.getOsVersion()), + "host and package os version is not match pkg: " + os + " " + osVersion + " , host: " + + host.getOsVersion()); + } + + private void checkEnvironmentVariablePath(OpsClusterTaskEntity entity) { + String envPath = entity.getEnvPath(); + if (StrUtil.isEmpty(envPath)) { + entity.setEnvPath(SshCommandConstants.DEFAULT_ENV_BASHRC); + } else { + if (!envPath.endsWith(".bashrc")) { + log.warn("The host {} environment variable path {} is not a bashrc file, the default path will be used", + entity.getClusterName(), envPath); + entity.setEnvPath( + "/home/" + entity.getHostUsername() + "/cluster_env_" + entity.getClusterName() + ".bashrc"); + } + } + } + + @Override + public List listHostUserByHostId(String hostId) { + return opsHostRemoteService.listHostUserByHostId(hostId); + } + + @Override + public OpsHostEntity getHostByIp(String hostIp) { + return opsHostRemoteService.getByPublicIp(hostIp); + } + + @Override + public OpsHostPortUsedStatusEnum checkHostPort(String taskId, String hostId, Integer hostPort) { + List hostDbPortList = list(Wrappers.lambdaQuery(OpsClusterTaskEntity.class) + .select(OpsClusterTaskEntity::getDatabasePort) + .notIn(StrUtil.isNotEmpty(taskId), OpsClusterTaskEntity::getClusterId, taskId) + .eq(OpsClusterTaskEntity::getHostId, hostId) + .eq(OpsClusterTaskEntity::getDatabasePort, hostPort)); + if (CollUtil.isNotEmpty(hostDbPortList)) { + return OpsHostPortUsedStatusEnum.DATABASE_USED; + } + if (opsClusterTaskNodeService.checkHostPortUsedByCm(taskId, hostId, hostPort)) { + return OpsHostPortUsedStatusEnum.DATABASE_CM_USED; + } + boolean isHostUsed = opsHostRemoteService.portUsed(hostId, hostPort); + return isHostUsed ? OpsHostPortUsedStatusEnum.HOST_USED : OpsHostPortUsedStatusEnum.NO_USED; + } + + @Override + public Map checkTaskHostPort(String clusterId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsClusterTaskEntity.class); + queryWrapper.select(OpsClusterTaskEntity::getHostId, OpsClusterTaskEntity::getDatabasePort) + .eq(OpsClusterTaskEntity::getClusterId, clusterId); + OpsClusterTaskEntity taskEntity = getOne(queryWrapper); + Assert.isTrue(Objects.nonNull(taskEntity), "cluster id is not exist"); + // 查询集群节点列表 + List hostPortList = opsClusterTaskNodeService.queryClusterNodeAndPortList(clusterId); + Assert.isTrue(CollUtil.isNotEmpty(hostPortList), "cluster node is not invalid"); + // 校验节点主机端口是否被使用 + Map checkResult = new HashMap<>(); + hostPortList.forEach(hostNode -> { + String clusterNodeId = hostNode.getClusterNodeId(); + String hostId = hostNode.getHostId(); + String databasePort = String.valueOf(taskEntity.getDatabasePort()); + String cmPort = String.valueOf(hostNode.getCmPort()); + ClusterPortVo nodePort = new ClusterPortVo(clusterNodeId, databasePort, cmPort); + // 集群节点数据库端口检查 + List hostDbPortList = list(Wrappers.lambdaQuery(OpsClusterTaskEntity.class) + .select(OpsClusterTaskEntity::getDatabasePort) + .notIn(OpsClusterTaskEntity::getClusterId, clusterId) + .eq(OpsClusterTaskEntity::getHostId, hostId) + .eq(OpsClusterTaskEntity::getDatabasePort, databasePort)); + if (CollUtil.isNotEmpty(hostDbPortList)) { + nodePort.setDatabasePortStatus(OpsHostPortUsedStatusEnum.DATABASE_USED); + } else { + boolean isHostUsed = opsHostRemoteService.portUsed(hostId, Integer.valueOf(databasePort)); + nodePort.setDatabasePortStatus( + isHostUsed ? OpsHostPortUsedStatusEnum.HOST_USED : OpsHostPortUsedStatusEnum.NO_USED); + } + // 集群节点CM端口检查 + boolean isNodeCmUsed = opsClusterTaskNodeService.checkHostPortUsedByCm(clusterId, hostNode.getHostId(), + hostNode.getCmPort()); + if (isNodeCmUsed) { + nodePort.setCmPortStatus(OpsHostPortUsedStatusEnum.DATABASE_CM_USED); + } else { + boolean isHostUsed = opsHostRemoteService.portUsed(hostId, Integer.valueOf(cmPort)); + nodePort.setDatabasePortStatus( + isHostUsed ? OpsHostPortUsedStatusEnum.HOST_USED : OpsHostPortUsedStatusEnum.NO_USED); + } + checkResult.put(nodePort.getClusterNodeId(), nodePort); + }); + return checkResult; + } + + @Override + public Map batchInstallCluster(List taskIds) { + Map result = new HashMap<>(); + try { + List list = listByIds(taskIds); + Assert.isTrue(CollectionUtils.isNotEmpty(list), "cluster task is not exist : " + taskIds); + Map taskMap = convertClusterTaskToMap(list); + taskIds.forEach(taskId -> { + OpsClusterTaskEntity task = taskMap.get(taskId); + if (task != null && OpsClusterTaskStatusEnum.isPending(task.getStatus())) { + result.put(taskId, "cluster task add cluster task provider queue : " + task.getStatus()); + providerManager.addClusterTask(taskId); + updateClusterTaskStatus(task, OpsClusterTaskStatusEnum.WAITING, ""); + } else { + result.put(taskId, task != null + ? "cluster task is not pending : " + task.getStatus() + : "cluster task is not exist"); + } + }); + threadPoolTaskExecutor.submit(this::asyncStartInstallProvider); + } catch (Exception ex) { + throw new OpsException(ex.getMessage()); + } + return result; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void reInstallCluster(String taskId) { + OpsClusterTaskEntity task = getById(taskId); + Assert.isTrue(Objects.nonNull(task), "cluster task is not exist : " + taskId); + if (OpsClusterTaskStatusEnum.isFailed(task.getStatus())) { + providerManager.addClusterTask(taskId); + updateClusterTaskStatus(task, OpsClusterTaskStatusEnum.WAITING, ""); + } else { + throw new OpsException("cluster task is not failed : " + task.getStatus()); + } + threadPoolTaskExecutor.submit(this::asyncStartInstallProvider); + } + + public void asyncStartInstallProvider() { + while (providerManager.hasTaskWaiting()) { + String clusterId = providerManager.poll(); + if (clusterId != null) { + // 执行安装前再次检查当前任务是否存在 + OpsClusterTaskEntity taskEntity = getById(clusterId); + if (taskEntity == null) { + log.warn("install:{}", clusterId + " cluster task not found"); + continue; + } + log.info("install:{}", JSON.toJSONString(taskEntity)); + boolean isInstallSuccess = true; + String installMessage = ""; + try { + InstallContext installContext = providerManager.populateInstallContext(taskEntity); + installContext.setRetBuffer(new RetBuffer(clusterId)); + installContext.checkTaskConfig(); + Future future = threadPoolTaskExecutor.submit(() -> { + updateClusterTaskStatus(taskEntity, OpsClusterTaskStatusEnum.RUNNING, ""); + providerManager.doInstall(installContext); + }); + TaskManager.registry(providerManager.getBusinessId(clusterId), future); + } catch (OpsException ex) { + log.error("install:{} {} ", clusterId, ex.getMessage(), ex); + isInstallSuccess = false; + installMessage = ex.getMessage(); + OperateLogFactory.operateInstall(taskEntity.getClusterId(), ex.toString()); + } finally { + if (!isInstallSuccess) { + updateClusterTaskStatus(taskEntity, OpsClusterTaskStatusEnum.FAILED, installMessage); + } + } + } + } + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void updateClusterTask(OpsClusterTaskDTO dto) { + OpsClusterTaskEntity entity = dto.toEntity(); + checkPackageInfo(entity); + Assert.isTrue(!checkClusterNameExist(entity.getClusterId(), entity.getClusterName()), + "cluster name " + entity.getClusterName() + " is exist"); + entity.setStatus(OpsClusterTaskStatusEnum.DRAFT); + // update task + Assert.isTrue(updateById(entity), + "cluster task update failed, cluster task id not exist:" + dto.getClusterId()); + Assert.isTrue(checkPasswordStrength(encryptionUtils.decrypt(entity.getDatabasePassword())), + "database password is not strong enough"); + OperateLogFactory.operateUpdate(entity.getClusterId()); + } + + @Override + public void confirmClusterTask(String taskId) { + OpsClusterTaskEntity task = getById(taskId); + Assert.isTrue(Objects.nonNull(task), "cluster task not exist :" + taskId); + Assert.isTrue(Objects.equals(task.getStatus(), OpsClusterTaskStatusEnum.DRAFT), + "cluster task status not correct, can't confirm"); + Assert.isTrue(Objects.equals(task.getEnvCheckResult(), ClusterEnvCheckResultEnum.SUCCESS), + "cluster task env check not pass, can't confirm"); + updateClusterTaskStatus(task, OpsClusterTaskStatusEnum.PENDING, ""); + OperateLogFactory.operateConfirm(taskId); + log.info("cluster task confirm success, taskId:{}", taskId); + } + + private void updateClusterTaskStatus(OpsClusterTaskEntity task, OpsClusterTaskStatusEnum status, String remark) { + task.setStatus(status); + task.setRemark(remark); + updateClusterTaskStatus(task.getClusterId(), status, remark); + } + + @Override + public void updateClusterTaskStatus(String clusterId, OpsClusterTaskStatusEnum status, String remark) { + update(Wrappers.lambdaUpdate(OpsClusterTaskEntity.class) + .set(OpsClusterTaskEntity::getStatus, status) + .set(OpsClusterTaskEntity::getRemark, remark) + .set(OpsClusterTaskEntity::getUpdateTime, LocalDateTime.now()) + .eq(OpsClusterTaskEntity::getClusterId, clusterId)); + log.info("update cluster task status {} clusterId:{}", status, clusterId); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public ClusterEnvCheck checkClusterInstallEnvironment(String taskId) { + OpsClusterTaskEntity task = getById(taskId); + Assert.isTrue(Objects.nonNull(task), "cluster task not exist :" + taskId); + // 执行环境检查 + List nodes = opsClusterTaskNodeService.listByClusterTaskId(taskId); + Assert.isTrue(CollUtil.isNotEmpty(nodes), "cluster task node not exist :" + taskId); + Map envCheckMap = new HashMap<>(); + nodes.forEach(node -> { + String hostId = node.getHostId(); + HostEnv hostEnv = opsClusterEnvService.env(hostId); + ClusterNodeEnvCheck envCheck = new ClusterNodeEnvCheck(); + envCheck.setClusterNodeId(node.getClusterNodeId()); + envCheck.setEnvCheckDetails(hostEnv); + envCheck.setResult(populateNodeEnvCheckResult(hostEnv)); + envCheckMap.put(node.getClusterNodeId(), envCheck); + node.setEnvCheckResult(envCheck.getResult()); + node.setEnvCheckDetail(parseEnvCheckDetails(envCheck.getEnvCheckDetails())); + }); + // 更新集群安装任务的最终环境检查结果 + boolean isAllMatch = envCheckMap.values() + .stream() + .allMatch(envCheck -> Objects.equals(envCheck.getResult(), ClusterEnvCheckResultEnum.SUCCESS)); + task.setEnvCheckResult(isAllMatch ? ClusterEnvCheckResultEnum.SUCCESS : ClusterEnvCheckResultEnum.FAILED); + updateById(task); + opsClusterTaskNodeService.updateBatchById(nodes); + // 环境检查日志在执行完成后记录。 + envCheckMap.keySet().forEach(nodeId -> { + OperateLogFactory.operateCheckEnvironment(taskId, nodeId, envCheckMap.get(nodeId)); + }); + ClusterEnvCheck clusterEnvCheck = new ClusterEnvCheck(); + clusterEnvCheck.setClusterId(taskId); + clusterEnvCheck.setResult(task.getEnvCheckResult()); + clusterEnvCheck.setEnvCheckDetails(new LinkedList<>(envCheckMap.values())); + log.info("cluster task env check {}", taskId); + return clusterEnvCheck; + } + + @Override + public ClusterEnvCheck getClusterInstallEnvironment(String taskId) { + OpsClusterTaskEntity task = getById(taskId); + Assert.isTrue(Objects.nonNull(task), "cluster task not exist :" + taskId); + List nodes = opsClusterTaskNodeService.listByClusterTaskId(taskId); + Assert.isTrue(Objects.nonNull(nodes), "cluster task node not exist :" + taskId); + ClusterEnvCheck clusterEnvCheck = new ClusterEnvCheck(); + clusterEnvCheck.setClusterId(taskId); + clusterEnvCheck.setResult(task.getEnvCheckResult()); + List nodeList = new LinkedList<>(); + nodes.forEach(node -> { + ClusterNodeEnvCheck nodeEnvCheck = new ClusterNodeEnvCheck(); + nodeEnvCheck.setClusterNodeId(node.getClusterNodeId()); + nodeEnvCheck.setResult(node.getEnvCheckResult()); + nodeEnvCheck.setEnvCheckDetails(parseEnvCheckDetails(node.getEnvCheckDetail())); + nodeList.add(nodeEnvCheck); + }); + clusterEnvCheck.setEnvCheckDetails(nodeList); + return clusterEnvCheck; + } + + private HostEnv parseEnvCheckDetails(String hostEnvText) { + try { + return objectMapper.readValue(hostEnvText, HostEnv.class); + } catch (JsonProcessingException ignore) { + log.warn("Failed to parse host env details: {}", hostEnvText); + } + return null; + } + + private String parseEnvCheckDetails(HostEnv hostEnv) { + try { + return objectMapper.writeValueAsString(hostEnv); + } catch (JsonProcessingException ignore) { + log.warn("Failed to parse host env details: {}", hostEnv); + } + return null; + } + + private ClusterEnvCheckResultEnum populateNodeEnvCheckResult(HostEnv hostEnv) { + if (checkHostEnvIsEmpty(hostEnv)) { + return ClusterEnvCheckResultEnum.FAILED; + } + Boolean hardStatusHasError = checkNodeEnvCheckResultHasError(hostEnv.getHardwareEnv().getEnvProperties()); + Boolean softStatusHasError = checkNodeEnvCheckResultHasError(hostEnv.getSoftwareEnv().getEnvProperties()); + if (hardStatusHasError || softStatusHasError) { + return ClusterEnvCheckResultEnum.FAILED; + } else { + return ClusterEnvCheckResultEnum.SUCCESS; + } + } + + private boolean checkHostEnvIsEmpty(HostEnv hostEnv) { + if (Objects.isNull(hostEnv)) { + return true; + } + if (Objects.isNull(hostEnv.getHardwareEnv()) || Objects.isNull(hostEnv.getSoftwareEnv())) { + return true; + } + return CollUtil.isEmpty(hostEnv.getHardwareEnv().getEnvProperties()) || CollUtil.isEmpty( + hostEnv.getSoftwareEnv().getEnvProperties()); + } + + private boolean checkNodeEnvCheckResultHasError(List envProperties) { + return envProperties.stream().map(EnvProperty::getStatus).anyMatch(status -> status == HostEnvStatusEnum.ERROR); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public String copyClusterTask(String taskId) { + OpsClusterTaskEntity task = getById(taskId); + Assert.isTrue(Objects.nonNull(task), "cluster task not exist:" + taskId); + // 复制集群任务 , 重置任务信息 + task.setClusterId(null); + task.setClusterName(autoCreateClusterName()); + task.setHostId(null); + task.setHostIp(null); + task.setHostUserId(null); + task.setHostUsername(null); + task.setEnvCheckResult(null); + task.setStatus(OpsClusterTaskStatusEnum.DRAFT); + task.setDatabasePassword(null); + task.setDatabasePort(5432); + task.setRemark("copy from " + taskId); + save(task); + // 复制节点信息 + List nodes = opsClusterTaskNodeService.listByClusterTaskId(taskId); + Assert.isTrue(CollectionUtils.isNotEmpty(nodes), "cluster task node not exist"); + nodes.forEach(node -> { + node.setClusterId(task.getClusterId()); + node.setClusterNodeId(null); + node.setHostId(null); + node.setHostUserId(null); + }); + // 保存复制任务 + opsClusterTaskNodeService.saveBatch(nodes); + OperateLogFactory.operateCopy(task.getClusterId()); + log.info("cluster task copy success, clusterId: old: {} new: {} ", taskId, task.getClusterId()); + return task.getClusterId(); + } + + private String autoCreateClusterName() { + return "auto-cluster-" + DateUtils.dateTimeNow(); + } + + @Override + public boolean checkClusterNameExist(String clusterId, String clusterName) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsClusterTaskEntity.class) + .select(OpsClusterTaskEntity::getClusterId) + .eq(OpsClusterTaskEntity::getClusterName, clusterName); + if (StrUtil.isNotEmpty(clusterId)) { + queryWrapper.notIn(OpsClusterTaskEntity::getClusterId, List.of(clusterId)); + } + return !list(queryWrapper).isEmpty(); + } + + @Override + public Map getBatchInstallTaskStatus(List taskIds) { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(OpsClusterTaskEntity.class) + .select(OpsClusterTaskEntity::getClusterId, OpsClusterTaskEntity::getStatus, + OpsClusterTaskEntity::getRemark) + .in(OpsClusterTaskEntity::getClusterId, taskIds); + List list = list(wrapper); + Map taskStatusMap = list.stream() + .map(task -> new TaskStatusVo(task.getClusterId(), task.getStatus(), task.getRemark())) + .collect(Collectors.toMap(TaskStatusVo::getClusterId, Function.identity())); + Map taskStatusResult = new HashMap<>(taskIds.size()); + taskIds.forEach(taskId -> taskStatusResult.put(taskId, taskStatusMap.get(taskId))); + return taskStatusResult; + } + + @Override + public List getHostList(String os, String osVersion, String cpuArch) { + return opsHostRemoteService.getHostList(os, osVersion, cpuArch); + } + + private boolean checkPasswordStrength(String password) { + return password.matches(PASSWORD_REGEX); + } + + private void checkTaskDirDiskSpace(OpsClusterTaskEntity task, OpsClusterTaskNodeEntity node) { + List nodePathList = new LinkedList<>(); + if (StrUtil.isNotEmpty(task.getCorePath())) { + nodePathList.add(task.getCorePath()); + } + if (StrUtil.isNotEmpty(task.getInstallPath())) { + nodePathList.add(task.getInstallPath()); + } + if (StrUtil.isNotEmpty(task.getTmpPath())) { + nodePathList.add(task.getTmpPath()); + } + if (StrUtil.isNotEmpty(task.getInstallPackagePath())) { + nodePathList.add(task.getInstallPackagePath()); + } + if (StrUtil.isNotEmpty(task.getLogPath())) { + nodePathList.add(task.getLogPath()); + } + if (StrUtil.isNotEmpty(task.getOmToolsPath())) { + nodePathList.add(task.getOmToolsPath()); + } + if (StrUtil.isNotEmpty(node.getDataPath())) { + nodePathList.add(node.getDataPath()); + } + if (StrUtil.isNotEmpty(node.getCmDataPath())) { + nodePathList.add(node.getCmDataPath()); + } + Map nodeDiskSpaceResult = checkHostDiskSpace(node.getHostId(), nodePathList); + log.info("check host {} disk space result: {}", node.getHostId(), nodeDiskSpaceResult); + parseNodeDiskSpaceResult(nodeDiskSpaceResult); + } + + private void parseNodeDiskSpaceResult(Map nodeDiskSpaceResult) { + if (CollUtil.isEmpty(nodeDiskSpaceResult)) { + return; + } + nodeDiskSpaceResult.forEach((key, value) -> { + if (!(StrUtil.contains(value, "G") && Integer.parseInt(value.replace("G", "")) > 2)) { + throw new OpsException(" disk check error " + nodeDiskSpaceResult); + } + }); + } + + /** + * check host disk space + * + * @param hostId host Id + * @param paths directory path list + * @return check result + */ + public Map checkHostDiskSpace(String hostId, List paths) { + Map checkResult = new HashMap<>(); + Map topLevelPaths = new HashMap<>(); + clusterTaskPathFactory.addAllPath(topLevelPaths, paths); + checkHostTopPathsDiskSpace(hostId, topLevelPaths); + paths.forEach(path -> { + if (StrUtil.isNotEmpty(path)) { + String topPath = clusterTaskPathFactory.applyPath(path); + checkResult.put(path, topLevelPaths.get(topPath)); + } + }); + return checkResult; + } + + private void checkHostTopPathsDiskSpace(String hostId, Map topLevelPaths) { + if (MapUtils.isEmpty(topLevelPaths)) { + return; + } + Assert.isTrue(CollectionUtils.isNotEmpty(topLevelPaths), "Paths must contain at least one top level directory"); + OpsHostEntity host = opsHostRemoteService.getHost(hostId); + OpsHostUserEntity hostRootUser = opsHostRemoteService.getHostRootUser(hostId); + HostBean hostBean = opsHostRemoteService.createSessionHostBean(host, hostRootUser); + topLevelPaths.keySet().forEach(topLevelPath -> { + try { + topLevelPaths.put(topLevelPath, opsHostRemoteService.checkHostDiskSpace(hostBean, topLevelPath) + "G"); + } finally { + log.info("host [id={}],[ip={}],[path={}],[space size={}]", hostId, host.getPublicIp(), topLevelPath, + topLevelPaths.get(topLevelPath)); + } + }); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsHostRemoteService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsHostRemoteService.java new file mode 100644 index 000000000..fd5e51da5 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsHostRemoteService.java @@ -0,0 +1,540 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpHostRemoteService.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpHostRemoteService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.util.StrUtil; + +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.jcraft.jsch.Session; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsAzEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.core.vo.HostBean; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.model.ops.JschResult; +import org.opengauss.admin.plugin.domain.model.ops.RetBuffer; +import org.opengauss.admin.plugin.domain.model.ops.SshCommandConstants; +import org.opengauss.admin.plugin.utils.OpsAssert; +import org.opengauss.admin.plugin.utils.OpsJschExecPlugin; +import org.opengauss.admin.plugin.utils.JschRetBufferUtil; +import org.opengauss.admin.system.plugin.beans.SshLogin; +import org.opengauss.admin.system.plugin.facade.AzFacade; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; +import org.opengauss.admin.system.plugin.facade.JschExecutorFacade; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import jakarta.annotation.Resource; + +import java.io.IOException; +import java.util.Objects; +import java.util.Map; +import java.util.List; + +/** + * OpHostRemoteService + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Slf4j +@Service +@Scope("prototype") +public class OpsHostRemoteService { + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private AzFacade azFacade; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostFacade hostFacade; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostUserFacade hostUserFacade; + + @Resource + private OpsJschExecPlugin opsJschExecPlugin; + @Resource + private JschRetBufferUtil jschRetBufferUtil; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private JschExecutorFacade jschExecutorFacade; + + /** + * get host by hostId, service called from plugin-main hostFacade + * + * @param hostId hostId + * @return host + */ + public OpsHostEntity getHost(String hostId) { + Assert.isTrue(StrUtil.isNotEmpty(hostId), "hostId does not empty"); + OpsHostEntity hostEntity = hostFacade.getById(hostId); + OpsAssert.nonNull(hostEntity, "host information does not exist"); + return hostEntity; + } + + /** + * query host list by condition + * + * @param os os + * @param osVersion osVersion + * @param cpuArch cpuArch + * @return host list + */ + public List getHostList(String os, String osVersion, String cpuArch) { + return hostFacade.getHostList(os, osVersion, cpuArch); + } + + /** + * get host root user by hostId, service called from plugin-main hostFacade + * + * @param hostId hostId + * @return host root user + */ + public OpsHostUserEntity getHostRootUser(String hostId) { + OpsHostEntity hostEntity = hostFacade.getById(hostId); + OpsAssert.nonNull(hostEntity, "Host information does not exist, hostId: " + hostId); + + OpsHostUserEntity userEntity = hostUserFacade.getHostUserByUsername(hostId, "root"); + if (Objects.isNull(userEntity)) { + throw new OpsException("Please add the host root user, host ip: " + hostEntity.getPublicIp()); + } + if (StrUtil.isEmpty(userEntity.getPassword())) { + throw new OpsException("The host root user password is empty, host ip: " + hostEntity.getPublicIp()); + } + return userEntity; + } + + /** + * create a session + * + * @param host host + * @param hostUser host user + * @return session + */ + public Session createHostUserSession(OpsHostEntity host, OpsHostUserEntity hostUser) { + Assert.isTrue(Objects.nonNull(host), "host information does not exist"); + Assert.isTrue(Objects.nonNull(hostUser), "hostUser information does not exist"); + Assert.isTrue(StrUtil.isNotEmpty(hostUser.getPassword()), "hostUser password does not exist"); + return jschRetBufferUtil.getSession(host.getPublicIp(), host.getPort(), hostUser.getUsername(), + encryptionUtils.decrypt(hostUser.getPassword())) + .orElseThrow(() -> new OpsException("Failed to establish connection with host")); + } + + /** + * create a host session bean + * + * @param host host + * @param hostUser host user + * @return hostBean + */ + public HostBean createSessionHostBean(OpsHostEntity host, OpsHostUserEntity hostUser) { + Assert.isTrue(Objects.nonNull(host), "host information does not exist"); + Assert.isTrue(Objects.nonNull(hostUser), "hostUser information does not exist"); + Assert.isTrue(StrUtil.isNotEmpty(hostUser.getPassword()), "hostUser password does not exist"); + return new HostBean(host.getPublicIp(), host.getPort(), hostUser.getUsername(), + encryptionUtils.decrypt(hostUser.getPassword())); + } + + /** + * get host disk space + * + * @param hostBean hostBean + * @param topLevelPath topLevelPath + * @return result + */ + public int checkHostDiskSpace(HostBean hostBean, String topLevelPath) { + String command = SshCommandConstants.DIR_FREE_HARD_DISK.replace("{0}", topLevelPath); + String freeHardDisk = opsJschExecPlugin.execCommand(hostBean, command); + OpsAssert.isTrue(!freeHardDisk.contains("No such file or directory"), freeHardDisk); + return translateDiskFreeSpaceUnitGb(freeHardDisk); + } + + private int translateDiskFreeSpaceUnitGb(String freeHardDisk) { + int res = 0; + String[] split = freeHardDisk.split("\n"); + for (String s : split) { + try { + if (s.contains("G")) { + res += Integer.parseInt(s.replace("G", " ").trim()); + } + } catch (NumberFormatException ignored) { + } + } + return res; + } + + /** + * get host list by host ids + * + * @param hostIds hostIds + * @return host list + */ + public List getOpsHostList(List hostIds) { + return hostFacade.listByIds(hostIds); + } + + /** + * get host user list by host user ids + * + * @param hostUserIds hostUserIds + * @return host user list + */ + public List getOpsHostUserList(List hostUserIds) { + return hostUserFacade.listByIds(hostUserIds); + } + + /** + * get host user by host user id + * + * @param hostUserId hostUserId + * @return host user + */ + public OpsHostUserEntity getOpsHostUser(String hostUserId) { + return hostUserFacade.getById(hostUserId); + } + + /** + * get host user list by host id + * + * @param hostId hostId + * @return host user list + */ + public List listHostUserByHostId(String hostId) { + return hostUserFacade.listHostUserByHostId(hostId); + } + + /** + * get host by host ip + * + * @param hostIp hostIp + * @return host + */ + public OpsHostEntity getByPublicIp(String hostIp) { + return hostFacade.getByPublicIp(hostIp); + } + + /** + * check port used + * + * @param hostId hostId + * @param hostPort hostPort + * @return true if port used + */ + public boolean portUsed(String hostId, Integer hostPort) { + return portUsed(hostId, hostPort, null); + } + + /** + * check port used + * + * @param id hostId + * @param port hostPort + * @param rootPassword rootPassword + * @return true if port used + */ + public boolean portUsed(String id, Integer port, String rootPassword) { + SshLogin sshLogin = buildHostSshLoginByRootOrNormalUser(id); + return !jschExecutorFacade.checkOsPortConflict(sshLogin, port); + } + + /** + * get host user list by host id + * + * @return host user list + */ + public List listAll() { + return hostFacade.listAll(); + } + + /** + * get host user by host id and username + * + * @param hostId hostId + * @param hostUsername hostUsername + * @return host user + */ + public OpsHostUserEntity getHostUserByUsername(String hostId, String hostUsername) { + return hostUserFacade.getHostUserByUsername(hostId, hostUsername); + } + + /** + * get az + * + * @return az list + */ + public List listAllAz() { + return azFacade.listAll(); + } + + /** + * get az by az id + * + * @param azId azId + * @return az + */ + public OpsAzEntity getAzById(String azId) { + return azFacade.getById(azId); + } + + /** + * execute command + * + * @param command command + * @param rootSession rootSession + * @param desc desc + * @return result + */ + public String executeCommand(String command, Session rootSession, String desc) { + try { + JschResult jschResult = jschRetBufferUtil.executeCommand(command, rootSession); + if (0 != jschResult.getExitCode()) { + log.error("Failed to execute command {} , exit code: {}, log: {}", command, jschResult.getExitCode(), + jschResult.getResult()); + throw new OpsException("Failed to execute command : " + command); + } + return jschResult.getResult().trim(); + } catch (IOException | InterruptedException | OpsException e) { + log.error("Failed to execute command {} {}", desc, command, e); + throw new OpsException("Failed to execute command : " + command); + } + } + + /** + * execute command + * + * @param command command + * @param rootSession rootSession + * @param retBuffer retBuffer + * @param desc desc + * @return result + */ + public String executeCommand(String command, Session rootSession, RetBuffer retBuffer, String desc) { + try { + JschResult jschResult = jschRetBufferUtil.executeCommandWithRetBuffer(command, rootSession, retBuffer); + if (0 != jschResult.getExitCode()) { + log.error("Failed to execute command {} , exit code: {}, log: {}", command, jschResult.getExitCode(), + jschResult.getResult()); + throw new OpsException("Failed to execute command"); + } + return jschResult.getResult().trim(); + } catch (Exception e) { + log.error("Failed to execute command {} {}", desc, command, e); + throw new OpsException("Failed to execute command : " + desc); + } + } + + /** + * execute command then return empty + * + * @param command command + * @param rootSession rootSession + * @param retBuffer retBuffer + * @param desc desc + * @return result + */ + public String executeCommandThenReturnEmpty(String command, Session rootSession, RetBuffer retBuffer, String desc) { + try { + JschResult jschResult = jschRetBufferUtil.executeCommand(command, rootSession, retBuffer, null, false); + if (0 != jschResult.getExitCode()) { + log.error("Failed to execute command {} , exit code: {}, log: {}", command, jschResult.getExitCode(), + jschResult.getResult()); + } + return jschResult.getResult().trim(); + } catch (Exception e) { + log.error("Failed to execute command {} {}", desc, command, e); + throw new OpsException("Failed to execute command : " + desc); + } + } + + /** + * execute command then return empty + * + * @param command command + * @param rootSession rootSession + * @param retBuffer retBuffer + * @return result + */ + public String executeCommand(String command, Session rootSession, RetBuffer retBuffer) { + return executeCommand(command, rootSession, retBuffer, ""); + } + + /** + * execute command then return empty + * + * @param command command + * @param rootSession rootSession + * @param retBuffer retBuffer + * @param autoResponse autoResponse + * @return result + */ + public String executeCommand(String command, Session rootSession, RetBuffer retBuffer, + Map autoResponse) { + try { + JschResult jschResult = jschRetBufferUtil.executeCommandWithRetBufferAndAutoResponse(command, rootSession, + retBuffer, autoResponse); + if (0 != jschResult.getExitCode()) { + log.error("Failed to execute command {} , exit code: {}, log: {}", command, jschResult.getExitCode(), + jschResult.getResult()); + throw new OpsException("Failed to execute command"); + } + return jschResult.getResult().trim(); + } catch (Exception e) { + log.error("Failed to execute command {}", command, e); + throw new OpsException("Failed to execute command"); + } + } + + /** + * execute command + * + * @param command command + * @param rootSession rootSession + * @param retBuffer retBuffer + * @param autoResponse autoResponse + * @param shouldHandleErrorBeforeExit shouldHandleErrorBeforeExit + * @return JschResult + */ + public JschResult executeCommand(String command, Session rootSession, RetBuffer retBuffer, + Map autoResponse, boolean shouldHandleErrorBeforeExit) { + try { + return jschRetBufferUtil.executeCommand(command, rootSession, retBuffer, autoResponse, + shouldHandleErrorBeforeExit); + } catch (IOException | InterruptedException e) { + log.error("Failed to execute command {}", command, e); + throw new OpsException("Failed to execute command: " + command); + } + } + + /** + * upload file + * + * @param rootSession rootSession + * @param retBuffer retBuffer + * @param sourcePath sourcePath + * @param installPackageFullPath installPackageFullPath + */ + public void executeUpload(Session rootSession, RetBuffer retBuffer, String sourcePath, + String installPackageFullPath) { + jschRetBufferUtil.upload(rootSession, retBuffer, sourcePath, installPackageFullPath); + } + + /** + * close session + * + * @param session session + */ + public void closeSession(Session session) { + if (Objects.nonNull(session) && session.isConnected()) { + session.disconnect(); + } + } + + /** + * 检查Host指定路径是否为空 + * + * @param hostId hostId + * @param path path + * @return true/false + */ + public boolean pathEmpty(String hostId, String path) { + SshLogin sshLogin = buildHostSshLoginByRootOrNormalUser(hostId); + return jschExecutorFacade.checkPathEmpty(sshLogin, path); + } + + private SshLogin buildHostSshLoginByRootOrNormalUser(String hostId) { + OpsHostEntity hostEntity = getHost(hostId); + OpsAssert.isTrue(Objects.nonNull(hostEntity), "host information not found"); + OpsHostUserEntity hostUser = getHostUserRootOrNormal(hostId); + SshLogin sshLogin = new SshLogin(hostEntity.getPublicIp(), hostEntity.getPort(), hostUser.getUsername(), + encryptionUtils.decrypt(hostUser.getPassword())); + return sshLogin; + } + + /** + * get host user,when has root ,return root user,otherwise return normal user + * + * @param hostId host id + * @return host user + * @throws OpsException host user not found + */ + private OpsHostUserEntity getHostUserRootOrNormal(String hostId) { + List userList = hostUserFacade.listHostUserByHostId(hostId); + OpsHostUserEntity hostUser = userList.stream() + .filter(user -> StrUtil.isNotEmpty(user.getPassword())) + .filter(user -> StrUtil.equalsIgnoreCase(user.getUsername(), "root")) + .findFirst() + .orElse(null); + if (Objects.isNull(hostUser)) { + hostUser = userList.stream() + .filter(user -> StrUtil.isNotEmpty(user.getPassword())) + .findAny() + .orElseThrow(() -> new OpsException("host user does not exist")); + } + return hostUser; + } + + /** + * check file exist + * + * @param hostId hostId + * @param file file + * @return boolean + */ + public boolean checkFileExist(String hostId, String file) { + SshLogin sshLogin = buildHostSshLoginByRootOrNormalUser(hostId); + return jschExecutorFacade.checkFileExist(sshLogin, file); + } + + /** + * create session with root or normal user + * + * @param hostId host + * @return Session + */ + public Session createPluginSessionWithRootOrNormalUser(String hostId) { + SshLogin sshLogin = buildHostSshLoginByRootOrNormalUser(hostId); + return jschRetBufferUtil.getSession(sshLogin.getHost(), sshLogin.getPort(), sshLogin.getUsername(), + sshLogin.getPassword()) + .orElseThrow(() -> new OpsException("create plugin session with root or normal user failed")); + } + + /** + * get host root or normal user + * + * @param hostId host id + * @return any user + */ + public OpsHostUserEntity getAnyHostUser(String hostId) { + return hostUserFacade.listHostUserByHostId(hostId).stream().findAny().get(); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsOlkServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsOlkServiceImpl.java new file mode 100644 index 000000000..01e659545 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsOlkServiceImpl.java @@ -0,0 +1,754 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsOlkServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsOlkServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.google.common.util.concurrent.SimpleTimeLimiter; +import com.google.common.util.concurrent.TimeLimiter; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.Session; +import lombok.Builder; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.http.HttpUtils; +import org.opengauss.admin.common.utils.uuid.IdUtils; +import org.opengauss.admin.plugin.domain.entity.ops.OpsOlkEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackageManagerEntity; +import org.opengauss.admin.plugin.domain.model.ops.JschResult; +import org.opengauss.admin.plugin.domain.model.ops.WsSession; +import org.opengauss.admin.plugin.domain.model.ops.cache.TaskManager; +import org.opengauss.admin.plugin.domain.model.ops.cache.WsConnectorManager; +import org.opengauss.admin.plugin.domain.model.ops.olk.InstallOlkBody; +import org.opengauss.admin.plugin.domain.model.ops.olk.InstallOlkContext; +import org.opengauss.admin.plugin.domain.model.ops.olk.OlkConfig; +import org.opengauss.admin.plugin.domain.model.ops.olk.OlkPageVO; +import org.opengauss.admin.plugin.domain.model.ops.olk.OlkProcessFlagStr; +import org.opengauss.admin.plugin.domain.model.ops.olk.ShardingDatasourceConfig; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.DadReqPath; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.DadResult; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.OlkCommandDto; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.OpenLooKengDto; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.ServerDto; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.ShardingDataSourceDto; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.ShardingRuleDto; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.ShardingsDto; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.ZookeeperDto; +import org.opengauss.admin.plugin.mapper.ops.OpsOlkMapper; +import org.opengauss.admin.plugin.service.ops.IOpsOlkService; +import org.opengauss.admin.plugin.service.ops.IOpsPackageManagerService; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.plugin.utils.WsUtil; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.RestTemplate; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + + +@Slf4j +@Service +public class OpsOlkServiceImpl extends ServiceImpl implements IOpsOlkService { + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + @Autowired + private WsConnectorManager wsConnectorManager; + @Autowired + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + @Autowired + private JschUtil jschUtil; + @Autowired + private WsUtil wsUtil; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostFacade hostFacade; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostUserFacade hostUserFacade; + @Autowired + private IOpsPackageManagerService packageManagerService; + @Autowired + private RestTemplate restTemplate; + @Value("${olk.readLogTimeout}") + private int readLogTimeout; + @Value("${olk.startLoopCount}") + private int startLoopCount; + @Value("${olk.startLoopWaitTime}") + private int startLoopWaitTime; + + @Override + public void install(InstallOlkBody installBody) { + // create ws + InstallOlkContext installContext = installBody.getInstallContext(); + WsSession wsSession = wsConnectorManager.getSession(installBody.getBusinessId()).orElseThrow(() -> new OpsException("websocket session not exist")); + installContext.setRetSession(wsSession); + installContext.checkConfig(); + // create Task + InstallOlkContext clone = ObjectUtil.clone(installContext); + // RequestAttributes context = RequestContextHolder.currentRequestAttributes(); + Future future = threadPoolTaskExecutor.submit(() -> { + // RequestContextHolder.setRequestAttributes(context); + doInstall(clone); + }); + TaskManager.registry(installBody.getBusinessId(), future); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void remove(String id) { + removeById(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void destroy(String id, String bid) { + requestNormalService(id, bid, DadReqPath.DESTROY, OlkProcessFlagStr.START_DESTROY, OlkProcessFlagStr.END_DESTROY, OlkProcessFlagStr.END_DESTROY_IN_ERROR); + removeById(id); + } + + private void requestNormalService(String id, String bid, String path, String startFlag, String endFlag, String endErrFlag) { + OpsOlkEntity olkEntity = getById(id); + if (olkEntity == null) { + throw new OpsException("OpenLooKeng deployment info not exists, please refresh and try again"); + } + WsSession wsSession = wsConnectorManager.getSession(bid).orElseThrow(() -> new OpsException("websocket session not exist")); + ServerDto serverDto = new ServerDto(); + OpsHostEntity hostEntity = hostFacade.getById(olkEntity.getDadInstallHostId()); + if (hostEntity == null) { + throw new OpsException("OpenLooKeng distribute deploy host not exists, please refresh and try again"); + } + serverDto.setIp(hostEntity.getPublicIp()); + serverDto.setPort(hostEntity.getPort()); + serverDto.setUsername(olkEntity.getDadInstallUsername()); + serverDto.setPassword(encryptionUtils.decrypt(olkEntity.getDadInstallPassword())); + + String reqUrl = String.format("http://%s:%s/%s", hostEntity.getPublicIp(), olkEntity.getDadPort(), path); + HttpUtils.PostResponse response = HttpUtils.sendPost(reqUrl, JSON.toJSONString(serverDto)); + DadResult result = JSON.parseObject(response.getBody(), DadResult.class); + String reqId = response.getHeader(DadReqPath.REQ_ID_KEY); + if (StrUtil.isEmpty(reqId)) { + processError(wsSession, "Send command to deployment service failed, Can't find req id"); + } + if (result != null && result.isSuccess()) { + ReadLogParam param = ReadLogParam.builder().serverDto(serverDto).logPath(getDadLogFileName(olkEntity.getDadInstallPath())).startFlag(startFlag).endFlag(endFlag).errFlag(endErrFlag).reqId(response.getHeader(DadReqPath.REQ_ID_KEY)).wsSession(wsSession).build(); + threadReadLogToWs(param); + wsUtil.sendText(wsSession, OlkProcessFlagStr.SEND_CMD_TO_SERVICE_COMPLETE); + } else { + processError(wsSession, "Send command to deployment service failed, error: " + result.get(DadResult.MSG_TAG)); + } + } + + @Override + public String generateRuleYaml(OlkConfig config) { + JSONObject databases = new JSONObject(new LinkedHashMap<>()); + AtomicInteger i = new AtomicInteger(); + ShardingRuleDto ruleDto = buildRuleDto(config); + ShardingDataSourceDto sourceDto = ruleDto.getDataSourceDto(); + String[] users = sourceDto.getUsername().split(","); + String[] passwords = sourceDto.getPassword().split(","); + Arrays.stream(sourceDto.getUrl().split(",")).map(String::trim).forEach(e -> { + databases.fluentPut("ds_" + i.get(), new JSONObject().fluentPut("url", e).fluentPut("username", users[i.get()].trim()).fluentPut("password", passwords[i.get()].trim()).fluentPut("connectionTimeoutMilliseconds", 30000).fluentPut("idleTimeoutMilliseconds", 60000).fluentPut("maxLifetimeMilliseconds", 1800000).fluentPut("maxPoolSize", 50).fluentPut("minPoolSize", 1)); + i.getAndIncrement(); + }); + JSONObject table = new JSONObject(); + JSONObject sharding = new JSONObject(); + String[] tables = ruleDto.getTableName().split(","); + String[] columns = ruleDto.getColumn().split(","); + AtomicInteger k = new AtomicInteger(); + Arrays.stream(tables).map(String::trim).forEach(e -> { + String ds = "ds_" + e + "_alg"; + String ts = "ts_" + e + "_alg"; + String exp = "ds_${0.." + i.get() + "}." + e + "_${0..2}"; + String dsExp = "ds_${" + columns[k.get()].trim() + " % " + i.get() + "}"; + String tsExp = e + "_${" + columns[k.get()].trim() + " % 2}"; + sharding.fluentPut(ds, new JSONObject().fluentPut("type", "INLINE").fluentPut("props", new JSONObject().fluentPut("algorithm-expression", dsExp))).fluentPut(ts, new JSONObject().fluentPut("type", "INLINE").fluentPut("props", new JSONObject().fluentPut("algorithm-expression", tsExp))); + table.fluentPut(e, new JSONObject().fluentPut("actualDataNodes", exp).fluentPut("tableStrategy", new JSONObject().fluentPut("standard", new JSONObject().fluentPut("shardingColumn", columns[k.get()].trim()).fluentPut("shardingAlgorithmName", ts))).fluentPut("databaseStrategy", new JSONObject().fluentPut("standard", new JSONObject().fluentPut("shardingColumn", columns[k.get()].trim()).fluentPut("shardingAlgorithmName", ds)))); + k.getAndIncrement(); + }); + val ret = new JSONObject().fluentPut("databaseName", "sharding_db").fluentPut("dataSources", new JSONObject(new LinkedHashMap<>()).fluentPutAll(databases)).fluentPut("rules", new JSONArray().fluentAdd(new JSONObject().fluentPut("\\!SHARDING", new JSONObject().fluentPut("shardingAlgorithms", new JSONObject().fluentPutAll(sharding)).fluentPut("tables", new JSONObject().fluentPutAll(table)).fluentPut("defaultTableStrategy", new JSONObject().fluentPut("none", null)).fluentPut("defaultDatabaseStrategy", new JSONObject().fluentPut("none", null))))); + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + Yaml yaml = new Yaml(options); + return yaml.dump(ret); + } + + @Override + public void start(String id, String bid) { + requestNormalService(id, bid, DadReqPath.START, OlkProcessFlagStr.START_SERVICE, OlkProcessFlagStr.END_START_SERVICE, OlkProcessFlagStr.END_START_SERVICE_IN_ERROR); + } + + @Override + public void stop(String id, String bid) { + requestNormalService(id, bid, DadReqPath.STOP, OlkProcessFlagStr.START_STOP_SERVICE, OlkProcessFlagStr.END_STOP_SERVICE, OlkProcessFlagStr.END_STOP_SERVICE_IN_ERROR); + } + + @Override + public IPage pageOlk(Page pageReq, String name) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsOlkEntity.class).like(StrUtil.isNotEmpty(name), OpsOlkEntity::getName, name); + IPage entityPage = page(pageReq, queryWrapper); + List hostEntityList = hostFacade.listAll(); + List managerEntities = packageManagerService.list(); + List voList = new ArrayList<>(); + for (OpsOlkEntity item : entityPage.getRecords()) { + OlkPageVO vo = new OlkPageVO(); + BeanUtils.copyProperties(item, vo); + for (OpsHostEntity hostEntity : hostEntityList) { + if (hostEntity.getHostId().equals(item.getDadInstallHostId())) { + vo.setDadInstallIp(hostEntity.getPublicIp()); + } + if (hostEntity.getHostId().equals(item.getSsInstallHostId())) { + vo.setSsInstallIp(hostEntity.getPublicIp()); + } + if (hostEntity.getHostId().equals(item.getOlkInstallHostId())) { + vo.setOlkInstallIp(hostEntity.getPublicIp()); + } + } + for (OpsPackageManagerEntity pkgEntity : managerEntities) { + if (pkgEntity.getPackageId().equals(item.getDadTarId())) { + vo.setDadPkgName(pkgEntity.getFileName()); + } + if (pkgEntity.getPackageId().equals(item.getSsTarId())) { + vo.setSsPkgName(pkgEntity.getFileName()); + } + if (pkgEntity.getPackageId().equals(item.getZkTarId())) { + vo.setZkPkgName(pkgEntity.getFileName()); + } + if (pkgEntity.getPackageId().equals(item.getOlkTarId())) { + vo.setOlkPkgName(pkgEntity.getFileName()); + } + } + voList.add(vo); + } + IPage voPage = new Page<>(); + voPage.setRecords(voList); + voPage.setPages(entityPage.getPages()); + voPage.setCurrent(entityPage.getCurrent()); + voPage.setTotal(entityPage.getTotal()); + voPage.setSize(entityPage.getSize()); + return voPage; + } + + private void checkInstallConfig(InstallOlkContext installContext) { + installContext.checkConfig(); + } + + @Transactional(rollbackFor = Exception.class) + public void doInstall(InstallOlkContext context) { + WsSession session = context.getRetSession(); + try { + checkInstallConfig(context); + wsUtil.sendText(session, OlkProcessFlagStr.START_DEPLOY_PROCESS); + createId(context.getOlkConfig()); + // transfer package to all host + uploadPackageToHost(context); + // start distribute deploy jar(install and start) + startDadService(context.getOlkConfig(), session); + requestDadInstallService(context.getOlkConfig(), session); + } catch (Exception ex) { + String errMsg = "Install OpenLooKeng failed, error: " + ex.getMessage(); + log.error(errMsg); + wsUtil.sendText(session, errMsg); + wsUtil.setErrorFlag(context.getRetSession()); + } + } + + private void uploadPackageToHost(InstallOlkContext context) { + wsUtil.sendText(context.getRetSession(), OlkProcessFlagStr.START_UPLOAD); + OlkConfig olkConfig = context.getOlkConfig(); + // upload dad + uploadDad(olkConfig, context.getRetSession()); + // upload sharding and zk + uploadShardAndZk(olkConfig, context.getRetSession()); + // upload olk + uploadOlk(olkConfig, context.getRetSession()); + wsUtil.sendText(context.getRetSession(), OlkProcessFlagStr.END_UPLOAD); + } + + private void uploadShardAndZk(OlkConfig olkConfig, WsSession wsSession) { + OpsHostEntity opsHostEntity = hostFacade.getById(olkConfig.getSsInstallHostId()); + if (ObjectUtil.isNull(opsHostEntity)) { + processError(wsSession, "Can't find host info, please try again"); + } + Session session = jschUtil.getSession(opsHostEntity.getPublicIp(), opsHostEntity.getPort(), olkConfig.getSsInstallUsername(), encryptionUtils.decrypt(olkConfig.getSsInstallPassword())).orElseThrow(() -> new OpsException("Failed to establish a connection with the target host [ " + opsHostEntity.getPublicIp() + " ]")); + OpsPackageManagerEntity ssEntity = packageManagerService.getById(olkConfig.getSsTarId()); + if (ObjectUtil.isNull(ssEntity)) { + processError(wsSession, "Can't find ShardingProxy package, please try again"); + } + OpsPackageManagerEntity zkEntity = packageManagerService.getById(olkConfig.getZkTarId()); + if (ObjectUtil.isNull(zkEntity)) { + processError(wsSession, "Can't find Zookeeper package, please try again"); + } + wsUtil.sendText(wsSession, String.format(OlkProcessFlagStr.START_UPLOAD_SHARDING_PROXY, opsHostEntity.getPublicIp(), olkConfig.getSsInstallUsername())); + try { + JschResult result = jschUtil.executeCommand("mkdir -p " + olkConfig.getSsUploadPath(), session); + if (result.getExitCode() != 0) { + throw new OpsException(result.getResult()); + } + } catch (Exception ex) { + processError(wsSession, String.format("Can't create upload path %s, error: %s", olkConfig.getSsUploadPath(), ex.getMessage())); + } + jschUtil.upload(session, wsSession, ssEntity.getRealPath(), getPath(olkConfig.getSsUploadPath(), ssEntity.getFileName())); + wsUtil.sendText(wsSession, String.format(OlkProcessFlagStr.END_UPLOAD_SHARDING_PROXY, opsHostEntity.getPublicIp())); + wsUtil.sendText(wsSession, String.format(OlkProcessFlagStr.START_UPLOAD_ZOOKEEPER, opsHostEntity.getPublicIp(), olkConfig.getSsInstallUsername())); + jschUtil.upload(session, wsSession, zkEntity.getRealPath(), getPath(olkConfig.getSsUploadPath(), zkEntity.getFileName())); + wsUtil.sendText(wsSession, String.format(OlkProcessFlagStr.END_UPLOAD_ZOOKEEPER, opsHostEntity.getPublicIp())); + wsUtil.sendText(wsSession, String.format(OlkProcessFlagStr.START_UPLOAD_RULE_YAML, opsHostEntity.getPublicIp())); + jschUtil.upload(session, wsSession, new ByteArrayInputStream( + olkConfig.getRuleYaml().getBytes(StandardCharsets.UTF_8)), + getPath(olkConfig.getSsUploadPath(), "rule.yaml")); + wsUtil.sendText(wsSession, String.format(OlkProcessFlagStr.END_UPLOAD_RULE_YAML, opsHostEntity.getPublicIp())); + session.disconnect(); + } + + private void uploadOlk(OlkConfig olkConfig, WsSession wsSession) { + OpsHostEntity opsHostEntity = hostFacade.getById(olkConfig.getOlkInstallHostId()); + if (ObjectUtil.isNull(opsHostEntity)) { + processError(wsSession, "Can't find host info, please try again"); + } + Session session = jschUtil.getSession(opsHostEntity.getPublicIp(), opsHostEntity.getPort(), olkConfig.getOlkInstallUsername(), encryptionUtils.decrypt(olkConfig.getOlkInstallPassword())).orElseThrow(() -> new OpsException("Failed to establish a connection with the target host [ " + opsHostEntity.getPublicIp() + " ]")); + OpsPackageManagerEntity olkEntity = packageManagerService.getById(olkConfig.getOlkTarId()); + if (ObjectUtil.isNull(olkEntity)) { + processError(wsSession, "Can't find openLooKeng package, please try again"); + } + wsUtil.sendText(wsSession, String.format(OlkProcessFlagStr.START_UPLOAD_OLK, opsHostEntity.getPublicIp(), olkConfig.getOlkInstallUsername())); + try { + JschResult result = jschUtil.executeCommand("mkdir -p " + olkConfig.getOlkUploadPath(), session); + if (result.getExitCode() != 0) { + throw new OpsException(result.getResult()); + } + } catch (Exception ex) { + processError(wsSession, String.format("Can't create upload path %s, error: %s", olkConfig.getOlkUploadPath(), ex.getMessage())); + } + jschUtil.upload(session, wsSession, olkEntity.getRealPath(), getPath(olkConfig.getOlkUploadPath(), olkEntity.getFileName())); + wsUtil.sendText(wsSession, String.format(OlkProcessFlagStr.END_UPLOAD_OLK, opsHostEntity.getPublicIp())); + session.disconnect(); + } + + private void uploadDad(OlkConfig olkConfig, WsSession wsSession) { + OpsHostEntity opsHostEntity = hostFacade.getById(olkConfig.getDadInstallHostId()); + if (ObjectUtil.isNull(opsHostEntity)) { + processError(wsSession, "Can't find host info, please try again"); + } + Session session = jschUtil.getSession(opsHostEntity.getPublicIp(), opsHostEntity.getPort(), olkConfig.getDadInstallUsername(), encryptionUtils.decrypt(olkConfig.getDadInstallPassword())).orElseThrow(() -> new OpsException("Failed to establish a connection with the target host [ " + opsHostEntity.getPublicIp() + " ]")); + OpsPackageManagerEntity dadEntity = packageManagerService.getById(olkConfig.getDadTarId()); + if (ObjectUtil.isNull(dadEntity)) { + processError(wsSession, "Can't find Deployment package, please try again"); + } + wsUtil.sendText(wsSession, String.format(OlkProcessFlagStr.START_UPLOAD_DAD, opsHostEntity.getPublicIp(), olkConfig.getDadInstallUsername())); + try { + JschResult result = jschUtil.executeCommand("mkdir -p " + olkConfig.getDadInstallPath(), session); + if (result.getExitCode() != 0) { + throw new OpsException(result.getResult()); + } + } catch (Exception ex) { + processError(wsSession, String.format("Can't create upload path %s, error: %s", olkConfig.getDadInstallPath(), ex.getMessage())); + } + + jschUtil.upload(session, wsSession, dadEntity.getRealPath(), getPath(olkConfig.getDadInstallPath(), dadEntity.getFileName())); + wsUtil.sendText(wsSession, String.format(OlkProcessFlagStr.END_UPLOAD_DAD, opsHostEntity.getPublicIp())); + session.disconnect(); + } + + private void startDadService(OlkConfig config, WsSession wsSession) { + OpsPackageManagerEntity entity = packageManagerService.getById(config.getDadTarId()); + if (ObjectUtil.isNull(entity)) { + processError(wsSession, "Can't find deployment package, please try again"); + } + OpsHostEntity host = hostFacade.getById(config.getDadInstallHostId()); + if (ObjectUtil.isNull(host)) { + processError(wsSession, "Can't find deployment host info, please try again"); + } + // check if already started + ServerDto serverDto = new ServerDto(); + serverDto.setIp(host.getPublicIp()); + serverDto.setPort(host.getPort()); + serverDto.setUsername(config.getDadInstallUsername()); + serverDto.setPassword(encryptionUtils.decrypt(config.getDadInstallPassword())); + wsUtil.sendText(wsSession, "Check if deployment service is running"); + boolean isRunning = isProcessRunning(config.getDadPort(), OlkProcessFlagStr.DAD_PKG_NAME, serverDto, wsSession); + if (isRunning) { + wsUtil.sendText(wsSession, "Deployment service is alreay running, skip start"); + return; + } + Session session = jschUtil.getSession(host.getPublicIp(), host.getPort(), config.getDadInstallUsername(), encryptionUtils.decrypt(config.getDadInstallPassword())).orElseThrow(() -> new OpsException("Connection establishment failed")); + try { + String cmd = String.format("cd %s; nohup java \'-Dserver.port=%s\' -Dpath=/opt -jar %s > %s 2>&1 &", config.getDadInstallPath(), config.getDadPort(), entity.getFileName(), getPath(config.getDadInstallPath(), DadReqPath.OUTPUT_LOG)); + JschResult result = jschUtil.executeCommand(cmd, session, wsSession); + if (result.getExitCode() != 0) { + processError(wsSession, "Use nohup to start deployment jar failed, exitCode: " + result.getExitCode()); + } + // Judge whether it has been started + int maxTryCount = 3; + int tryCount = 0; + boolean flag = false; + while (tryCount < maxTryCount) { + isRunning = isProcessRunning(config.getDadPort(), OlkProcessFlagStr.DAD_PKG_NAME, serverDto, wsSession); + if (isRunning) { + flag = true; + wsUtil.sendText(wsSession, "Deployment service start successfully, Now we can send command"); + break; + } else { + tryCount++; + Thread.sleep(5000); + } + } + if (!flag) { + throw new OpsException("Failed to start deployment service after 15s, please try again"); + } + } catch (Exception ex) { + processError(wsSession, "Failed to start deployment service, error: " + ex.getMessage()); + } finally { + session.disconnect(); + } + } + + private void requestDadInstallService(OlkConfig config, WsSession wsSession) { + wsUtil.sendText(wsSession, OlkProcessFlagStr.SEND_CMD_TO_DAD_SERVICE); + OpsHostEntity host = hostFacade.getById(config.getDadInstallHostId()); + if (ObjectUtil.isNull(host)) { + processError(wsSession, "Can't find deployment host info, please try again"); + } + + ServerDto serverDto = new ServerDto(); + serverDto.setIp(host.getPublicIp()); + serverDto.setPort(host.getPort()); + serverDto.setUsername(config.getDadInstallUsername()); + serverDto.setPassword(encryptionUtils.decrypt(config.getDadInstallPassword())); + + OlkCommandDto olkCommandDto = new OlkCommandDto(); + ShardingsDto ssDto = buildShardDto(config, wsSession); + OpenLooKengDto olkDto = buildOlkDto(config, wsSession); + olkCommandDto.setShardingsDto(ssDto); + olkCommandDto.setOpenLooKengDto(olkDto); + + // if code goes here, we have already monitor on log file + String reqUrl = String.format("http://%s:%s/%s", host.getPublicIp(), config.getDadPort(), DadReqPath.DEPLOY); + HttpUtils.PostResponse response = HttpUtils.sendPost(reqUrl, JSON.toJSONString(olkCommandDto)); + DadResult result = JSON.parseObject(response.getBody(), DadResult.class); + String reqId = response.getHeader(DadReqPath.REQ_ID_KEY); + if (StrUtil.isEmpty(reqId)) { + processError(wsSession, "Send command to deployment service failed, Can't find req id"); + } + if (result != null && result.isSuccess()) { + ReadLogParam param = ReadLogParam.builder().serverDto(serverDto) + .logPath(config.getDadLogFileName()).olkEntity(config.toEntity()) + .startFlag(OlkProcessFlagStr.START_DEPLOY) + .endFlag(OlkProcessFlagStr.END_DEPLOY) + .errFlag(OlkProcessFlagStr.END_DEPLOY_IN_ERROR) + .reqId(response.getHeader(DadReqPath.REQ_ID_KEY)) + .wsSession(wsSession).build(); + threadReadLogToWs(param); + wsUtil.sendText(wsSession, OlkProcessFlagStr.SEND_CMD_TO_SERVICE_COMPLETE); + } else { + processError(wsSession, "Send command to deployment service failed, error: " + result.get(DadResult.MSG_TAG)); + } + } + + private ShardingsDto buildShardDto(OlkConfig config, WsSession session) { + ShardingsDto shardingsDto = new ShardingsDto(); + + ServerDto shardingServer = new ServerDto(); + OpsHostEntity ssHost = hostFacade.getById(config.getSsInstallHostId()); + if (ObjectUtil.isNull(ssHost)) { + processError(session, "Can't find host info, please try again"); + } + shardingServer.setIp(ssHost.getPublicIp()); + shardingServer.setPort(ssHost.getPort()); + shardingServer.setUsername(config.getSsInstallUsername()); + shardingServer.setPassword(encryptionUtils.decrypt(config.getSsInstallPassword())); + shardingsDto.setServerDto(shardingServer); + + shardingsDto.setPort(Integer.parseInt(config.getSsPort())); + shardingsDto.setShardingRuleDto(buildRuleDto(config)); + shardingsDto.setSourcePath(config.getSsUploadPath()); + shardingsDto.setInstallPath(config.getSsInstallPath()); + + ZookeeperDto zookeeperDto = new ZookeeperDto(); + zookeeperDto.setPort(Integer.parseInt(config.getZkPort())); + shardingsDto.setZookeeperDto(zookeeperDto); + return shardingsDto; + } + + private ShardingRuleDto buildRuleDto(OlkConfig config) { + ShardingRuleDto ruleDto = new ShardingRuleDto(); + StringBuilder urlBuilder = new StringBuilder(); + StringBuilder usernameBuilder = new StringBuilder(); + StringBuilder pwdBuilder = new StringBuilder(); + for (ShardingDatasourceConfig dsItem : config.getDsConfig()) { + urlBuilder.append(String.format("jdbc:opengauss://%s:%s/%s", dsItem.getHost(), dsItem.getPort(), dsItem.getDbName())).append(","); + usernameBuilder.append(dsItem.getUsername()).append(","); + pwdBuilder.append(dsItem.getPassword()).append(","); + } + ShardingDataSourceDto dataSourceDto = new ShardingDataSourceDto(); + dataSourceDto.setUrl(urlBuilder.substring(0, urlBuilder.length() - 1)); + dataSourceDto.setUsername(usernameBuilder.substring(0, usernameBuilder.length() - 1)); + dataSourceDto.setPassword(pwdBuilder.substring(0, pwdBuilder.length() - 1)); + ruleDto.setDataSourceDto(dataSourceDto); + ruleDto.setColumn(config.getColumns()); + ruleDto.setTableName(config.getTableName()); + return ruleDto; + } + + private OpenLooKengDto buildOlkDto(OlkConfig config, WsSession session) { + OpenLooKengDto olkDto = new OpenLooKengDto(); + + ServerDto shardingServer = new ServerDto(); + OpsHostEntity olkHost = hostFacade.getById(config.getOlkInstallHostId()); + if (ObjectUtil.isNull(olkHost)) { + processError(session, "Can't find host info, please try again"); + } + shardingServer.setIp(olkHost.getPublicIp()); + shardingServer.setPort(olkHost.getPort()); + shardingServer.setUsername(config.getSsInstallUsername()); + shardingServer.setPassword(encryptionUtils.decrypt(config.getSsInstallPassword())); + olkDto.setServerDto(shardingServer); + + olkDto.setInstallPath(config.getOlkInstallPath()); + olkDto.setSourcePath(config.getOlkUploadPath()); + olkDto.setPort(Integer.parseInt(config.getOlkPort())); + olkDto.setOlkConfigDto(config.getOlkParamConfig()); + return olkDto; + } + + @Transactional + public void saveToDb (OpsOlkEntity entity) { + save(entity); + } + + public void threadReadLogToWs(ReadLogParam param) { + Future future = threadPoolTaskExecutor.submit(() -> { + BufferedReader reader = null; + InputStream in = null; + InputStreamReader isr = null; + ChannelExec channel = null; + Session session = null; + ServerDto serverDto = param.getServerDto(); + try { + // Read the log file line by line + session = jschUtil.getSession(serverDto.getIp(), serverDto.getPort(), serverDto.getUsername(), serverDto.getPassword()).orElseThrow(() -> new OpsException("Connection establishment failed")); + int startCount = 0; + wsUtil.sendText(param.getWsSession(), String.format("Finding start flag \"%s\" in log file %s", param.getSF(), param.getLogPath())); + while (true) { + String cmd = String.format("grep -n \"%s\" %s", param.getSF(), param.getLogPath()); + JschResult result = jschUtil.executeCommand(cmd, session); + if (result.getExitCode() != 0 || StrUtil.isEmpty(result.getResult())) { + startCount++; + if (startCount > startLoopCount) { + throw new Exception("Can't find start log after " + startLoopCount * startLoopWaitTime + "s"); + } + wsUtil.sendText(param.getWsSession(), String.format("%s st try to find start flag", startCount)); + Thread.sleep(startLoopWaitTime * 1000); + } else { + wsUtil.sendText(param.getWsSession(), "Find start flag in log file: " + result.getResult()); + break; + } + } + + channel = (ChannelExec) session.openChannel("exec"); + channel.setPtyType("dump"); + channel.setPty(false); + channel.setCommand(String.format("grep -n \"%s\" %s | tail -1 | awk -F \":\" '{print $1}' | xargs -I {} tail -f %s -n +{}", param.getSF(), param.getLogPath(), param.getLogPath())); + channel.connect(20000); + in = channel.getInputStream(); + isr = new InputStreamReader(in, StandardCharsets.UTF_8); + reader = new BufferedReader(isr); + String line; + TimeLimiter limiter = SimpleTimeLimiter.create(Executors.newSingleThreadExecutor()); + + while (true) { + line = limiter.callWithTimeout(reader::readLine, readLogTimeout, TimeUnit.SECONDS); + if (line.contains(param.getEEE())) { + throw new Exception(line); + } + if (line.contains(param.getEF())) { + wsUtil.sendText(param.getWsSession(), line); + wsUtil.sendText(param.getWsSession(), OlkProcessFlagStr.END_DEPLOY_PROCESS); + wsUtil.setSuccessFlag(param.getWsSession()); + if (param.getOlkEntity() != null) { + saveToDb(param.getOlkEntity()); + } + return; + } + wsUtil.sendText(param.getWsSession(), line); + } + } catch (Exception ex) { + String errMsg = String.format("Failed to Read process log %s, error: %s", param.getLogPath(), ex.getMessage()); + wsUtil.sendText(param.getWsSession(), errMsg); + wsUtil.setErrorFlag(param.getWsSession()); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + log.error("close input stream failed: " + e.getMessage()); + } + } + if (isr != null) { + try { + isr.close(); + } catch (IOException e) { + log.error("close input stream reader failed: " + e.getMessage()); + } + } + if (channel != null) { + channel.disconnect(); + } + if (session != null) { + session.disconnect(); + } + try { + if (reader != null) { + reader.close(); + } + } catch (IOException ioException) { + wsUtil.sendText(param.getWsSession(), "Close log reader error: " + ioException.getMessage()); + } + } + }); + + TaskManager.registry(param.getWsSession().getSessionId(), future); + } + + private void readLogToWs(ServerDto serverDto, String logPath, String reqId, WsSession wsSession) { + try { + Session session = jschUtil.getSession(serverDto.getIp(), serverDto.getPort(), serverDto.getUsername(), serverDto.getPassword()).orElseThrow(() -> new OpsException("Connection establishment failed")); + JschResult result = jschUtil.executeCommand(String.format("cat %s | grep %s", logPath, reqId), session); + if (result.getExitCode() != 0) { + processError(wsSession, String.format("Failed to Read process log %s, error: %s", logPath, result.getExitCode())); + } + wsUtil.sendText(wsSession, result.getResult()); + session.disconnect(); + } catch (Exception ex) { + processError(wsSession, String.format("Failed to Read process log %s, error: %s", logPath, ex.getMessage())); + } + } + + private void processError(WsSession session, String errMsg) { + wsUtil.sendText(session, errMsg); + wsUtil.setErrorFlag(session); + throw new OpsException(errMsg); + } + + private boolean isProcessRunning(String processPort, String searchKey, ServerDto serverDto, WsSession wsSession) { + String cmd = String.format("netstat -anp | grep -w %s | awk '{print $7}' | sed 's|/.*||'", processPort); + Session session = jschUtil.getSession(serverDto.getIp(), serverDto.getPort(), serverDto.getUsername(), serverDto.getPassword()).orElseThrow(() -> new OpsException(String.format("Connection to %s establishment failed", serverDto.getIp()))); + try { + JschResult result = jschUtil.executeCommand(cmd, session, wsSession); + if (result.getExitCode() != 0) { + processError(wsSession, "Find dad process error, exitCode: " + result.getExitCode()); + } + String pid = result.getResult(); + if (StrUtil.isEmpty(pid) || pid.equals("-")) { + return false; + } + if (pid.contains("\r\n")) { + pid = pid.split("\r\n")[0]; + } + result = jschUtil.executeCommand("ps -aux|grep " + pid, session, wsSession); + if (result.getExitCode() != 0) { + processError(wsSession, "Find dad process error, exitCode: " + result.getExitCode()); + } + return result.getResult().contains(searchKey); + } catch (Exception ex) { + processError(wsSession, "Find dad process error, error: " + ex.getMessage()); + } finally { + session.disconnect(); + } + return false; + } + + private void createId(OlkConfig olkConfig) { + olkConfig.setId(IdUtils.fastUuid()); + } + + private String getPath(String path1, String path2) { + return Path.of(path1, path2).toString().replace("\\", "/"); + } + + public String getDadLogFileName(String dadInstallPath) { + // return Path.of(dadInstallPath, DadReqPath.LOG_FILE_NAME + id).toString(); + return Path.of(dadInstallPath, DadReqPath.OUTPUT_LOG).toString().replace("\\", "/"); + } + + @Data + @Builder + private static class ReadLogParam { + private ServerDto serverDto; + private String startFlag; + private String endFlag; + private String errFlag; + private String reqId; + private String logPath; + private WsSession wsSession; + private OpsOlkEntity olkEntity; + + public String getSF() { + return String.format("(%s) %s", reqId, startFlag); + } + + public String getEF() { + return String.format("(%s) %s", reqId, endFlag); + } + + public String getEEE() { + return String.format("(%s) %s", reqId, errFlag); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsPackageManagerService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsPackageManagerService.java new file mode 100644 index 000000000..83c86b5fd --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsPackageManagerService.java @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsPackageManagerService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsPackageManagerService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.UUID; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.UploadInfo; +import org.opengauss.admin.common.core.domain.entity.SysSettingEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsAzEntity; +import org.opengauss.admin.common.enums.CpuArchName; +import org.opengauss.admin.common.enums.OsName; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackageManagerEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackageVO; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.enums.ops.PackageTypeEnum; +import org.opengauss.admin.plugin.mapper.ops.OpsPackageManagerMapper; +import org.opengauss.admin.plugin.service.ops.IOpsPackageManagerService; +import org.opengauss.admin.system.plugin.facade.SysSettingFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author lhf + * @date 2022/12/11 16:14 + **/ +@Slf4j +@Service +public class OpsPackageManagerService extends ServiceImpl implements IOpsPackageManagerService { + + @Autowired + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private SysSettingFacade sysSettingFacade; + + @Override + public String getCpuArchByPackagePath(String installPackagePath, OpenGaussVersionEnum version) { + if (!FileUtil.exist(installPackagePath)) { + throw new OpsException("The installation package does not exist"); + } + + if (version == OpenGaussVersionEnum.ENTERPRISE) { + return getEnterprisePackageCpuArch(installPackagePath); + } else if (version == OpenGaussVersionEnum.MINIMAL_LIST) { + return getMinimalListPackageCpuArch(installPackagePath); + } else if (version == OpenGaussVersionEnum.LITE) { + return getLitePackageCpuArch(installPackagePath); + } else { + throw new OpsException("An unsupported version"); + } + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void savePackage(OpsPackageManagerEntity pkg, Integer userId) throws OpsException { + // upload file + savePackageTarFile(pkg, pkg.getName(), userId); + saveOrUpdate(pkg); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void updatePackage(OpsPackageManagerEntity pkg, Integer userId) { + savePackage(pkg, userId); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void delPackage(String id) { + OpsPackageManagerEntity entity = getById(id); + if (ObjectUtil.isNull(entity)) { + log.warn(String.format("Can't find package record [%s] to delete. Skip", entity.getPackageId())); + return; + } + String realPath = entity.getRealPath(); + if (StrUtil.isNotEmpty(realPath)) { + File file = new File(realPath); + if (!file.exists()) { + log.warn(String.format("Can't find package tar file [%s] to delete. Skip", entity.getPackagePath().toString())); + } else { + boolean result = file.delete(); + if (!result) { + log.warn(String.format("Can't delete file [%s]. Skip", entity.getPackagePath().toString())); + } + } + } + removeById(id); + } + + @Override + public OpsPackageVO analysisPkg(String pkgName, String pkgType) { + OpsPackageVO result = new OpsPackageVO(); + if (StrUtil.containsIgnoreCase(pkgName, CpuArchName.AARCH64.getCpuArchName())) { + result.setCpuArch(CpuArchName.AARCH64.getCpuArchName()); + } else if (StrUtil.containsIgnoreCase(pkgName, CpuArchName.X86_64.getCpuArchName()) || StrUtil.containsIgnoreCase(pkgName, "64") || StrUtil.containsIgnoreCase(pkgName, "86")) { + result.setCpuArch(CpuArchName.X86_64.getCpuArchName()); + } else { + result.setCpuArch(CpuArchName.NOARCH.getCpuArchName()); + } + if (StrUtil.containsIgnoreCase(pkgName, OsName.CENTOS.getOsName())) { + result.setOs(OsName.CENTOS.getOsName()); + } else if (StrUtil.containsIgnoreCase(pkgName, OsName.OPEN_EULER.getOsName())) { + result.setOs(OsName.OPEN_EULER.getOsName()); + } else { + result.setOs(OsName.ALL.getOsName()); + } + + String patternString = "([0-9]+\\.){1,2}[0-9]+"; + Pattern pattern = Pattern.compile(patternString); + Matcher matcher = pattern.matcher(pkgName); + if (matcher.find()) { + String version = matcher.group(); + result.setPackageVersionNum(version); + } + + if (pkgType.equalsIgnoreCase(PackageTypeEnum.OPENGAUSS.getPackageType())) { + if (StrUtil.containsIgnoreCase(pkgName, "-all")) { + result.setPackageVersion(OpenGaussVersionEnum.ENTERPRISE.name()); + } else if (StrUtil.containsIgnoreCase(pkgName, "-Lite-")) { + result.setPackageVersion(OpenGaussVersionEnum.LITE.toString()); + } else { + result.setPackageVersion(OpenGaussVersionEnum.MINIMAL_LIST.name()); + } + } + return result; + } + + @Deprecated + @Override + public UploadInfo upload(MultipartFile file, Integer userId) throws OpsException { + OpsPackageManagerEntity entity = new OpsPackageManagerEntity(); + UploadInfo info = new UploadInfo(); + entity.setPackagePath(info); + entity.setFile(file); + savePackageTarFile(entity, "", userId); + return info; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public boolean deletePkgTar(String path, String id) { + File file = new File(path); + boolean result = true; + if (file.exists()) { + result = file.delete(); + } + if (ObjectUtil.isNotNull(id)) { + UpdateWrapper wrapper = new UpdateWrapper<>(); + wrapper.set("package_path", "").eq("package_id", id); + update(wrapper); + } + return result; + } + + @Override + public String getSysUploadPath(Integer userId) { + SysSettingEntity entity = sysSettingFacade.getSysSetting(userId); + if (ObjectUtil.isNull(entity)) { + return ""; + } + return entity.getUploadPath(); + } + + @Override + public boolean checkUploadPath(String path, Integer userId) { + return !sysSettingFacade.checkUploadPath(path, userId); + } + + private String getLitePackageCpuArch(String installPackagePath) { + if (installPackagePath.contains("aarch64")) { + return "aarch64"; + } + + if (installPackagePath.contains("x86_64")) { + return "x86_64"; + } + + return null; + } + + private String getMinimalListPackageCpuArch(String installPackagePath) { + String tempFolder = "temp-minimal-" + StrUtil.uuid(); + String tempAbsoluteFolder = FileUtil.getParent(installPackagePath, 1) + File.separator + tempFolder; + FileUtil.mkdir(tempAbsoluteFolder); + + try { + try { + Process tar = Runtime.getRuntime().exec( + "tar -xvf " + installPackagePath + " -C " + tempAbsoluteFolder); + int exitCode = tar.waitFor(); + if (0 != exitCode) { + throw new OpsException("Failed to decompress the installation package with exitCode " + exitCode); + } + } catch (IOException | InterruptedException e) { + log.error("Failed to decompress the installation package", e); + throw new OpsException("Failed to decompress the installation package"); + } + InputStream in = null; + try { + Process file = Runtime.getRuntime().exec("file " + tempAbsoluteFolder + File.separator + "bin" + File.separator + "gs_ctl"); + int exitCode = file.waitFor(); + if (0 != exitCode) { + throw new OpsException("Failed to get cpu arch with exitCode " + exitCode); + } + in = file.getInputStream(); + String res = IoUtil.read(in, StandardCharsets.UTF_8); + if (StrUtil.isEmpty(res)) { + log.error("Failed to get cpu arch:{}"); + throw new OpsException("Failed to get cpu arch"); + } else { + String[] split = res.split(","); + if (split.length < 2) { + log.error("Failed to get cpu arch:{}", res); + throw new OpsException("Failed to get cpu arch"); + } + + String trim = split[1].trim(); + String[] s = trim.split(" "); + + if (s.length == 1) { + return s[0]; + } + if (s.length > 1) { + return s[s.length - 1]; + } + } + } catch (IOException | InterruptedException e) { + log.error("Failed to get cpu arch", e); + throw new OpsException("Failed to get cpu arch"); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + log.error("close input stream failed: " + e.getMessage()); + } + } + } + return null; + } finally { + threadPoolTaskExecutor.submit(() -> { + String command = "rm -rf " + tempAbsoluteFolder; + try { + Process exec = Runtime.getRuntime().exec(command); + exec.waitFor(); + } catch (Exception ignore) { + + } + }); + } + } + + private String getEnterprisePackageCpuArch(String installPackagePath) { + String version = getVersion(installPackagePath); + String system = getSystem(installPackagePath); + log.info("version:{}", version); + String tempFolder = "temp-enterprise-" + StrUtil.uuid(); + String tempAbsoluteFolder = FileUtil.getParent(installPackagePath, 1) + File.separator + tempFolder; + FileUtil.mkdir(tempAbsoluteFolder); + + try { + try { + String command = "tar -xvf " + installPackagePath + " -C " + tempAbsoluteFolder; + Process tar = Runtime.getRuntime().exec(command); + int exitCode = tar.waitFor(); + if (0 != exitCode) { + log.error("command:{}", command); + throw new OpsException("Failed to decompress the installation package with exitCode " + exitCode); + } + } catch (IOException | InterruptedException e) { + log.error("Failed to decompress the installation package", e); + throw new OpsException("Failed to decompress the installation package"); + } + + try { + String command = "tar -xvf " + tempAbsoluteFolder + File.separator + "openGauss-" + version + "-" + system + "-64bit-cm.tar.gz " + " -C " + tempAbsoluteFolder; + Process tar = Runtime.getRuntime().exec(command); + int exitCode = tar.waitFor(); + if (0 != exitCode) { + log.error("command:{}", command); + throw new OpsException("Failed to decompress the installation package with exitCode " + exitCode); + } + } catch (IOException | InterruptedException e) { + log.error("Failed to decompress the installation package", e); + throw new OpsException("Failed to decompress the installation package"); + } + InputStream in = null; + try { + String command = "file " + tempAbsoluteFolder + File.separator + "bin" + File.separator + "cm_ctl"; + Process file = Runtime.getRuntime().exec(command); + int exitCode = file.waitFor(); + if (0 != exitCode) { + log.error("command:{}", command); + throw new OpsException("Failed to get cpu arch with exitCode " + exitCode); + } + in = file.getInputStream(); + String res = IoUtil.read(in, StandardCharsets.UTF_8); + if (StrUtil.isEmpty(res)) { + log.error("Failed to get cpu arch:{}"); + throw new OpsException("Failed to get cpu arch"); + } else { + String[] split = res.split(","); + if (split.length < 2) { + log.error("Failed to get cpu arch:{}", res); + throw new OpsException("Failed to get cpu arch"); + } + + String trim = split[1].trim(); + String[] s = trim.split(" "); + + if (s.length == 1) { + return s[0]; + } + if (s.length > 1) { + return s[s.length - 1]; + } + } + } catch (IOException | InterruptedException e) { + log.error("Failed to get cpu arch", e); + throw new OpsException("Failed to get cpu arch"); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + log.error("close input stream failed: " + e.getMessage()); + } + } + } + + return null; + } finally { + threadPoolTaskExecutor.submit(() -> { + String command = "rm -rf " + tempAbsoluteFolder; + try { + Process exec = Runtime.getRuntime().exec(command); + exec.waitFor(); + } catch (Exception ignore) { + + } + }); + } + } + + private String getSystem(String installPackagePath) { + String packageName = installPackagePath.substring(installPackagePath.lastIndexOf("/")); + return packageName.split("-")[2]; + } + + private String getVersion(String installPackagePath) { + String packageName = installPackagePath.substring(installPackagePath.lastIndexOf("/")); + return packageName.split("-")[1]; + } + + /** + * save package tar file to disk + * + * @param pkg + * @param userId + */ + private void savePackageTarFile(OpsPackageManagerEntity pkg, String pkgManagedName, Integer userId) throws OpsException { + MultipartFile file = pkg.getFile(); + if (ObjectUtil.isNull(file) || StrUtil.isNotEmpty(pkg.getRealPath())) { + return; + } + SysSettingEntity entity = sysSettingFacade.getSysSetting(userId); + if (ObjectUtil.isNull(entity)) { + log.error("Cannot find system setting of user id: " + userId); + throw new OpsException("Cannot find system setting of your account, please try again"); + } + // create folder + String uploadFolder = entity.getUploadPath() + pkgManagedName; + File folder = new File(uploadFolder); + if (!folder.exists()) { + boolean res = folder.mkdirs(); + if (!res) { + String errMsg = String.format("Can't create folder: %s, please try again", uploadFolder); + log.error(errMsg); + throw new OpsException(errMsg); + } + } + String fileRealPath = Path.of(uploadFolder, file.getOriginalFilename()).toString(); + try { + file.transferTo(new File(fileRealPath)); + UploadInfo info = pkg.getPackagePath(); + if (ObjectUtil.isNull(info)) { + info = new UploadInfo(); + } + info.setName(file.getOriginalFilename()); + info.setRealPath(fileRealPath); + pkg.setPackagePath(info); + } catch (Exception ex) { + String errMsg = String.format("Upload tar file to %s failed: %s", fileRealPath, ex.getMessage()); + log.error(errMsg); + throw new OpsException(errMsg); + } + } + + @Override + public boolean hasName(String name) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsPackageManagerEntity.class) + .eq(OpsPackageManagerEntity::getName, name); + return count(queryWrapper) > 0; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsPackageManagerV2Service.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsPackageManagerV2Service.java new file mode 100644 index 000000000..834519391 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsPackageManagerV2Service.java @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsPackageManagerV2Service.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsPackageManagerV2Service.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.UploadInfo; +import org.opengauss.admin.common.core.domain.entity.SysSettingEntity; +import org.opengauss.admin.common.core.dto.ops.PackageDto; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.DateUtils; +import org.opengauss.admin.plugin.constant.OpsConstants; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackageManagerEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackageVO; +import org.opengauss.admin.plugin.domain.model.ops.WsSession; +import org.opengauss.admin.plugin.domain.model.ops.cache.TaskManager; +import org.opengauss.admin.plugin.domain.model.ops.cache.WsConnectorManager; +import org.opengauss.admin.plugin.enums.ops.OpsPackageSource; +import org.opengauss.admin.plugin.mapper.ops.OpsPackageManagerMapper; +import org.opengauss.admin.plugin.service.ops.IOpsPackageManagerV2Service; +import org.opengauss.admin.plugin.utils.DownloadUtil; +import org.opengauss.admin.plugin.utils.PathUtils; +import org.opengauss.admin.system.plugin.facade.SysSettingFacade; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.annotation.Resource; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Future; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * OpsPackageManagerV2Service + * + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Slf4j +@Service +public class OpsPackageManagerV2Service extends ServiceImpl implements IOpsPackageManagerV2Service { + private static final String FASTJSON_TYPE_HANDLER = "typeHandler=com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler"; + @Resource + private WsConnectorManager wsConnectorManager; + @Resource + private DownloadUtil downloadUtil; + @Resource + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private SysSettingFacade sysSettingFacade; + @Resource + private PathUtils pathUtils; + + @Override + public List queryOpsPackageList(PackageDto packageDict) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsPackageManagerEntity.class) + .eq(OpsPackageManagerEntity::getOs, packageDict.getOs()) + .eq(OpsPackageManagerEntity::getOsVersion, packageDict.getOsVersion()) + .eq(OpsPackageManagerEntity::getCpuArch, packageDict.getCpuArch()) + .eq(OpsPackageManagerEntity::getPackageVersion, packageDict.getOpenGaussVersion()) + .eq(OpsPackageManagerEntity::getPackageVersionNum, packageDict.getOpenGaussVersionNum()) + .orderByAsc(OpsPackageManagerEntity::getPackageVersionNum); + return list(queryWrapper).stream().map(OpsPackageManagerEntity::toVO) + .distinct().collect(Collectors.toList()); + } + + @Override + public List listVersionNumber() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.select("distinct package_version_num"); + queryWrapper.isNotNull("package_version_num"); + queryWrapper.orderByDesc("package_version_num"); + List list = list(queryWrapper); + return list.stream().map(OpsPackageManagerEntity::getPackageVersionNum).collect(Collectors.toList()); + } + + @Override + public void checkingPackageList(List packageIds) { + List packageEntityList = getAndValidPackageByIds(packageIds); + for (OpsPackageManagerEntity entity : packageEntityList) { + checkingPackageByEntity(entity); + } + } + + @Override + public boolean checkingPackage(String packageId) { + OpsPackageManagerEntity entity = getById(packageId); + Assert.isTrue(Objects.nonNull(entity), "packageId is not exits"); + return checkingPackageByEntity(entity); + } + + private boolean checkingPackageByEntity(OpsPackageManagerEntity entity) { + UploadInfo packagePath = entity.getPackagePath(); + boolean isValid = false; + if (Objects.nonNull(packagePath)) { + File file = new File(packagePath.getRealPath() + File.separatorChar + packagePath.getName()); + isValid = Files.exists(file.toPath()); + if (isValid) { + log.info("checking package list, packageId: {}, realPath success :{} ", + entity.getPackageId(), entity.getRealPath()); + return isValid; + } + } + + if (Objects.equals(entity.getRemark(), OpsConstants.PACKAGE_REMARK)) { + removeById(entity.getPackageId()); + log.info("checking package list, packageId: {}, realPath file not exit,delete package :{} ", + entity.getPackageId(), entity.getRealPath()); + } else { + UpdateWrapper wrapper = new UpdateWrapper<>(); + wrapper.set("package_path", "").set("source", "").eq("package_id", entity.getPackageId()); + update(wrapper); + log.info("checking package list, packageId: {}, realPath file not exit,clear package_path :{} ", + entity.getPackageId(), entity.getRealPath()); + } + return isValid; + } + + private List getAndValidPackageByIds(List packageIds) { + if (CollectionUtils.isEmpty(packageIds)) { + throw new OpsException("packageId is not empty"); + } + List packageEntityList = list(new LambdaQueryWrapper() + .in(OpsPackageManagerEntity::getPackageId, packageIds)); + if (CollectionUtils.isEmpty(packageEntityList)) { + log.error("packageId {} is not exist", packageIds); + throw new OpsException("packageId " + packageIds + " is not exist"); + } + if (packageIds.size() != packageEntityList.size()) { + List notExistIds = packageEntityList.stream() + .map(OpsPackageManagerEntity::getPackageId) + .filter(packageId -> !packageIds.contains(packageId)) + .collect(Collectors.toList()); + log.error("packageId {} is not exist", notExistIds); + throw new OpsException("packageId " + notExistIds + " is not exist"); + } + return packageEntityList; + } + + @Override + public void delPackage(List packageIds) { + List packageEntityList = getAndValidPackageByIds(packageIds); + packageEntityList.forEach(entry -> { + UploadInfo packagePath = entry.getPackagePath(); + if (Objects.nonNull(packagePath)) { + Path dir = Paths.get(packagePath.getRealPath()); + try (Stream pathStream = Files.walk(dir)) { + pathStream.sorted(Comparator.reverseOrder()).forEach(path -> { + try { + Files.delete(path); + } catch (IOException e) { + log.error("Failed to delete: {}", path, e); + } + }); + } catch (IOException e) { + log.error("Failed to walk directory: {}", dir, e); + } + } + removeById(entry.getPackageId()); + }); + } + + @Override + public void saveOnlinePackage(OpsPackageManagerEntity entity, Integer userId, String wsBusinessId) { + // create package path + UploadInfo packagePath = getPackagePath(entity, userId); + entity.setPackagePath(packagePath); + entity.setSource(OpsPackageSource.ONLINE.getName()); + String realPath = entity.getRealPath(); + checkPackageStoragePath(realPath, false); + // get download progress session + WsSession wsSession = wsConnectorManager.getSession(wsBusinessId).orElseThrow(() -> new OpsException("No output websocket session found")); + // download package + Future future = threadPoolTaskExecutor.submit(() -> downloadUtil.download(entity.getPackageUrl(), realPath, entity.getFileName(), wsSession)); + // register download progress + TaskManager.registry(wsBusinessId, future); + saveOrUpdate(entity); + } + + + @Override + public void updateOnlinePackage(OpsPackageManagerEntity entity, Integer userId, String wsBusinessId) { + if (Objects.isNull(entity.getPackagePath())) { + // create package path + UploadInfo packagePath = getPackagePath(entity, userId); + entity.setPackagePath(packagePath); + } + entity.setSource(OpsPackageSource.ONLINE.getName()); + String realPath = entity.getRealPath(); + checkPackageStoragePath(realPath, true); + // get download progress session + WsSession wsSession = wsConnectorManager.getSession(wsBusinessId).orElseThrow(() -> new OpsException("No output websocket session found")); + // download package + Future future = threadPoolTaskExecutor.submit(() -> downloadUtil.download(entity.getPackageUrl(), realPath, entity.getFileName(), wsSession)); + // register download progress + TaskManager.registry(wsBusinessId, future); + updatePackagePath(entity); + } + + @Override + public boolean hasName(String packageId, String name) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsPackageManagerEntity.class) + .eq(OpsPackageManagerEntity::getName, name) + .eq(StrUtil.isNotEmpty(packageId), OpsPackageManagerEntity::getPackageId, packageId); + return count(queryWrapper) > 0; + } + + @Override + public void saveUploadPackage(OpsPackageManagerEntity pkg, Integer userId) { + UploadInfo packagePath = getPackagePath(pkg, userId); + pkg.setPackagePath(packagePath); + uploadPackage(pkg, false); + // save package info + pkg.setSource(OpsPackageSource.OFFLINE.getName()); + save(pkg); + } + + @Override + public void updateUploadPackage(OpsPackageManagerEntity pkg, Integer userId) { + if (Objects.isNull(pkg.getPackagePath())) { + UploadInfo packagePath = getPackagePath(pkg, userId); + pkg.setPackagePath(packagePath); + } + uploadPackage(pkg, true); + pkg.setSource(OpsPackageSource.OFFLINE.getName()); + updatePackagePath(pkg); + } + + private void updatePackagePath(OpsPackageManagerEntity pkg) { + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); + updateWrapper.eq(OpsPackageManagerEntity::getPackageId, pkg.getPackageId()); + updateWrapper.set(OpsPackageManagerEntity::getPackagePath, pkg.getPackagePath(), FASTJSON_TYPE_HANDLER); + updateWrapper.set(OpsPackageManagerEntity::getSource, pkg.getSource()); + update(updateWrapper); + } + + private void uploadPackage(OpsPackageManagerEntity pkg, boolean isUpdate) { + MultipartFile file = pkg.getFile(); + if (ObjectUtil.isNull(file)) { + return; + } + UploadInfo packagePath = new UploadInfo(); + // create upload folder is the absolute path d:\\ops\\upload + String uploadFolder = pkg.getRealPath(); + // check upload folder + checkPackageStoragePath(uploadFolder, isUpdate); + // upload file + String fileRealPath = Path.of(uploadFolder, file.getOriginalFilename()).toString(); + // save the MultipartFile file to the target (fileRealPath) directory + try { + // transferTo param dest , + // dest is the absolute path to the file you want to save the uploaded file to + file.transferTo(new File(fileRealPath)); + packagePath.setName(file.getOriginalFilename()); + packagePath.setRealPath(uploadFolder); + pkg.setPackagePath(packagePath); + } catch (Exception ex) { + String errMsg = String.format("Upload tar file to %s failed: %s", fileRealPath, ex.getMessage()); + log.error(errMsg); + throw new OpsException(errMsg); + } + } + + /** + *

+     * create package storage folder
+     * realPath folder rule : sysUploadPath/versionNum/os/cpuArch/name
+     * name rule : the binary file name
+     * 
+ * + * @param entity entity + * @param userId user id + * @return UploadInfo + */ + private UploadInfo getPackagePath(OpsPackageManagerEntity entity, Integer userId) { + UploadInfo packagePath = new UploadInfo(); + SysSettingEntity sysSetting = sysSettingFacade.getSysSetting(userId); + Assert.isTrue(Objects.nonNull(sysSetting), "System setting not found"); + String sysUploadPath = pathUtils.getPath(sysSetting.getUploadPath()); + packagePath.setRealPath(sysUploadPath + + entity.getPackageVersionNum() + File.separatorChar + + entity.getOs() + File.separatorChar + + entity.getCpuArch() + File.separatorChar + + entity.getName()); + packagePath.setName(parseDownloadFileName(entity.getPackageUrl())); + return packagePath; + } + + private String parseDownloadFileName(String packageUrl) { + return packageUrl.substring(packageUrl.lastIndexOf("/") + 1); + } + + /** + *
+     * check realPath on the dataKit server disk.
+     * if realPath not exist,create it.
+     * if the realPath exist, check if is update package,
+     * if is update package,rename old package.
+     * 
+ * + * @param realPath real path of package + * @param isUpdate is update package + * @throws OpsException + */ + private void checkPackageStoragePath(String realPath, boolean isUpdate) { + if (StrUtil.isEmpty(realPath)) { + log.error("Cannot find package storage path : " + realPath); + throw new OpsException("Cannot find package storage path :" + realPath); + } + File folder = new File(realPath); + if (!folder.exists()) { + boolean res = folder.mkdirs(); + if (!res) { + String errMsg = String.format("Can't create folder: %s, please try again", realPath); + log.error(errMsg); + throw new OpsException(errMsg); + } + } else if (isUpdate && folder.canWrite()) { + String updatePath = realPath + "-" + DateUtils.dateTimeNow() + ".update"; + log.warn("Rename package storage path to " + updatePath); + folder.renameTo(new File(updatePath)); + folder = new File(realPath); + folder.mkdirs(); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsPackagePathDictService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsPackagePathDictService.java new file mode 100644 index 000000000..3063afaa3 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsPackagePathDictService.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsPackagePathDictService.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsPackagePathDictService.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.dto.ops.PackageDto; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.common.utils.http.HttpUtils; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackagePathDictEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackagePathDictVO; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackageVO; +import org.opengauss.admin.plugin.mapper.ops.OpsPackagePathDictMapper; +import org.opengauss.admin.plugin.service.ops.IOpsPackagePathDictService; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Slf4j +@Service +public class OpsPackagePathDictService extends ServiceImpl implements IOpsPackagePathDictService { + @Override + public List listPackagePathDict() { + return list().stream().map(OpsPackagePathDictEntity::toVO).collect(Collectors.toList()); + } + + @Override + public boolean checkOsExists(String os) { + return list().stream().map(OpsPackagePathDictEntity::getOs).anyMatch(os::equalsIgnoreCase); + } + + @Override + public boolean checkCpuArchExists(String cpuArch) { + return list().stream().map(OpsPackagePathDictEntity::getCpuArch).anyMatch(cpuArch::equalsIgnoreCase); + } + + @Override + public OpsPackagePathDictVO queryPackagePathDict(PackageDto packageDto) { + Optional dictEntity = list().stream() + .filter(item -> StrUtil.equalsIgnoreCase(item.getOs(), packageDto.getOs())) + .filter(item -> StrUtil.equalsIgnoreCase(item.getOsVersion(), packageDto.getOsVersion())) + .filter(item -> StrUtil.equalsIgnoreCase(item.getCpuArch(), packageDto.getCpuArch())) + .filter(item -> StrUtil.equalsIgnoreCase(item.getVersion(), packageDto.getOpenGaussVersion().name())) + .filter(item -> canUsedPkgTmpOfVersionNum(item.getPkgTmpUseVersion(), + packageDto.getOpenGaussVersionNum())) + .findFirst(); + return dictEntity.map(OpsPackagePathDictEntity::toVO).orElse(null); + } + + private boolean canUsedPkgTmpOfVersionNum(String pkgTmpUseVersion, String versionNum) { + return Arrays.stream(pkgTmpUseVersion.split(";")) + .anyMatch(v -> StrUtil.equalsIgnoreCase(v, versionNum)); + } + + @Override + public OpsPackageVO buildOpsPackage(String pkgUrlPrefix, String packageVersionNum, + OpsPackagePathDictVO packageDict) { + // 当前环境在线模式 + if (checkCurrentEnvironmentIsOnline()) { + return buildOpsPackageOnline(pkgUrlPrefix, packageVersionNum, packageDict); + } else { + return buildOpsPackageOffline(pkgUrlPrefix, packageVersionNum, packageDict); + } + } + + private OpsPackageVO buildOpsPackageOffline(String pkgUrlPrefix, String packageVersionNum, + OpsPackagePathDictVO packageDict) { + // 当前环境离线模式: 构建已发布版本下载链接 + String downloadUrl = packageDict.buildFullPackageUrl(pkgUrlPrefix, packageVersionNum, false); + return createOpsPackageVo(packageVersionNum, packageDict, downloadUrl); + } + + private OpsPackageVO buildOpsPackageOnline(String pkgUrlPrefix, String pkgVersionNum, + OpsPackagePathDictVO packageDict) { + // 构建 当前版本号 packageVersionNum last 下载链接 + String downloadUrl = packageDict.buildFullPackageUrl(pkgUrlPrefix, pkgVersionNum, true); + // 检查当前版本号 packageVersionNum 是否是 last 版本 + AjaxResult downloadUrlResult = HttpUtils.checkUrl(downloadUrl); + if (downloadUrlResult.isOk()) { + return createOpsPackageVo(pkgVersionNum, packageDict, downloadUrl); + } + // 当前版本号不是last版本 + downloadUrl = packageDict.buildFullPackageUrl(pkgUrlPrefix, pkgVersionNum, false); + downloadUrlResult = HttpUtils.checkUrl(downloadUrl); + if (downloadUrlResult.isOk()) { + return createOpsPackageVo(pkgVersionNum, packageDict, downloadUrl); + } else { + throw new OpsException("package download url is not ok"); + } + } + + private static OpsPackageVO createOpsPackageVo(String packageVersionNum, OpsPackagePathDictVO packageDict, + String downloadUrl) { + return OpsPackageVO.builder().os(packageDict.getOs()) + .osVersion(packageDict.getOsVersion()) + .cpuArch(packageDict.getCpuArch()) + .packageVersion(packageDict.getVersion()) + .packageVersionNum(packageVersionNum).packageUrl(downloadUrl).build(); + } + + @Override + public boolean checkCurrentEnvironmentIsOnline() { + AjaxResult result = HttpUtils.checkUrl("https://opengauss.org/zh/"); + return result.isOk(); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsWdrServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsWdrServiceImpl.java new file mode 100644 index 000000000..3db159bc2 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsWdrServiceImpl.java @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsWdrServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/OpsWdrServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsWdrEntity; +import org.opengauss.admin.plugin.domain.model.ops.JschResult; +import org.opengauss.admin.plugin.domain.model.ops.SshCommandConstants; +import org.opengauss.admin.plugin.domain.model.ops.WdrGeneratorBody; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.enums.ops.WdrScopeEnum; +import org.opengauss.admin.plugin.enums.ops.WdrTypeEnum; +import org.opengauss.admin.plugin.mapper.ops.OpsWdrMapper; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterService; +import org.opengauss.admin.plugin.service.ops.IOpsWdrService; +import org.opengauss.admin.plugin.utils.DBUtil; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.plugin.vo.ops.DwrSnapshotVO; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import com.jcraft.jsch.Session; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import jakarta.servlet.http.HttpServletResponse; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.text.MessageFormat; +import java.util.*; + +/** + * @author lhf + * @date 2022/10/13 15:14 + **/ +@Slf4j +@Service +public class OpsWdrServiceImpl extends ServiceImpl implements IOpsWdrService { + @Autowired + private IOpsClusterService opsClusterService; + @Autowired + private IOpsClusterNodeService opsClusterNodeService; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostFacade hostFacade; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostUserFacade hostUserFacade; + @Autowired + private DBUtil DBUtil; + @Autowired + private JschUtil jschUtil; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + + @Autowired + private ClusterOpsProviderManager clusterOpsProviderManager; + + @Override + public List listWdr(String clusterId, WdrScopeEnum wdrScope, WdrTypeEnum wdrType, String hostId, Date start, Date end) { + log.info("clusterId:{},wdrScope:{},wdrType:{},hostId:{},start:{},end:{}",clusterId,wdrScope,wdrType,hostId,start,end); + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OpsWdrEntity.class) + .eq(OpsWdrEntity::getClusterId, clusterId) + .eq(Objects.nonNull(wdrScope), OpsWdrEntity::getScope, Objects.nonNull(wdrScope)?wdrScope.name():StrUtil.EMPTY) + .eq(Objects.nonNull(wdrType), OpsWdrEntity::getReportType, Objects.nonNull(wdrType)?wdrType.name():StrUtil.EMPTY) + .eq(StrUtil.isNotEmpty(hostId), OpsWdrEntity::getHostId, hostId) + .ge(Objects.nonNull(start), OpsWdrEntity::getReportAt, start) + .le(Objects.nonNull(end), OpsWdrEntity::getReportAt, end) + .orderByDesc(OpsWdrEntity::getCreateTime); + + return list(queryWrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void generate(WdrGeneratorBody wdrGeneratorBody) { + String clusterId = wdrGeneratorBody.getClusterId(); + OpsClusterEntity clusterEntity = opsClusterService.getById(clusterId); + + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("Cluster node information does not exist"); + } + + WdrScopeEnum scope = wdrGeneratorBody.getScope(); + if (WdrScopeEnum.CLUSTER == scope) { + generateClusterWdr(clusterEntity, opsClusterNodeEntities, wdrGeneratorBody.getType(), wdrGeneratorBody.getStartId(), wdrGeneratorBody.getEndId()); + } else { + generateNodeWdr(clusterEntity, opsClusterNodeEntities, wdrGeneratorBody.getType(), wdrGeneratorBody.getHostId(), wdrGeneratorBody.getStartId(), wdrGeneratorBody.getEndId()); + } + } + + @Override + public Page listSnapshot(Page page, String clusterId, String hostId) { + OpsClusterEntity clusterEntity = opsClusterService.getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + + Connection connection = null; + try { + connection = DBUtil.getSession(hostEntity.getPublicIp(), clusterEntity.getPort(), clusterEntity.getDatabaseUsername(), encryptionUtils.decrypt(clusterEntity.getDatabasePassword())).orElseThrow(() -> new OpsException("Unable to connect to the database")); + return listSnapshot(page.getCurrent(), page.getSize(), connection); + }catch (Exception e) { + log.error("get connection fail"); + }finally { + if (Objects.nonNull(connection)){ + try { + connection.close(); + } catch (SQLException e) { + + } + } + } + + return new Page<>(); + } + + @Override + public void createSnapshot(String clusterId, String hostId) { + OpsClusterEntity clusterEntity = opsClusterService.getById(clusterId); + if (Objects.isNull(clusterEntity)) { + throw new OpsException("Cluster information does not exist"); + } + + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + + List opsClusterNodeEntities = opsClusterNodeService.listClusterNodeByClusterId(clusterId); + if (CollUtil.isEmpty(opsClusterNodeEntities)) { + throw new OpsException("Cluster node information is empty"); + } + + OpsClusterNodeEntity nodeEntity = opsClusterNodeEntities.stream().filter(node -> node.getHostId().equals(hostId)).findFirst().orElseThrow(() -> new OpsException("Cluster node configuration not found")); + + String installUserId = nodeEntity.getInstallUserId(); + OpsHostUserEntity userEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(userEntity)) { + throw new OpsException("Installation user information does not exist"); + } + + Session session = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), userEntity.getUsername(), encryptionUtils.decrypt(userEntity.getPassword())).orElseThrow(() -> new OpsException("Failed to establish session with host")); + + try { + clusterOpsProviderManager.provider(clusterEntity.getVersion()).orElseThrow(() -> new OpsException("The current version does not support")).enableWdrSnapshot(session, clusterEntity, opsClusterNodeEntities, WdrScopeEnum.CLUSTER, null); + String clientLoginOpenGauss = MessageFormat.format(SshCommandConstants.LOGIN, String.valueOf(clusterEntity.getPort())); + Map> response = new HashMap<>(); + List responseList = new ArrayList<>(); + String sql = "select create_wdr_snapshot();\n\n\\q"; + + responseList.add(sql); + + response.put("openGauss=#", responseList); + JschResult jschResult = jschUtil.executeCommandWithSerialResponse(clusterEntity.getEnvPath(), clientLoginOpenGauss, session, response, null); + if (0 != jschResult.getExitCode()) { + log.error("Generate wdr snapshot exception, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Generate wdr snapshot exception"); + } + + } catch (Exception e) { + log.error("Generate wdr snapshot exception", e); + throw new OpsException("Generate wdr snapshot exception"); + }finally { + if (Objects.nonNull(session) && session.isConnected()){ + session.disconnect(); + } + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void del(String id) { + OpsWdrEntity wdrEntity = getById(id); + if (Objects.isNull(wdrEntity)) { + throw new OpsException("The record to delete does not exist"); + } + + String hostId = wdrEntity.getHostId(); + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + + List hostUserList = hostUserFacade.listHostUserByHostId(hostId); + if (CollUtil.isEmpty(hostUserList)) { + throw new OpsException("Host user information does not exist"); + } + + OpsHostUserEntity installUser = hostUserList.stream().filter(userEntity -> !"root".equals(userEntity.getUsername())).findFirst().orElseThrow(() -> new OpsException("No installation user information found")); + + Session session = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), installUser.getUsername(), encryptionUtils.decrypt(installUser.getPassword())).orElseThrow(() -> new OpsException("Failed to establish connection with host")); + + try { + rmFile(session, wdrEntity.getReportPath(), wdrEntity.getReportName()); + }finally { + if (Objects.nonNull(session) && session.isConnected()){ + session.disconnect(); + } + } + + removeById(id); + } + + @Override + public void downloadWdr(String wdrId, HttpServletResponse response) { + OpsWdrEntity wdrEntity = getById(wdrId); + if (Objects.isNull(wdrEntity)){ + throw new OpsException("wdr information not found"); + } + + String hostId = wdrEntity.getHostId(); + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)){ + throw new OpsException("host information not found"); + } + + OpsHostUserEntity hostUserEntity = hostUserFacade.getById(wdrEntity.getUserId()); + if (Objects.isNull(hostUserEntity)){ + throw new OpsException("No user information was found to generate the report"); + } + + Session session = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), hostUserEntity.getUsername(), encryptionUtils.decrypt(hostUserEntity.getPassword())).orElseThrow(() -> new OpsException("user failed to establish connection")); + + try { + jschUtil.download(session, wdrEntity.getReportPath(), wdrEntity.getReportName(), response); + }finally { + if (Objects.nonNull(session) && session.isConnected()){ + session.disconnect(); + } + } + } + + private void rmFile(Session session, String reportPath, String reportName) { + String command = MessageFormat.format(SshCommandConstants.DEL_FILE, reportPath + "/" + reportName); + try { + JschResult jschResult = jschUtil.executeCommand(command, session); + if (0 != jschResult.getExitCode()) { + log.error("delete wdr failed,code:{},msg:{}", jschResult.getExitCode(), jschResult.getResult()); + } + } catch (Exception e) { + log.error("delete wdr failed"); + } + } + + private void generateNodeWdr(OpsClusterEntity clusterEntity, List opsClusterNodeEntities, WdrTypeEnum type, String hostId, String startId, String endId) { + OpsClusterNodeEntity nodeEntity = opsClusterNodeEntities.stream().filter(node -> node.getHostId().equals(hostId)).findFirst().orElseThrow(() -> new OpsException("Cluster node configuration not found")); + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + + String installUserId = nodeEntity.getInstallUserId(); + OpsHostUserEntity userEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(userEntity)) { + throw new OpsException("Installation user information does not exist"); + } + + Session session = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), userEntity.getUsername(), encryptionUtils.decrypt(userEntity.getPassword())).orElseThrow(() -> new OpsException("Failed to establish session with host")); + try { + clusterOpsProviderManager.provider(clusterEntity.getVersion()).orElseThrow(() -> new OpsException("The current version does not support")).enableWdrSnapshot(session, clusterEntity, opsClusterNodeEntities, WdrScopeEnum.CLUSTER, null); + + String wdrPath = "/home/" + userEntity.getUsername(); + String wdrName = "WDR-" + StrUtil.uuid() + ".html"; + doGenerate(wdrPath, wdrName, startId, endId, WdrScopeEnum.NODE, type, session, clusterEntity.getPort(),clusterEntity.getEnvPath()); + OpsWdrEntity opsWdrEntity = new OpsWdrEntity(); + opsWdrEntity.setScope(WdrScopeEnum.NODE); + opsWdrEntity.setReportAt(new Date()); + opsWdrEntity.setReportType(type); + opsWdrEntity.setReportName(wdrName); + opsWdrEntity.setReportPath(wdrPath); + opsWdrEntity.setStartSnapshotId(startId); + opsWdrEntity.setEndSnapshotId(endId); + opsWdrEntity.setClusterId(clusterEntity.getClusterId()); + opsWdrEntity.setUserId(userEntity.getHostUserId()); + opsWdrEntity.setHostId(hostId); + opsWdrEntity.setNodeId(nodeEntity.getClusterNodeId()); + save(opsWdrEntity); + }finally { + if (Objects.nonNull(session) && session.isConnected()){ + session.disconnect(); + } + } + } + + private void generateClusterWdr(OpsClusterEntity clusterEntity, List opsClusterNodeEntities, WdrTypeEnum type, String startId, String endId) { + OpsClusterNodeEntity masterNodeEntity = opsClusterNodeEntities.stream().filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER).findFirst().orElseThrow(() -> new OpsException("Cluster master configuration not found")); + String hostId = masterNodeEntity.getHostId(); + OpsHostEntity hostEntity = hostFacade.getById(hostId); + if (Objects.isNull(hostEntity)) { + throw new OpsException("host information does not exist"); + } + + String installUserId = masterNodeEntity.getInstallUserId(); + OpsHostUserEntity userEntity = hostUserFacade.getById(installUserId); + if (Objects.isNull(userEntity)) { + throw new OpsException("Installation user information does not exist"); + } + + Session session = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), userEntity.getUsername(), encryptionUtils.decrypt(userEntity.getPassword())).orElseThrow(() -> new OpsException("Failed to establish session with host")); + + try { + clusterOpsProviderManager.provider(clusterEntity.getVersion()).orElseThrow(() -> new OpsException("The current version does not support")).enableWdrSnapshot(session, clusterEntity, opsClusterNodeEntities, WdrScopeEnum.CLUSTER, null); + + String wdrPath = "/home/" + userEntity.getUsername(); + String wdrName = "WDR-" + StrUtil.uuid() + ".html"; + doGenerate(wdrPath, wdrName, startId, endId, WdrScopeEnum.CLUSTER, type, session, clusterEntity.getPort(), clusterEntity.getEnvPath()); + + OpsWdrEntity opsWdrEntity = new OpsWdrEntity(); + opsWdrEntity.setScope(WdrScopeEnum.CLUSTER); + opsWdrEntity.setHostId(masterNodeEntity.getHostId()); + opsWdrEntity.setReportAt(new Date()); + opsWdrEntity.setReportType(type); + opsWdrEntity.setReportName(wdrName); + opsWdrEntity.setReportPath(wdrPath); + opsWdrEntity.setStartSnapshotId(startId); + opsWdrEntity.setEndSnapshotId(endId); + opsWdrEntity.setClusterId(clusterEntity.getClusterId()); + opsWdrEntity.setUserId(userEntity.getHostUserId()); + opsWdrEntity.setNodeId(masterNodeEntity.getClusterNodeId()); + save(opsWdrEntity); + }finally { + if (Objects.nonNull(session) && session.isConnected()){ + session.disconnect(); + } + } + } + + private Page listSnapshot(Long pageNo, Long pageSize, Connection connection) { + Page page = new Page<>(); + List res = new ArrayList<>(); + String sql = "select * from snapshot.snapshot"; + if (Objects.nonNull(pageNo) && Objects.nonNull(pageSize) && pageNo > 0 && pageSize > 0) { + String countSql = "select count(*) from snapshot.snapshot"; + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(countSql)){ + resultSet.next(); + int count = resultSet.getInt("count"); + page.setTotal(count); + }catch (Exception e){ + log.error("Query snapshot record exception", e); + } + + sql = "select * from snapshot.snapshot LIMIT " + pageSize + "OFFSET " + (pageNo - 1) * pageSize; + } + + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(sql);) { + while (resultSet.next()) { + DwrSnapshotVO dwrSnapshotVO = new DwrSnapshotVO(); + dwrSnapshotVO.setSnapshot_id(resultSet.getString("snapshot_id")); + dwrSnapshotVO.setStart_ts(resultSet.getTimestamp("start_ts")); + dwrSnapshotVO.setEnd_ts(resultSet.getTimestamp("end_ts")); + res.add(dwrSnapshotVO); + } + } catch (Exception e) { + log.error("Query snapshot record exception", e); + } + + page.setRecords(res); + return page; + } + + private void doGenerate(String wdrPath, String wdrName, String startId, String endId, WdrScopeEnum scope, WdrTypeEnum type, Session session, Integer port, String envPath) { + String clientLoginOpenGauss = MessageFormat.format(SshCommandConstants.LOGIN, String.valueOf(port)); + try { + String nodeName = null; + if (scope == WdrScopeEnum.NODE){ + nodeName = "pgxc_node_str()::cstring"; + } + Map> response = new HashMap<>(); + List responseList = new ArrayList<>(); + String startSql = "\\a \\t \\o " + wdrPath + "/" + wdrName + "\n"; + String generateSql = "select generate_wdr_report('" + startId + "', '" + endId + "', '" + type.name().toLowerCase() + "', '" + scope.name().toLowerCase() + "'," + nodeName +"); \n"; + String endSql = "\\o \\a \\t \n \\q"; + + responseList.add(startSql); + responseList.add(generateSql); + responseList.add(endSql); + + response.put("openGauss=#", responseList); + JschResult jschResult = jschUtil.executeCommandWithSerialResponse(envPath, clientLoginOpenGauss, session, response, null); + if (0 != jschResult.getExitCode()) { + log.error("Generated wdr exception, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("generate wdr exception"); + } + + } catch (Exception e) { + log.error("generate wdr exception", e); + throw new OpsException("generate wdr exception"); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/WsServiceImpl.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/WsServiceImpl.java new file mode 100644 index 000000000..7364a7ce2 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/WsServiceImpl.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WsServiceImpl.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/WsServiceImpl.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl; + +import com.gitee.starblues.annotation.Extract; +import com.jcraft.jsch.JSchException; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.model.ops.WsSession; +import org.opengauss.admin.plugin.domain.model.ops.cache.SSHChannelManager; +import org.opengauss.admin.plugin.domain.model.ops.cache.TaskManager; +import org.opengauss.admin.plugin.domain.model.ops.cache.WsConnectorManager; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.system.plugin.extract.SocketExtract; +import com.jcraft.jsch.ChannelShell; + +import jakarta.websocket.Session; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.Objects; + + +/** + * @author lhf + * @date 2022/8/6 18:14 + **/ +@Slf4j +@Service +@Extract(bus = "ops-handler") +public class WsServiceImpl implements SocketExtract { + @Autowired + private WsConnectorManager wsConnectorManager; + @Autowired + private JschUtil jschUtil; + @Autowired + private SSHChannelManager sshChannelManager; + + @Override + public void onOpen(String pluginId, String sessionId, Session session) { + log.info("WebSocket onOpen, pluginId: {}", pluginId); + wsConnectorManager.register(sessionId,new WsSession(session,sessionId)); + } + + @Override + public void processMessage(String sessionId, String message) { + log.info("WebSocket processMessage, message: {}", message); + ChannelShell channelShell = sshChannelManager.getChannelShell(sessionId).orElseThrow(() -> new OpsException("No connection information found")); + jschUtil.sendToChannelShell(channelShell, message); + } + + @Override + public void onClose(String pluginId, String sessionId) { + log.info("WebSocket onClose, pluginId: {}", pluginId); + sshChannelManager.getChannelShell(sessionId).ifPresent(channelShell -> { + com.jcraft.jsch.Session session = null; + try { + session = channelShell.getSession(); + } catch (JSchException e) { + log.error("get channelSession By channelShell fail",e); + } + + if (Objects.nonNull(session) && session.isConnected()){ + session.disconnect(); + } + + if (channelShell.isConnected()){ + channelShell.disconnect(); + } + }); + + wsConnectorManager.getSession(sessionId).ifPresent(wsSession -> { + try { + wsSession.getSession().close(); + } catch (IOException e) { + log.error("close websocket session fail",e); + } + }); + + TaskManager.remove(sessionId).ifPresent(future -> future.cancel(true)); + wsConnectorManager.remove(sessionId); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/CheckDeployTypeFunction.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/CheckDeployTypeFunction.java new file mode 100644 index 000000000..7305defcc --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/CheckDeployTypeFunction.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckDeployTypeFunction.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/CheckDeployTypeFunction.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.function; + +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; + +/** + * CheckDeployTypeFunction + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@FunctionalInterface +public interface CheckDeployTypeFunction { + /** + * check cluster node by deploy type and size + * @param deployType deploy type + * @param size size + */ + void check(DeployTypeEnum deployType, int size); +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/CheckDeployTypeService.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/CheckDeployTypeService.java new file mode 100644 index 000000000..520c2b028 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/CheckDeployTypeService.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckDeployTypeFunctionInstance.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/CheckDeployTypeFunctionInstance.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.function; + +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * CheckDeployTypeFunctionInstance + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Service +public class CheckDeployTypeService { + private Map checkFunctions = new HashMap<>(); + + private final CheckDeployTypeFunction lite = (deployType, size) -> { + if (Objects.equals(deployType, DeployTypeEnum.SINGLE_NODE)) { + Assert.isTrue(size == 1, "Lite version single node deployment mode, host node number must be 1"); + } else { + Assert.isTrue(size == 2, "Lite version cluster deployment mode, host node number must be 2"); + } + }; + + private final CheckDeployTypeFunction minimal = (deployType, size) -> { + Assert.isTrue(size == 1, "minimal list version,host node number must be 1"); + }; + + private final CheckDeployTypeFunction enterprise = (deployType, size) -> { + if (Objects.equals(deployType, DeployTypeEnum.SINGLE_NODE)) { + Assert.isTrue(size == 1, "enterprise version single node deployment mode, host node number must be 1"); + } else { + Assert.isTrue(size > 1 && size < 10, "enterprise version cluster deployment mode ,host node number must be greater than 1 and less than 10"); + } + }; + + /** + * constructor + */ + public CheckDeployTypeService() { + checkFunctions.put(OpenGaussVersionEnum.LITE, lite); + checkFunctions.put(OpenGaussVersionEnum.MINIMAL_LIST, minimal); + checkFunctions.put(OpenGaussVersionEnum.ENTERPRISE, enterprise); + } + + public void check(OpenGaussVersionEnum version, DeployTypeEnum deployType, int size) { + Assert.isTrue(Objects.nonNull(version), "OpenGaussVersion can not be null"); + Assert.isTrue(Objects.nonNull(deployType), "DeployTypeEnum can not be null"); + checkFunctions.get(version).check(deployType, size); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/ClusterTaskPathFactory.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/ClusterTaskPathFactory.java new file mode 100644 index 000000000..347e234cc --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/ClusterTaskPathFactory.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterTaskPathFactory.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/ClusterTaskPathFactory.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.function; + +import cn.hutool.core.util.StrUtil; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * ClusterTaskPathFactory + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Service +public class ClusterTaskPathFactory { + private ClusterTaskPathFunction taskPathFunction = path -> { + if (StrUtil.isNotEmpty(path)) { + String[] split = path.split("/"); + if (split.length > 1) { + return "/" + split[1]; + } + } + return ""; + }; + + /** + * Add path to pathMap + * + * @param pathMap pathMap + * @param path path + */ + public void addPath(Map pathMap, String path) { + if (StrUtil.isNotEmpty(path)) { + String apply = taskPathFunction.apply(path); + pathMap.put(apply, ""); + } + } + + /** + * Apply path ,get the path after applying the function + * + * @param path path + * @return top path + */ + public String applyPath(String path) { + return taskPathFunction.apply(path); + } + + /** + * Add all paths to pathMap + * + * @param pathMap pathMap + * @param paths paths + */ + public void addAllPath(Map pathMap, List paths) { + paths.forEach(path -> { + addPath(pathMap, path); + }); + } + + /** + * ClusterTaskPathFunction + */ + @FunctionalInterface + public interface ClusterTaskPathFunction { + /** + * apply path function + * + * @param path path + * @return top path + */ + String apply(String path); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/GenerateClusterConfigXml.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/GenerateClusterConfigXml.java new file mode 100644 index 000000000..8cdbac7f8 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/GenerateClusterConfigXml.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * GenerateClusterConfigXml.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/GenerateClusterConfigXml.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.function; + +import org.opengauss.admin.plugin.domain.model.ops.InstallContext; + +/** + * GenerateClusterConfigXml + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@FunctionalInterface +public interface GenerateClusterConfigXml { + /** + * generate openGauss enterprise cluster config xml + * + * @param installContext install context + * @return xml + */ + String generate(InstallContext installContext); +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/GenerateClusterConfigXmlInstance.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/GenerateClusterConfigXmlInstance.java new file mode 100644 index 000000000..6c4a99400 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/GenerateClusterConfigXmlInstance.java @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * GenerateClusterConfigXmlInstance.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/GenerateClusterConfigXmlInstance.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.function; + +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.model.ops.EnterpriseInstallConfig; +import org.opengauss.admin.plugin.domain.model.ops.InstallContext; +import org.opengauss.admin.plugin.domain.model.ops.SharingStorageInstallConfig; +import org.opengauss.admin.plugin.domain.model.ops.node.EnterpriseInstallNodeConfig; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.enums.ops.ConnectTypeEnum; +import org.opengauss.admin.plugin.enums.ops.DatabaseKernelArch; +import org.opengauss.admin.plugin.mapper.ops.OpsBaseHostMapper; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import jakarta.annotation.Resource; +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static java.lang.Math.min; + +/** + * GenerateClusterConfigXml + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Slf4j +@Service +public class GenerateClusterConfigXmlInstance { + private static final int DEFAULT_SSH_PORT = 22; + + @Resource + private OpsBaseHostMapper opsBaseHostMapper; + + private final GenerateClusterConfigXml clusterConfigXml = (installContext) -> { + String xmlString = ""; + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + try { + // Disallow the DTDs (doctypes) entirely. + documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + + // Or do the following: + documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); + documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + documentBuilderFactory.setXIncludeAware(false); + documentBuilderFactory.setExpandEntityReferences(false); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + Document document = documentBuilder.newDocument(); + document.setXmlVersion("1.0"); + document.setXmlStandalone(true); + Element rootElement = document.createElement("ROOT"); + + Element cluster = document.createElement("CLUSTER"); + appendClusterParam(document, cluster, installContext); + + Element deviceList = document.createElement("DEVICELIST"); + appendDeviceList(document, deviceList, installContext); + + rootElement.appendChild(cluster); + rootElement.appendChild(deviceList); + + document.appendChild(rootElement); + + TransformerFactory transFactory = TransformerFactory.newInstance(); + transFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + transFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + transFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); + Transformer transformer = transFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + DOMSource domSource = new DOMSource(document); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + transformer.transform(domSource, new StreamResult(byteArrayOutputStream)); + xmlString = byteArrayOutputStream.toString(StandardCharsets.UTF_8); + } catch (Exception e) { + log.error("generate xml exception:", e); + throw new OpsException("generate xml exception"); + } + return xmlString; + }; + + /** + * generate openGauss enterprise cluster config xml + * + * @param installContext install context + * @return xml + */ + public String generate(InstallContext installContext) { + return clusterConfigXml.generate(installContext); + } + + private void appendDeviceList(Document document, Element deviceList, InstallContext installContext) { + List nodeConfigList = installContext + .getEnterpriseInstallConfig().getNodeConfigList(); + for (EnterpriseInstallNodeConfig enterpriseInstallNodeConfig : nodeConfigList) { + Element device = document.createElement("DEVICE"); + device.setAttribute("sn", enterpriseInstallNodeConfig.getHostname()); + deviceList.appendChild(device); + + appendDeviceParam(document, device, enterpriseInstallNodeConfig, + nodeConfigList, installContext); + } + } + + private void appendSshPortParam(Document document, Element device, + EnterpriseInstallNodeConfig enterpriseInstallNodeConfig, + InstallContext installContext) { + int port = opsBaseHostMapper.queryPortByHostId(enterpriseInstallNodeConfig.getHostId()); + String opengaussVersion = installContext.getOpenGaussVersionNum(); + if (opengaussVersion != null && !opengaussVersion.isEmpty()) { + String versionNum = opengaussVersion.split("\\.")[0]; + if (Integer.parseInt(versionNum) >= 7) { + Element sshPort = document.createElement("PARAM"); + sshPort.setAttribute("name", "sshPort"); + sshPort.setAttribute("value", String.valueOf(port)); + device.appendChild(sshPort); + } else { + if (port != DEFAULT_SSH_PORT) { + throw new OpsException("invalid ssh port, expected is: 22, actual is: " + port); + } + } + } + } + + private void appendDeviceParam(Document document, Element device, + EnterpriseInstallNodeConfig enterpriseInstallNodeConfig, + List nodeConfigList, + InstallContext installContext) { + Element name = document.createElement("PARAM"); + name.setAttribute("name", "name"); + name.setAttribute("value", enterpriseInstallNodeConfig.getHostname()); + device.appendChild(name); + + Element azName = document.createElement("PARAM"); + azName.setAttribute("name", "azName"); + azName.setAttribute("value", enterpriseInstallNodeConfig.getAzName()); + device.appendChild(azName); + + Element azPriority = document.createElement("PARAM"); + azPriority.setAttribute("name", "azPriority"); + azPriority.setAttribute("value", enterpriseInstallNodeConfig.getAzPriority()); + device.appendChild(azPriority); + + Element backIp1 = document.createElement("PARAM"); + backIp1.setAttribute("name", "backIp1"); + backIp1.setAttribute("value", enterpriseInstallNodeConfig.getPrivateIp()); + device.appendChild(backIp1); + + appendSshPortParam(document, device, enterpriseInstallNodeConfig, installContext); + EnterpriseInstallConfig enterpriseInstallConfig = installContext.getEnterpriseInstallConfig(); + if (enterpriseInstallConfig.getIsInstallCM()) { + if (enterpriseInstallNodeConfig.getIsCMMaster()) { + Element cmsNum = document.createElement("PARAM"); + cmsNum.setAttribute("name", "cmsNum"); + cmsNum.setAttribute("value", "1"); + device.appendChild(cmsNum); + + Element cmServerPortBase = document.createElement("PARAM"); + cmServerPortBase.setAttribute("name", "cmServerPortBase"); + cmServerPortBase.setAttribute("value", String.valueOf(enterpriseInstallNodeConfig.getCmPort())); + device.appendChild(cmServerPortBase); + + Element cmServerListenIp1 = document.createElement("PARAM"); + cmServerListenIp1.setAttribute("name", "cmServerListenIp1"); + Object[] ips = nodeConfigList.stream().map(EnterpriseInstallNodeConfig::getPrivateIp).toArray(); + cmServerListenIp1.setAttribute("value", StringUtils.arrayToCommaDelimitedString(ips)); + device.appendChild(cmServerListenIp1); + + Element cmServerHaIp1 = document.createElement("PARAM"); + cmServerHaIp1.setAttribute("name", "cmServerHaIp1"); + cmServerHaIp1.setAttribute("value", StringUtils.arrayToCommaDelimitedString(ips)); + device.appendChild(cmServerHaIp1); + + Element cmServerlevel = document.createElement("PARAM"); + cmServerlevel.setAttribute("name", "cmServerlevel"); + cmServerlevel.setAttribute("value", "1"); + device.appendChild(cmServerlevel); + + Element cmServerRelation = document.createElement("PARAM"); + cmServerRelation.setAttribute("name", "cmServerRelation"); + Object[] hostnames = nodeConfigList.stream().map(EnterpriseInstallNodeConfig::getHostname).toArray(); + cmServerRelation.setAttribute("value", StringUtils.arrayToCommaDelimitedString(hostnames)); + device.appendChild(cmServerRelation); + + Element cmDir = document.createElement("PARAM"); + cmDir.setAttribute("name", "cmDir"); + cmDir.setAttribute("value", enterpriseInstallNodeConfig.getCmDataPath()); + device.appendChild(cmDir); + } else { + Element cmDir = document.createElement("PARAM"); + cmDir.setAttribute("name", "cmDir"); + cmDir.setAttribute("value", enterpriseInstallNodeConfig.getCmDataPath()); + device.appendChild(cmDir); + + Element cmServerPortStandby = document.createElement("PARAM"); + cmServerPortStandby.setAttribute("name", "cmServerPortStandby"); + cmServerPortStandby.setAttribute("value", String.valueOf(enterpriseInstallNodeConfig.getCmPort())); + device.appendChild(cmServerPortStandby); + } + + + } + + if (enterpriseInstallNodeConfig.getClusterRole() == ClusterRoleEnum.MASTER) { + Element dataNum = document.createElement("PARAM"); + dataNum.setAttribute("name", "dataNum"); + dataNum.setAttribute("value", "1"); + device.appendChild(dataNum); + + Element dataPortBase = document.createElement("PARAM"); + dataPortBase.setAttribute("name", "dataPortBase"); + dataPortBase.setAttribute("value", String.valueOf(enterpriseInstallConfig.getPort())); + device.appendChild(dataPortBase); + + Element dataNode1 = document.createElement("PARAM"); + dataNode1.setAttribute("name", "dataNode1"); + List nodeConfigs = nodeConfigList.stream().collect(Collectors.toList()); + EnterpriseInstallNodeConfig master = nodeConfigs.stream().filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER).findFirst().orElseThrow(() -> new OpsException("master node information not found")); + StringBuilder dataNodeValue = new StringBuilder(master.getDataPath()); + for (EnterpriseInstallNodeConfig nodeConfig : nodeConfigs) { + if (!master.equals(nodeConfig)) { + dataNodeValue.append(",") + .append(nodeConfig.getHostname()) + .append(",") + .append(nodeConfig.getDataPath()); + } + + } + dataNode1.setAttribute("value", dataNodeValue.toString()); + device.appendChild(dataNode1); + + Element dataNode1_syncNum = document.createElement("PARAM"); + dataNode1_syncNum.setAttribute("name", "dataNode1_syncNum"); + dataNode1_syncNum.setAttribute("value", "0"); + device.appendChild(dataNode1_syncNum); + } + } + + + private void appendSharingStorageClusterParam(Document document, Element cluster, InstallContext installContext) { + SharingStorageInstallConfig sharingStorageInstallConfig = installContext.getEnterpriseInstallConfig().getSharingStorageInstallConfig(); + Element isEnableDss = document.createElement("PARAM"); + isEnableDss.setAttribute("name", "enable_dss"); + isEnableDss.setAttribute("value", "on"); + cluster.appendChild(isEnableDss); + + Element dssHome = document.createElement("PARAM"); + dssHome.setAttribute("name", "dss_home"); + dssHome.setAttribute("value", sharingStorageInstallConfig.getDssHome()); + cluster.appendChild(dssHome); + + Element dssVgName = document.createElement("PARAM"); + dssVgName.setAttribute("name", "ss_dss_vg_name"); + dssVgName.setAttribute("value", sharingStorageInstallConfig.getDssVgName()); + cluster.appendChild(dssVgName); + + Element dssVgInfo = document.createElement("PARAM"); + dssVgInfo.setAttribute("name", "dss_vg_info"); + List xlogVgName = Arrays.asList(sharingStorageInstallConfig.getXlogVgName().split(",")); + List xlogLunPath = sharingStorageInstallConfig.getXlogLunLinkPath(); + if (xlogVgName.size() != xlogLunPath.size()) { + log.warn("xlog vg name size {} is not corresponding to xlog lun path size {}", xlogVgName.size(), + xlogLunPath.size()); + } + + List combine = new ArrayList<>(); + for (int i = 0; i < min(xlogVgName.size(), xlogLunPath.size()); ++i) { + combine.add(xlogVgName.get(i).trim() + ":" + xlogLunPath.get(i).trim()); + } + String finalStr = StringUtils.collectionToCommaDelimitedString(combine); + Object[] vgInfo = + new String[]{sharingStorageInstallConfig.getDssVgName().trim() + ":" + + sharingStorageInstallConfig.getDssDataLunLinkPath().trim(), finalStr}; + dssVgInfo.setAttribute("value", StringUtils.arrayToCommaDelimitedString(vgInfo)); + cluster.appendChild(dssVgInfo); + + Element votingLun = document.createElement("PARAM"); + votingLun.setAttribute("name", "votingDiskPath"); + votingLun.setAttribute("value", sharingStorageInstallConfig.getCmVotingLunLinkPath()); + cluster.appendChild(votingLun); + + Element sharingLun = document.createElement("PARAM"); + sharingLun.setAttribute("name", "shareDiskDir"); + sharingLun.setAttribute("value", sharingStorageInstallConfig.getCmSharingLunLinkPath()); + cluster.appendChild(sharingLun); + + Element enableSsl = document.createElement("PARAM"); + enableSsl.setAttribute("name", "dss_ssl_enable"); + String ssl = sharingStorageInstallConfig.isEnableSsl() ? "on" : "off"; + enableSsl.setAttribute("value", ssl); + cluster.appendChild(enableSsl); + + if (sharingStorageInstallConfig.getInterconnectType() == ConnectTypeEnum.RDMA) { + Element interconnectType = document.createElement("PARAM"); + interconnectType.setAttribute("name", "ss_interconnect_type"); + interconnectType.setAttribute("value", String.valueOf(sharingStorageInstallConfig.getInterconnectType())); + cluster.appendChild(interconnectType); + + Element rdmaConfig = document.createElement("PARAM"); + rdmaConfig.setAttribute("name", "ss_rdma_work_config"); + rdmaConfig.setAttribute("value", sharingStorageInstallConfig.getRdamConfig()); + cluster.appendChild(rdmaConfig); + + Element rdmaLogPath = document.createElement("PARAM"); + rdmaLogPath.setAttribute("name", "ss_ock_log_path"); + rdmaLogPath.setAttribute("value", sharingStorageInstallConfig.getRdamLogPath()); + cluster.appendChild(rdmaLogPath); + } + } + + private void appendClusterParam(Document document, Element cluster, InstallContext installContext) { + Element clusterName = document.createElement("PARAM"); + clusterName.setAttribute("name", "clusterName"); + clusterName.setAttribute("value", installContext.getClusterId()); + cluster.appendChild(clusterName); + + Element nodeNames = document.createElement("PARAM"); + nodeNames.setAttribute("name", "nodeNames"); + List hostNames = installContext.getEnterpriseInstallConfig() + .getNodeConfigList().stream() + .map(EnterpriseInstallNodeConfig::getHostname).collect(Collectors.toList()); + nodeNames.setAttribute("value", StringUtils.collectionToCommaDelimitedString(hostNames)); + cluster.appendChild(nodeNames); + + Element gaussdbAppPath = document.createElement("PARAM"); + gaussdbAppPath.setAttribute("name", "gaussdbAppPath"); + gaussdbAppPath.setAttribute("value", installContext.getEnterpriseInstallConfig().getInstallPath()); + cluster.appendChild(gaussdbAppPath); + + Element gaussdbLogPath = document.createElement("PARAM"); + gaussdbLogPath.setAttribute("name", "gaussdbLogPath"); + gaussdbLogPath.setAttribute("value", installContext.getEnterpriseInstallConfig().getLogPath()); + cluster.appendChild(gaussdbLogPath); + + Element tmpMppdbPath = document.createElement("PARAM"); + tmpMppdbPath.setAttribute("name", "tmpMppdbPath"); + tmpMppdbPath.setAttribute("value", installContext.getEnterpriseInstallConfig().getTmpPath()); + cluster.appendChild(tmpMppdbPath); + + Element gaussdbToolPath = document.createElement("PARAM"); + gaussdbToolPath.setAttribute("name", "gaussdbToolPath"); + gaussdbToolPath.setAttribute("value", installContext.getEnterpriseInstallConfig().getOmToolsPath()); + cluster.appendChild(gaussdbToolPath); + + Element corePath = document.createElement("PARAM"); + corePath.setAttribute("name", "corePath"); + corePath.setAttribute("value", installContext.getEnterpriseInstallConfig().getCorePath()); + cluster.appendChild(corePath); + + Element backIp1s = document.createElement("PARAM"); + backIp1s.setAttribute("name", "backIp1s"); + Object[] privateIps = installContext.getEnterpriseInstallConfig().getNodeConfigList().stream().map(EnterpriseInstallNodeConfig::getPrivateIp).toArray(); + backIp1s.setAttribute("value", StringUtils.arrayToCommaDelimitedString(privateIps)); + cluster.appendChild(backIp1s); + + if (installContext.getEnterpriseInstallConfig().getEnableDCF()) { + Element enableDcf = document.createElement("PARAM"); + enableDcf.setAttribute("name", "enable_dcf"); + enableDcf.setAttribute("value", "on"); + cluster.appendChild(enableDcf); + + Element dcfConfig = document.createElement("PARAM"); + dcfConfig.setAttribute("name", "dcf_config"); + + StringBuilder res = new StringBuilder("["); + String template = "'{'\"stream_id\":1,\"node_id\":{0},\"ip\":\"{1}\",\"port\":{2},\"role\":\"{3}\"'}'"; + List nodeConfigList = installContext.getEnterpriseInstallConfig().getNodeConfigList(); + for (int i = 0; i < nodeConfigList.size(); i++) { + EnterpriseInstallNodeConfig enterpriseInstallNodeConfig = nodeConfigList.get(i); + String format = MessageFormat.format(template, i + 1, + enterpriseInstallNodeConfig.getPrivateIp(), + String.valueOf(installContext.getEnterpriseInstallConfig().getDcfPort()), + enterpriseInstallNodeConfig + .getClusterRole() == ClusterRoleEnum.MASTER ? "LEADER" : "FOLLOWER"); + res.append(format); + res.append(","); + } + + res.deleteCharAt(res.length() - 1); + res.append("]"); + + dcfConfig.setAttribute("value", res.toString()); + cluster.appendChild(dcfConfig); + } + + if (installContext.getEnterpriseInstallConfig().getDatabaseKernelArch() == DatabaseKernelArch.SHARING_STORAGE) { + appendSharingStorageClusterParam(document, cluster, installContext); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/InstallContextConfigFunction.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/InstallContextConfigFunction.java new file mode 100644 index 000000000..d658eb902 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/InstallContextConfigFunction.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * InstallContextConfigFunction.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/InstallContextConfigFunction.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.function; + +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.InstallContext; + +import java.util.List; + +/** + * InstallContextConfigFunction + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@FunctionalInterface +public interface InstallContextConfigFunction { + + /** + * fill installContext config info by task and taskNodes + * + * @param installContext installContext + * @param task task + * @param taskNodeList taskNodeList + */ + void accept(InstallContext installContext, OpsClusterTaskEntity task, List taskNodeList); +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/InstallContextConfigFunctionInstance.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/InstallContextConfigFunctionInstance.java new file mode 100644 index 000000000..e6ffacdd6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/InstallContextConfigFunctionInstance.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * InstallContextConfigFunctionInstance.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/function/InstallContextConfigFunctionInstance.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.function; + +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.*; +import org.opengauss.admin.plugin.domain.model.ops.node.EnterpriseInstallNodeConfig; +import org.opengauss.admin.plugin.domain.model.ops.node.LiteInstallNodeConfig; +import org.opengauss.admin.plugin.domain.model.ops.node.MinimalistInstallNodeConfig; +import org.opengauss.admin.plugin.enums.ops.DatabaseKernelArch; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * InstallContextConfigFunctionInstance + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Service +public class InstallContextConfigFunctionInstance { + private Map configFunctions = new HashMap<>(); + + /** + * 轻量版 LITE install context config function + * cluster mode only support one primary and one standby, need to install on different nodes manually + */ + private final InstallContextConfigFunction lite = (installContext, task, taskNodeList) -> { + Assert.isTrue(taskNodeList.size() >= 1 && taskNodeList.size() <= 2, + "lite cluster mode only support one primary and one standby, " + + "need to install on different nodes manually"); + LiteInstallConfig config = new LiteInstallConfig(); + config.setInstallPackagePath(task.getInstallPackagePath()); + config.setPort(task.getDatabasePort()); + config.setDatabaseUsername(task.getDatabaseUsername()); + config.setDatabasePassword(task.getDatabasePassword()); + List nodeConfigList = new ArrayList<>(); + Map hostInfoHolderMap = installContext.getHostInfoHolderMap(); + for (OpsClusterTaskNodeEntity node : taskNodeList) { + LiteInstallNodeConfig nodeConfig = new LiteInstallNodeConfig(); + nodeConfig.setClusterRole(node.getNodeType()); + nodeConfig.setHostId(node.getHostId()); + nodeConfig.setInstallUserId(node.getHostUserId()); + nodeConfig.setRootPassword(getHostRootPassword(hostInfoHolderMap.get(node.getHostId()))); + nodeConfig.setInstallPath(task.getInstallPath()); + nodeConfig.setDataPath(node.getDataPath()); + nodeConfigList.add(nodeConfig); + } + config.setNodeConfigList(nodeConfigList); + installContext.setLiteInstallConfig(config); + }; + + /** + * 极简版 MINIMAL_LIST install context config function + * cluster mode only support one master and one standby, config will be installed automatically on the same node + */ + private final InstallContextConfigFunction minimal = (installContext, task, taskNodeList) -> { + Assert.isTrue(taskNodeList.size() == 1, + "minimal_list cluster mode only support one master and one standby, " + + "config will be installed automatically on the same node"); + MinimalistInstallConfig config = new MinimalistInstallConfig(); + config.setInstallPackagePath(task.getInstallPackagePath()); + config.setPort(task.getDatabasePort()); + config.setDatabaseUsername(task.getDatabaseUsername()); + config.setDatabasePassword(task.getDatabasePassword()); + List nodeConfigList = new ArrayList<>(); + Map hostInfoHolderMap = installContext.getHostInfoHolderMap(); + for (OpsClusterTaskNodeEntity node : taskNodeList) { + MinimalistInstallNodeConfig nodeConfig = new MinimalistInstallNodeConfig(); + nodeConfig.setHostId(node.getHostId()); + nodeConfig.setInstallUserId(node.getHostUserId()); + nodeConfig.setRootPassword(getHostRootPassword(hostInfoHolderMap.get(node.getHostId()))); + nodeConfig.setInstallPath(task.getInstallPath()); + nodeConfig.setIsInstallDemoDatabase(true); + nodeConfig.setClusterRole(node.getNodeType()); + nodeConfigList.add(nodeConfig); + config.setNodeConfigList(nodeConfigList); + } + installContext.setMinimalistInstallConfig(config); + }; + + /** + * 企业版 ENTERPRISE install context config function + * cluster mode node list size must between 1 and 10 + */ + private final InstallContextConfigFunction enterprise = (installContext, task, taskNodeList) -> { + Assert.isTrue(taskNodeList.size() >= 1 && taskNodeList.size() <= 10, + "enterprise cluster mode node list size must between 1 and 10"); + EnterpriseInstallConfig config = new EnterpriseInstallConfig(); + BeanUtils.copyProperties(task, config); + config.setPort(task.getDatabasePort()); + config.setDatabaseKernelArch(DatabaseKernelArch.MASTER_SLAVE); + config.setIsInstallCM(task.getEnableCmTool()); + + List nodeConfigList = new ArrayList<>(); + Map hostInfoHolderMap = installContext.getHostInfoHolderMap(); + for (OpsClusterTaskNodeEntity node : taskNodeList) { + EnterpriseInstallNodeConfig nodeConfig = new EnterpriseInstallNodeConfig(); + BeanUtils.copyProperties(node, nodeConfig); + // cm tool will be install at tools path + nodeConfig.setCmPort(node.getCmPort()); + nodeConfig.setIsCMMaster(node.getIsCmMaster()); + nodeConfig.setCmDataPath(node.getCmDataPath()); + HostInfoHolder hostInfoHolder = hostInfoHolderMap.get(node.getHostId()); + OpsHostEntity host = hostInfoHolder.getHostEntity(); + nodeConfig.setPublicIp(host.getPublicIp()); + nodeConfig.setPrivateIp(host.getPrivateIp()); + nodeConfig.setAzName(node.getAzOwner()); + nodeConfig.setHostname(host.getHostname()); + nodeConfig.setClusterRole(node.getNodeType()); + Map userMap = hostInfoHolder.getHostUserEntities().stream().collect(Collectors.toMap(OpsHostUserEntity::getHostUserId, Function.identity())); + OpsHostUserEntity nodeUser = userMap.get(node.getHostUserId()); + nodeConfig.setInstallUserId(nodeUser.getHostUserId()); + nodeConfig.setInstallUsername(nodeUser.getUsername()); + nodeConfig.setRootPassword(getHostRootPassword(hostInfoHolder)); + nodeConfigList.add(nodeConfig); + } + config.setNodeConfigList(nodeConfigList); + installContext.setEnterpriseInstallConfig(config); + }; + + /** + * constructor + */ + public InstallContextConfigFunctionInstance() { + configFunctions.put(OpenGaussVersionEnum.LITE, lite); + configFunctions.put(OpenGaussVersionEnum.MINIMAL_LIST, minimal); + configFunctions.put(OpenGaussVersionEnum.ENTERPRISE, enterprise); + } + + /** + * get InstallContextConfig function by OpenGaussVersionEnum + * + * @param version version + * @return check function + */ + public InstallContextConfigFunction getInstallContextConfig(OpenGaussVersionEnum version) { + return configFunctions.get(version); + } + + private String getHostRootPassword(HostInfoHolder hostInfoHolder) { + String hostId = hostInfoHolder.getHostEntity().getHostId(); + return hostInfoHolder.getHostUserEntities() + .stream() + .filter(user -> user.getUsername().equals("root")) + .findAny().orElseThrow(() -> new OpsException("hostId:" + hostId + "not found root user")) + .getPassword(); + } +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/AbstractOpsProvider.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/AbstractOpsProvider.java new file mode 100644 index 000000000..5afce4705 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/AbstractOpsProvider.java @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AbstractOpsProvider.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/AbstractOpsProvider.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.provider; + +import cn.hutool.core.util.StrUtil; + +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.jcraft.jsch.Session; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.model.ops.*; +import org.opengauss.admin.plugin.domain.model.ops.node.EnterpriseInstallNodeConfig; +import org.opengauss.admin.plugin.enums.ops.DatabaseKernelArch; +import org.opengauss.admin.plugin.enums.ops.GucSettingContextEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussSupportOSEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.service.ops.ClusterOpsProvider; +import org.opengauss.admin.plugin.service.ops.impl.ClusterOpsProviderManager; +import org.opengauss.admin.plugin.service.ops.impl.OpsClusterEnvService; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.plugin.vo.ops.GucSettingVO; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import jakarta.annotation.Resource; + +/** + * @author lhf + * @date 2022/8/12 09:20 + **/ +@Slf4j +public abstract class AbstractOpsProvider implements ClusterOpsProvider, InitializingBean { + /** + * encryption utils + */ + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + protected EncryptionUtils encryptionUtils; + + @Autowired + private OpsClusterEnvService opsClusterEnvService; + + /** + * Prepares a session before installation. + * + * @param jschUtil The JSch utility for SSH connections. + * @param encryptionUtils The utility for encryption and decryption. + * @param installContext The context containing installation details. + * @param installPath The path where the installation will occur. + * @param dataPath The path where data will be stored. + * @param pkgPath The path to the package to be installed. + * @param hostId The ID of the host where the installation will occur. + * @param installUserId The ID of the user performing the installation. + * @param installUserName The name of the user performing the installation. + * @param decompressArgs Arguments for decompressing the package. + * @return A session object representing the SSH session. + */ + protected Session beforeInstall(JschUtil jschUtil, EncryptionUtils encryptionUtils, InstallContext installContext, + String installPath, String dataPath, String pkgPath, String hostId, String installUserId, + String installUserName, String decompressArgs) { + WsSession retSession = installContext.getRetSession(); + log.info("The root user logs in to the host"); + Session rootSession = null; + try { + rootSession = getRootOrInstallUserSession(jschUtil, encryptionUtils, installContext, hostId, + installUserId); + installDependency(jschUtil, rootSession, retSession, installContext.getOs()); + ensureDirExist(jschUtil, rootSession, pkgPath, retSession); + ensureDirExist(jschUtil, rootSession, installPath, retSession); + ensureDirExist(jschUtil, rootSession, dataPath, retSession); + ensureLimits(jschUtil, rootSession, retSession); + wsRetSendText(retSession, "START_SCP_INSTALL_PACKAGE"); + log.info("Copy the installation package to the target host"); + String installPackageFullPath = scpInstallPackageToMasterNode(jschUtil, rootSession, + installContext.getInstallPackagePath(), pkgPath, retSession); + wsRetSendText(retSession, "END_SCP_INSTALL_PACKAGE"); + // SEM + log.info("set kernel.sem"); + sem(jschUtil, rootSession, retSession); + // unzip + log.info("Unzip the installation package"); + wsRetSendText(retSession, "START_UNZIP_INSTALL_PACKAGE"); + decompress(jschUtil, rootSession, installPath, installPackageFullPath, retSession, decompressArgs); + wsRetSendText(retSession, "END_UNZIP_INSTALL_PACKAGE"); + ensureStrictPermission(jschUtil, rootSession, installUserName, pkgPath, retSession); + ensureStrictPermission(jschUtil, rootSession, installUserName, installPath, retSession); + ensureDataPathPermission(jschUtil, rootSession, installUserName, dataPath, retSession); + ensureEnvPathPermission(jschUtil, rootSession, installContext.getEnvPath(), retSession); + log.info("Login and install user"); + } finally { + closeSession(rootSession); + } + return loginWithUser(jschUtil, encryptionUtils, installContext.getHostInfoHolders(), false, hostId, + installUserId); + } + + private Session getRootOrInstallUserSession(JschUtil jschUtil, EncryptionUtils encryptionUtils, + InstallContext installContext, String hostId, String installUserId) { + Session rootSession = null; + try { + rootSession = loginWithUser(jschUtil, encryptionUtils, installContext.getHostInfoHolders(), true, hostId, + null); + if (Objects.isNull(rootSession)) { + rootSession = loginWithUser(jschUtil, encryptionUtils, installContext.getHostInfoHolders(), false, + hostId, installUserId); + } + } catch (OpsException ex) { + log.warn("Failed to login with root user, try to login with install user: {}", ex.getMessage()); + } + return rootSession; + } + + private void closeSession(Session session) { + if (Objects.nonNull(session) && session.isConnected()) { + session.disconnect(); + } + } + + private void wsRetSendText(WsSession retSession, String message) { + try { + retSession.getSession().getBasicRemote().sendText(message); + } catch (IOException e) { + log.error("send websocket text fail", e); + } + } + + protected String wrapperEnvSep(String command, String envPath) { + if (StrUtil.isNotEmpty(envPath)) { + command = command + " --sep-env-file=" + envPath; + } + return command; + } + + protected String wrapperLiteEnvSep(String command, String envPath) { + if (StrUtil.isNotEmpty(envPath)) { + command = command + " --env-sep-file " + envPath; + } + return command; + } + + protected void ensureEnvPathPermission(JschUtil jschUtil, Session rootSession, String envPath, WsSession retSession) { + if (StrUtil.isEmpty(envPath)) { + return; + } + + String touchEnvSep = "touch \"" + envPath + "\""; + + try { + jschUtil.executeCommand(touchEnvSep, rootSession, retSession); + } catch (Exception e) { + log.error("touch envFile fail", e); + throw new OpsException("touch envFile fail"); + } + + String permissionEnv = "chmod 755 -R " + envPath; + try { + jschUtil.executeCommand(permissionEnv, rootSession, retSession); + } catch (Exception e) { + log.error("chmod envFile fail", e); + throw new OpsException("chmod envFile fail"); + } + } + + protected void installDependency(JschUtil jschUtil, Session rootSession, WsSession retSession, + OpenGaussSupportOSEnum expectedOs) { + List dependencyPackageNames = List.of("libaio-devel", "flex", "bison", "ncurses-devel", "glibc-devel", + "patch", "readline-devel", "coreutils", "procps-ng", "openssh-clients", "unzip", "lsof", "grep", "tar"); + String queryCommand = "yum list installed | egrep 'libaio-devel|flex|bison|ncurses-devel|glibc-devel|patch|" + + "redhat-lsb-core|readline-devel|lsscsi|coreutils|procps-ng|openssh-clients|unzip|lsof|grep|tar'"; + if (opsClusterEnvService.getMissingList(rootSession, expectedOs.getCpuArch(), queryCommand, + dependencyPackageNames).isEmpty()) { + return; + } + try { + retSession.getSession().getBasicRemote().sendText("START_INSTALL_DEPENDENCY"); + } catch (IOException e) { + log.error("send websocket fail", e); + } + boolean errorFlag = false; + String command = dependencyCommand(expectedOs); + try { + JschResult jschResult = null; + try { + jschResult = jschUtil.executeCommand(command, rootSession); + } catch (InterruptedException e) { + throw new OpsException("thread is interrupted"); + } + if (0 != jschResult.getExitCode()) { + log.error("install depencency fail, exit code: {}, error message: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("install depencency fail"); + } + + } catch (OpsException | IOException e) { + log.error("install depencency fail", e); + errorFlag = true; + } + + try { + if (errorFlag) { + retSession.getSession().getBasicRemote().sendText("error occurred, skip install dependency"); + } + retSession.getSession().getBasicRemote().sendText("END_INSTALL_DEPENDENCY"); + } catch (IOException e) { + log.error("send websocket fail", e); + } + } + + protected void ensureLimits(JschUtil jschUtil, Session rootSession, WsSession retSession) { + String limitsCheck = SshCommandConstants.LIMITS_CHECK; + try { + JschResult jschResult = jschUtil.executeCommand(limitsCheck, rootSession, retSession); + + if (0 != jschResult.getExitCode()) { + log.error("Detect ulimit exception, exit code: {}, error message: {}", jschResult.getExitCode(), jschResult.getResult()); + } + + if (StrUtil.isNotEmpty(jschResult.getResult())) { + return; + } + } catch (Exception e) { + log.error("Detect ulimit error", e); + } + + String limits = SshCommandConstants.LIMITS; + try { + jschUtil.executeCommand(limits, rootSession, retSession); + } catch (IOException | InterruptedException e) { + log.error("set ulimit exception", e); + throw new OpsException("set ulimit exception"); + } + } + + protected String scpInstallPackageToMasterNode(JschUtil jschUtil, Session rootSession, String sourcePath, String targetPath, WsSession retSession) { + String installPackageFileName = sourcePath.substring(sourcePath.lastIndexOf("/") + 1); + String installPackageFullPath = targetPath + "/" + installPackageFileName; + jschUtil.upload(rootSession, retSession, sourcePath, installPackageFullPath); + return installPackageFullPath; + } + + protected void sem(JschUtil jschUtil, Session rootSession, WsSession retSession) { + String command = SshCommandConstants.SEM; + try { + jschUtil.executeCommand(command, rootSession, retSession); + } catch (IOException | InterruptedException | OpsException e) { + log.error("set kernel.sem exception", e); + } + } + + protected void decompress(JschUtil jschUtil, Session rootSession, String targetPath, String installPackageFullPath, WsSession retSession, String decompressArgs) { + String command = MessageFormat.format(SshCommandConstants.DECOMPRESS, decompressArgs, installPackageFullPath, targetPath); + try { + jschUtil.executeCommand(command, rootSession, retSession); + } catch (Exception e) { + log.error("Unzip the installation package failed:", e); + throw new OpsException("Unzip the installation package failed"); + } + } + + protected Session loginWithUser(JschUtil jschUtil, EncryptionUtils encryptionUtils, List hostInfoHolders, boolean root, String hostId, String userId) { + HostInfoHolder hostInfoHolder = hostInfoHolders.stream().filter(host -> host.getHostEntity().getHostId().equals(hostId)).findFirst().orElseThrow(() -> new OpsException("host information not found")); + OpsHostEntity hostEntity = hostInfoHolder.getHostEntity(); + + if (root) { + userId = hostInfoHolder + .getHostUserEntities() + .stream().filter(hostUser -> "root".equals(hostUser.getUsername())) + .findFirst() + .orElseThrow(() -> new OpsException("root user information not found")).getHostUserId(); + } + + OpsHostUserEntity userEntity = null; + for (OpsHostUserEntity hostUserEntity : hostInfoHolder.getHostUserEntities()) { + if (hostUserEntity.getHostUserId().equals(userId)) { + userEntity = hostUserEntity; + break; + } + } + + if (Objects.isNull(userEntity)) { + throw new OpsException("No installation user information found"); + } + + return sshLogin(jschUtil, encryptionUtils, hostEntity, userEntity); + } + + protected Session sshLogin(JschUtil jschUtil, EncryptionUtils encryptionUtils, OpsHostEntity hostEntity, OpsHostUserEntity userEntity) { + return jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), userEntity.getUsername(), encryptionUtils.decrypt(userEntity.getPassword())) + .orElseThrow(() -> new OpsException("Session establishment exception with host[" + hostEntity.getPublicIp() + "]")); + } + + protected void chmodFullPath(JschUtil jschUtil, Session rootSession, String path, WsSession wsSession) { + String chmod = MessageFormat.format(SshCommandConstants.CHMOD, path); + + try { + try { + jschUtil.executeCommand(chmod, rootSession, wsSession); + } catch (InterruptedException e) { + throw new OpsException("thread is interrupted"); + } + } catch (IOException e) { + log.error("Failed to grant permission", e); + } + } + + protected void chmod(JschUtil jschUtil, Session rootSession, String path, WsSession wsSession) { + if (StrUtil.isNotEmpty(path) && path.indexOf("/", 1) > 0) { + path = path.substring(0, path.indexOf("/", 1)); + } + String chmod = MessageFormat.format(SshCommandConstants.CHMOD, path); + + try { + try { + jschUtil.executeCommand(chmod, rootSession, wsSession); + } catch (InterruptedException e) { + throw new OpsException("thread is interrupted"); + } + } catch (IOException e) { + log.error("Failed to grant permission", e); + } + } + + protected void chmodDataPath(JschUtil jschUtil, Session rootSession, String path, WsSession wsSession) { + String chmod = MessageFormat.format(SshCommandConstants.CHMOD_DATA_PATH, path); + + try { + try { + jschUtil.executeCommand(chmod, rootSession, wsSession); + } catch (InterruptedException e) { + throw new OpsException("thread is interrupted"); + } + } catch (IOException e) { + log.error("Failed to grant permission", e); + } + } + + protected void ensurePermission(JschUtil jschUtil, Session rootSession, String installUserName, String targetPath, WsSession wsSession) { + chmod(jschUtil, rootSession, targetPath, wsSession); + + String chown = MessageFormat.format(SshCommandConstants.CHOWN, installUserName, targetPath); + + try { + jschUtil.executeCommand(chown, rootSession, wsSession); + } catch (IOException | InterruptedException | OpsException e) { + log.error("Failed to grant permission", e); + throw new OpsException("Failed to grant permission"); + } + } + + protected void ensureStrictPermission(JschUtil jschUtil, Session rootSession, String installUserName, String targetPath, WsSession wsSession) { + chmodFullPath(jschUtil, rootSession, targetPath, wsSession); + + String chown = MessageFormat.format(SshCommandConstants.CHOWN, installUserName, targetPath); + + try { + jschUtil.executeCommand(chown, rootSession, wsSession); + } catch (IOException | InterruptedException | OpsException e) { + log.error("Failed to grant permission", e); + throw new OpsException("Failed to grant permission"); + } + } + + protected void ensureDataPathPermission(JschUtil jschUtil, Session rootSession, String installUserName, String targetPath, WsSession wsSession) { + chmodDataPath(jschUtil, rootSession, targetPath, wsSession); + + String chown = MessageFormat.format(SshCommandConstants.CHOWN, installUserName, targetPath); + + try { + jschUtil.executeCommand(chown, rootSession, wsSession); + } catch (IOException | InterruptedException | OpsException e) { + log.error("Failed to grant permission", e); + throw new OpsException("Failed to grant permission"); + } + } + + protected void ensureDirExist(JschUtil jschUtil, Session rootSession, String targetPath, WsSession retSession) { + String command = MessageFormat.format(SshCommandConstants.MK_DIR, targetPath); + try { + jschUtil.executeCommand(command, rootSession, retSession); + } catch (IOException | InterruptedException | OpsException e) { + log.error("Failed to create installation directory:", e); + throw new OpsException("Failed to create installation directory"); + } + } + + protected String preparePath(String path) { + if (StrUtil.isEmpty(path) || path.endsWith("/")) { + return path; + } + + return path + "/"; + } + + protected OmStatusModel omStatus(JschUtil jschUtil, Session ommUserSession, WsSession retSession, String envPath) { + OmStatusModel omStatusModel = new OmStatusModel(); + try { + String statusCommand = "gs_om -t status --detail"; + JschResult jschResult = jschUtil.executeCommand(statusCommand, ommUserSession, envPath); + if (0 != jschResult.getExitCode()) { + throw new OpsException("startup error,gs_om status fail,exit code " + jschResult.getExitCode()); + } + + String result = jschResult.getResult(); + Map nodeIdMapHostname = new HashMap<>(); + Map hostnameMapNodeId = new HashMap<>(); + omStatusModel.setInstallCm(result.contains("[ CMServer State ]")); + omStatusModel.setNodeIdMapHostname(nodeIdMapHostname); + omStatusModel.setHostnameMapNodeId(hostnameMapNodeId); + + if (omStatusModel.isInstallCm()) { + int datanodeStateIndex = result.indexOf("[ CMServer State ]"); + if (datanodeStateIndex < 0) { + + } else { + int splitIndex = result.indexOf("------------------", datanodeStateIndex); + String dataNodeStateStr = result.substring(splitIndex); + String[] dataNode = dataNodeStateStr.split("\n"); + for (String s : dataNode) { + if (StrUtil.isEmpty(s)) { + break; + } + String[] s1 = s.replaceAll(" +", " ").split(" "); + if (s1.length > 2) { + nodeIdMapHostname.put(s1[0], s1[1]); + hostnameMapNodeId.put(s1[1], s1[0]); + } + } + } + } + } catch (Exception e) { + log.error("An exception occurred during startup,gs_om status fail", e); + throw new OpsException("An exception occurred during startup,gs_om status fail"); + } + + return omStatusModel; + } + + protected void createEnterpriseRemoteUser(InstallContext installContext, OpsClusterContext opsClusterContext, JschUtil jschUtil, EncryptionUtils encryptionUtils) { + WsSession retSession = installContext.getRetSession(); + opsClusterContext.setRetSession(retSession); + opsClusterContext.setHostInfoHolders(installContext.getHostInfoHolders()); + + EnterpriseInstallConfig enterpriseInstallConfig = installContext.getEnterpriseInstallConfig(); + List nodeConfigList = enterpriseInstallConfig.getNodeConfigList(); + String databasePassword = enterpriseInstallConfig.getDatabasePassword(); + Integer port = enterpriseInstallConfig.getPort(); + + for (EnterpriseInstallNodeConfig enterpriseInstallNodeConfig : nodeConfigList) { + String hostId = enterpriseInstallNodeConfig.getHostId(); + String installUserId = enterpriseInstallNodeConfig.getInstallUserId(); + String dataPath = enterpriseInstallNodeConfig.getDataPath(); + + + Session session = loginWithUser(jschUtil, encryptionUtils, installContext.getHostInfoHolders(), false, hostId, installUserId); + String listenerAddress = MessageFormat.format(SshCommandConstants.LISTENER, dataPath); + try { + jschUtil.executeCommand(listenerAddress, installContext.getEnvPath(), session, retSession); + } catch (Exception e) { + log.error("Failed to modify listening address", e); + throw new OpsException("Failed to modify listening address"); + } + + String hba = MessageFormat.format(SshCommandConstants.HBA, dataPath); + try { + jschUtil.executeCommand(hba, installContext.getEnvPath(), session, retSession); + + } catch (Exception e) { + log.error("Failed to modify host", e); + throw new OpsException("Failed to modify host"); + } + + String clientLoginOpenGauss = MessageFormat.format(SshCommandConstants.LOGIN, String.valueOf(port)); + try { + Map response = new HashMap<>(); + String createUser = MessageFormat.format( + "CREATE USER gaussdb WITH MONADMIN AUDITADMIN SYSADMIN PASSWORD \"{0}\";\\q", + encryptionUtils.decrypt(databasePassword)); + response.put("openGauss=#", createUser); + if (enterpriseInstallConfig.getDatabaseKernelArch().equals(DatabaseKernelArch.MASTER_SLAVE) + || enterpriseInstallNodeConfig.getIsCMMaster()) { + jschUtil.executeCommand(installContext.getEnvPath(), clientLoginOpenGauss, session, retSession, response); + } + + } catch (Exception e) { + log.error("Failed to create user", e); + throw new OpsException("Failed to create user"); + } + } + + opsClusterContext.setHostInfoHolders(installContext.getHostInfoHolders()); + restart(opsClusterContext); + } + + @Override + public void configGucSetting(JschUtil jschUtil, Session session, OpsClusterEntity clusterEntity, boolean isApplyToAllNode, String dataPath, GucSettingVO gucSettingVO) { + String command; + if (gucSettingVO.getContext().equals(GucSettingContextEnum.POSTMASTER.getCode())) { + if (isApplyToAllNode && clusterEntity.getVersion().equals(OpenGaussVersionEnum.ENTERPRISE)) { + command = String.format("gs_guc set -N all -I all -c \"%s=\'%s\'\"", gucSettingVO.getName(), + gucSettingVO.getValue()); + } else { + command = String.format("gs_guc set -D %s -c \"%s=\'%s\'\"", dataPath, gucSettingVO.getName(), + gucSettingVO.getValue()); + } + } else { + if (isApplyToAllNode && clusterEntity.getVersion().equals(OpenGaussVersionEnum.ENTERPRISE)) { + command = String.format("gs_guc reload -N all -I all -c \"%s=\'%s\'\"", gucSettingVO.getName(), + gucSettingVO.getValue()); + } else { + command = String.format("gs_guc reload -D %s -c \"%s=\'%s\'\"", dataPath, gucSettingVO.getName(), + gucSettingVO.getValue()); + } + } + + try { + JschResult jschResult = jschUtil.executeCommand(command, session, clusterEntity.getEnvPath()); + if (0 != jschResult.getExitCode()) { + log.error("set guc parameter {} to {} failed, exit code: {}, error message: {}", gucSettingVO.getName(), gucSettingVO.getValue(), jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException(String.format("Failed to set guc parameter %s to %s failed: %s", gucSettingVO.getName(), gucSettingVO.getValue(), jschResult.getResult())); + } + + } catch (Exception e) { + log.error("set guc parameter {} to {} failed, error message: {}", gucSettingVO.getName(), gucSettingVO.getValue(), e.getMessage()); + throw new OpsException(String.format("Failed to set guc parameter %s to %s failed: %s", gucSettingVO.getName(), gucSettingVO.getValue(), e.getMessage())); + } + } + + @Override + public void afterPropertiesSet() { + ClusterOpsProviderManager.registry(version(), this); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/AbstractTaskProvider.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/AbstractTaskProvider.java new file mode 100644 index 000000000..3ae11d75b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/AbstractTaskProvider.java @@ -0,0 +1,768 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AbstractTaskProvider.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/AbstractTaskProvider.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.provider; + +import cn.hutool.core.util.StrUtil; + +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.jcraft.jsch.Session; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.model.ops.*; +import org.opengauss.admin.plugin.domain.model.ops.node.EnterpriseInstallNodeConfig; +import org.opengauss.admin.plugin.enums.ops.DatabaseKernelArch; +import org.opengauss.admin.plugin.enums.ops.OpenGaussSupportOSEnum; +import org.opengauss.admin.plugin.service.ops.ClusterTaskProvider; +import org.opengauss.admin.plugin.service.ops.impl.ClusterTaskProviderManager; +import org.opengauss.admin.plugin.service.ops.impl.OpsHostRemoteService; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.beans.factory.InitializingBean; + +import jakarta.annotation.Resource; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.MessageFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + * AbstractTaskProvider + * + * @author wangchao + * @since 2024/06/15 09:26 + */ +@Slf4j +public abstract class AbstractTaskProvider implements ClusterTaskProvider, InitializingBean { + @Resource + protected OpsHostRemoteService opsHostRemoteService; + + /** + * encryption utils + */ + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + protected EncryptionUtils encryptionUtils; + + /** + * before install config + * + * @param installContext install context + * @param installPath install path + * @param dataPath data path + * @param pkgPath pkg path + * @param hostId host id + * @param installUserId install user id + * @param installUserName install user name + * @param decompressArgs decompress args + * @return session + */ + protected Session beforeInstall(InstallContext installContext, String installPath, String dataPath, String pkgPath, + String hostId, String installUserId, String installUserName, String decompressArgs) { + RetBuffer retBuffer = installContext.getRetBuffer(); + + log.info("The root user logs in to the host"); + // root session + HostInfoHolder hostInfoHolder = getHostInfoHolder(installContext, hostId); + Session rootSession = createSessionWithRootUser(hostInfoHolder); + try { + installDependency(rootSession, retBuffer, installContext.getOs()); + ensureDirExist(rootSession, pkgPath, retBuffer); + ensureDirExist(rootSession, installPath, retBuffer); + ensureDirExist(rootSession, dataPath, retBuffer); + ensureLimits(rootSession, retBuffer); + retBuffer.sendText("START_SCP_INSTALL_PACKAGE"); + + log.info("Copy the installation package to the target host"); + // scp + String installPackageFullPath = scpInstallPackageToMasterNode(rootSession, + installContext.getInstallPackageLocalPath(), pkgPath, retBuffer); + retBuffer.sendText("END_SCP_INSTALL_PACKAGE"); + + log.info("set kernel.sem"); + // SEM + sem(rootSession, retBuffer); + + log.info("Unzip the installation package"); + // unzip + retBuffer.sendText("START_UNZIP_INSTALL_PACKAGE"); + decompress(rootSession, installPath, installPackageFullPath, retBuffer, decompressArgs); + retBuffer.sendText("END_UNZIP_INSTALL_PACKAGE"); + + ensureStrictPermission(rootSession, installUserName, pkgPath, retBuffer); + ensureStrictPermission(rootSession, installUserName, installPath, retBuffer); + ensureLevel2DirPermission(rootSession, installUserName, installPath, retBuffer); + chmodFullPathAndParentPath(rootSession, installPath, retBuffer); + ensureDataPathPermission(rootSession, installUserName, dataPath, retBuffer); + chmodFullPathAndParentPath(rootSession, dataPath, retBuffer); + ensureEnvPathPermission(rootSession, installContext.getEnvPath(), retBuffer); + log.info("Login and install user"); + } finally { + if (Objects.nonNull(rootSession) && rootSession.isConnected()) { + rootSession.disconnect(); + } + } + return createSessionWithUserId(installContext.getHostInfoHolders(), false, hostId, installUserId); + } + + /** + * ensure dir two lever permission + * chown user group permission && chmod path permission + * + * @param rootSession session + * @param installUserName user name + * @param targetPath path + * @param retBuffer buffer + */ + protected void ensureLevel2DirPermission(Session rootSession, String installUserName, String targetPath, + RetBuffer retBuffer) { + String targetParent2LevelPath = getLevel2DirPath(targetPath); + String chown = MessageFormat.format(SshCommandConstants.CHOWN_USER_GROUP, installUserName, + installUserName, targetParent2LevelPath); + opsHostRemoteService.executeCommand(chown, rootSession, retBuffer, "chown user group permission"); + String chmod = MessageFormat.format(SshCommandConstants.CHMOD, targetParent2LevelPath); + opsHostRemoteService.executeCommand(chmod, rootSession, retBuffer, "chmod path permission"); + } + + /** + * get path top two path + * + * @param targetPath targetPath + * @return path + */ + protected String getLevel2DirPath(String targetPath) { + String[] split = targetPath.split("/"); + if (split.length >= 3) { + return "/" + split[1] + "/" + split[2]; + } + return targetPath; + } + + /** + * Enterpres Version env sep file param name : --sep-env-file + * + * @param command command + * @param envPath envPath + * @return String + */ + protected String wrapperEnvSep(String command, String envPath) { + if (isNotSepEnv(envPath)) { + return command; + } + return command + " --sep-env-file=" + envPath; + } + + /** + * Lite Version env sep file param name : --env-sep-file + * + * @param command command + * @param envPath envPath + * @return String + */ + protected String wrapperLiteEnvSep(String command, String envPath) { + if (isNotSepEnv(envPath)) { + return command; + } + return command + " --env-sep-file " + envPath; + } + + /** + * Ensure env file permission + * + * @param rootSession rootSession + * @param envPath envPath + * @param retSession retSession + */ + protected void ensureEnvPathPermission(Session rootSession, String envPath, RetBuffer retSession) { + if (isNotSepEnv(envPath)) { + return; + } + String touchEnvSep = "touch \"" + envPath + "\""; + opsHostRemoteService.executeCommand(touchEnvSep, rootSession, retSession, "touch envFile"); + String permissionEnv = "chmod 755 -R " + envPath; + opsHostRemoteService.executeCommand(permissionEnv, rootSession, retSession, "chmod envFile"); + } + + protected boolean isNotSepEnv(String envPath) { + return StrUtil.isEmpty(envPath) || SshCommandConstants.DEFAULT_ENV_BASHRC.equalsIgnoreCase(envPath); + } + + /** + * Install dependency + * + * @param rootSession rootSession + * @param retBuffer retBuffer + * @param expectedOs expectedOs + */ + protected void installDependency(Session rootSession, RetBuffer retBuffer, + OpenGaussSupportOSEnum expectedOs) { + boolean dependencyCorrect = false; + String[] dependencyPackageNames = {"libaio-devel", "flex", "bison", "ncurses-devel", "glibc-devel", + "patch", "readline-devel"}; + try { + List dependencyPackages = Arrays.stream(dependencyPackageNames).map( + dependency -> dependency + "." + expectedOs.getCpuArch()).collect(Collectors.toList()); + String dependency = opsHostRemoteService.executeCommand(SshCommandConstants.DEPENDENCY, rootSession, retBuffer); + List notInstalledPackages = new ArrayList<>(); + for (String dependencyPackage : dependencyPackages) { + if (!dependency.contains(dependencyPackage)) { + notInstalledPackages.add(dependencyPackage); + } + } + if (notInstalledPackages.isEmpty()) { + dependencyCorrect = true; + } + } catch (Exception e) { + log.error("Execute dependencyPackageNames command exception:", e); + } + if (dependencyCorrect) { + log.info("dependencyCorrect,skip install dependency"); + return; + } + retBuffer.sendText("START_INSTALL_DEPENDENCY"); + boolean errorFlag = false; + try { + String command = dependencyCommand(expectedOs); + opsHostRemoteService.executeCommand(command, rootSession, retBuffer, "install depencency"); + } catch (OpsException e) { + errorFlag = true; + } + + if (errorFlag) { + retBuffer.sendText("error occurred, skip install dependency"); + } + retBuffer.sendText("END_INSTALL_DEPENDENCY"); + } + + /** + * check and reset linux os limits config + * + * @param rootSession rootSession + * @param retBuffer retBuffer + */ + protected void ensureLimits(Session rootSession, RetBuffer retBuffer) { + String limitsCheck = SshCommandConstants.LIMITS_CHECK; + String limitsCheckRes = opsHostRemoteService.executeCommandThenReturnEmpty(limitsCheck, rootSession, + retBuffer, "Detect ulimit"); + if (StrUtil.isNotEmpty(limitsCheckRes)) { + return; + } + String limits = SshCommandConstants.LIMITS; + opsHostRemoteService.executeCommand(limits, rootSession, retBuffer, "set ulimit"); + } + + /** + * scp install package to target node dir + * + * @param rootSession rootSession + * @param sourcePath sourcePath + * @param targetPath targetPath + * @param retSession retSession + * @return String + */ + protected String scpInstallPackageToMasterNode(Session rootSession, String sourcePath, String targetPath, + RetBuffer retSession) { + String installPackageFileName = sourcePath.substring(sourcePath.lastIndexOf("/") + 1); + String installPackageFullPath = targetPath + installPackageFileName; + if (!Files.exists(Paths.get(sourcePath))) { + throw new OpsException("Install package not exist " + sourcePath); + } + opsHostRemoteService.executeUpload(rootSession, retSession, sourcePath, installPackageFullPath); + return installPackageFullPath; + } + + /** + * set kernel.sem config + * + * @param rootSession rootSession + * @param retBuffer retBuffer + */ + protected void sem(Session rootSession, RetBuffer retBuffer) { + String kernelSemValue = opsHostRemoteService.executeCommand(SshCommandConstants.CHECK_SEM, rootSession, + retBuffer, "check kernel.sem"); + if (!SshCommandConstants.SEM_VALUE.equals(kernelSemValue)) { + opsHostRemoteService.executeCommand(SshCommandConstants.SEM, rootSession, retBuffer, "set kernel.sem"); + } + } + + /** + * decompress target install package + * + * @param rootSession rootSession + * @param targetPath targetPath + * @param installPackageFullPath installPackageFullPath + * @param retBuffer retBuffer + * @param decompressArgs decompressArgs + */ + protected void decompress(Session rootSession, String targetPath, String installPackageFullPath, + RetBuffer retBuffer, String decompressArgs) { + String command = MessageFormat.format(SshCommandConstants.DECOMPRESS, decompressArgs, + installPackageFullPath, targetPath); + opsHostRemoteService.executeCommand(command, rootSession, retBuffer, "Unzip the installation package"); + } + + /** + * create session with hostid and userId + * + * @param installContext installContext + * @param root is root user + * @param hostId hostId + * @param userId userId + * @return Session + */ + protected Session createSessionWithUserId(InstallContext installContext, boolean root, + String hostId, String userId) { + HostInfoHolder hostInfoHolder = getHostInfoHolder(installContext, hostId); + return createSessionWithUserId(hostInfoHolder, root, userId); + } + + /** + * create session with root userId + * + * @param hostInfoHolder hostInfoHolder + * @param root is root user + * @param userId userId + * @return Session + */ + protected Session createSessionWithUserId(HostInfoHolder hostInfoHolder, boolean root, String userId) { + if (root) { + return createSessionWithRootUser(hostInfoHolder); + } + OpsHostEntity hostEntity = hostInfoHolder.getHostEntity(); + OpsHostUserEntity hostUser = getHostUserInfo(hostInfoHolder, userId); + return opsHostRemoteService.createHostUserSession(hostEntity, hostUser); + } + + /** + * create session with root userId + * + * @param hostInfoHolders hostInfoHolder list + * @param root is root user + * @param hostId hostId + * @param installUserId userId + * @return Session + */ + protected Session createSessionWithUserId(List hostInfoHolders, boolean root, + String hostId, String installUserId) { + HostInfoHolder hostInfoHolder = getHostInfoHolder(hostInfoHolders, hostId); + return createSessionWithUserId(hostInfoHolder, root, installUserId); + } + + /** + * create root user session with hostInfoHolder + * + * @param hostInfoHolder hostInfoHolder + * @return Session + */ + protected Session createSessionWithRootUser(HostInfoHolder hostInfoHolder) { + OpsHostUserEntity hostUser = getHostUserInfoByUsername(hostInfoHolder, "root"); + if (Objects.isNull(hostUser) || StrUtil.isEmpty(hostUser.getPassword())) { + throw new OpsException("root password not found"); + } + return opsHostRemoteService.createHostUserSession(hostInfoHolder.getHostEntity(), hostUser); + } + + /** + * chmod full path + * + * @param rootSession root session + * @param path path + * @param retBuffer retBuffer + */ + protected void chmodFullPath(Session rootSession, String path, RetBuffer retBuffer) { + String chmod = MessageFormat.format(SshCommandConstants.CHMOD, path); + opsHostRemoteService.executeCommand(chmod, rootSession, retBuffer, "grant permission"); + } + + /** + * ensure dir user group permission + * + * @param rootSession rootSession + * @param installUserName installUserName + * @param targetPath targetPath + * @param retBuffer retBuffer + */ + protected void ensureDirUserGroupPermission(Session rootSession, String installUserName, + String targetPath, RetBuffer retBuffer) { + chmodFullPathAndParentPath(rootSession, targetPath, retBuffer); + String chown = MessageFormat.format(SshCommandConstants.CHOWN_USER_GROUP, installUserName, + installUserName, targetPath); + opsHostRemoteService.executeCommand(chown, rootSession, retBuffer, "grant permission"); + } + + /** + * chmod full path with parent path permission + * + * @param rootSession rootSession + * @param path path + * @param retBuffer retBuffer + */ + protected void chmodFullPathAndParentPath(Session rootSession, String path, RetBuffer retBuffer) { + int parentPermission = getPathPermission(rootSession, path, retBuffer); + if (parentPermission < 755) { + chmodFullPath(rootSession, path, retBuffer); + } + String parentPath = getParentPath(path); + if (StrUtil.isNotEmpty(parentPath) && !parentPath.equals("/")) { + chmodFullPathAndParentPath(rootSession, parentPath, retBuffer); + } + } + + private int getPathPermission(Session rootSession, String path, RetBuffer wsSession) { + String checkPermissionCommand = MessageFormat.format(SshCommandConstants.CHECK_PERMISSION, path); + int parentPermission = 41; + try { + String result = opsHostRemoteService.executeCommand(checkPermissionCommand, rootSession, wsSession); + String[] lines = result.split("\n"); + if (lines.length > 0) { + parentPermission = convertPermission(lines[0].substring(0, 10)); + } + } catch (Exception e) { + log.error("Failed to grant permission", e); + } + return parentPermission; + } + + private int convertPermission(String permissionString) { + if (permissionString == null || permissionString.length() != 10) { + throw new IllegalArgumentException("Invalid permission string"); + } + int owner = convertOwnerPermission(permissionString); + int group = convertGroupPermission(permissionString); + int others = convertOtherPermission(permissionString); + return Integer.valueOf(owner + String.valueOf(group) + others); + } + + private int convertOtherPermission(String permissionString) { + int others = 0; + if (permissionString.charAt(7) == 'r') { + others += 4; + } + if (permissionString.charAt(8) == 'w') { + others += 2; + } + if (permissionString.charAt(9) == 'x') { + others += 1; + } + return others; + } + + private int convertGroupPermission(String permissionString) { + int group = 0; + if (permissionString.charAt(4) == 'r') { + group += 4; + } + if (permissionString.charAt(5) == 'w') { + group += 2; + } + if (permissionString.charAt(6) == 'x') { + group += 1; + } + return group; + } + + private int convertOwnerPermission(String permissionString) { + int owner = 0; + if (permissionString.charAt(1) == 'r') { + owner += 4; + } + if (permissionString.charAt(2) == 'w') { + owner += 2; + } + if (permissionString.charAt(3) == 'x') { + owner += 1; + } + return owner; + } + + private String getParentPath(String dir) { + String[] split = dir.split("/"); + int length = split.length; + if (length > 1) { + String[] subString = new String[length - 1]; + System.arraycopy(split, 0, subString, 0, length - 1); + return String.join("/", subString); + } + return ""; + } + + private void chmodDataPath(Session rootSession, String path, RetBuffer wsSession) { + String chmod = MessageFormat.format(SshCommandConstants.CHMOD_DATA_PATH, path); + opsHostRemoteService.executeCommand(chmod, rootSession, wsSession, "grant permission"); + } + + private void ensureStrictPermission(Session rootSession, String installUserName, String targetPath, + RetBuffer wsSession) { + chmodFullPath(rootSession, targetPath, wsSession); + String chown = MessageFormat.format(SshCommandConstants.CHOWN_USER_GROUP, installUserName, + installUserName, targetPath); + opsHostRemoteService.executeCommand(chown, rootSession, wsSession, "grant permission"); + } + + private void ensureDataPathPermission(Session rootSession, String installUserName, + String targetPath, RetBuffer wsSession) { + chmodDataPath(rootSession, targetPath, wsSession); + String chown = MessageFormat.format(SshCommandConstants.CHOWN_USER_GROUP, installUserName, + installUserName, targetPath); + opsHostRemoteService.executeCommand(chown, rootSession, wsSession, "grant permission"); + } + + /** + * ensure dir exist + * + * @param rootSession rootSession + * @param targetPath targetPath + * @param retBuffer retBuffer + */ + protected void ensureDirExist(Session rootSession, String targetPath, RetBuffer retBuffer) { + String command = MessageFormat.format(SshCommandConstants.MK_DIR, targetPath); + opsHostRemoteService.executeCommand(command, rootSession, retBuffer, "create installation directory"); + } + + /** + * prepare Path ,path must end with / + * + * @param path path + * @return path + */ + protected String preparePath(String path) { + if (StrUtil.isEmpty(path) || path.endsWith("/")) { + return path; + } + return path + "/"; + } + + /** + * os status + * + * @param ommUserSession ommUserSession + * @param retBuffer retBuffer + * @param envFile envFile + * @return OmStatusModel + */ + protected OmStatusModel omStatus(Session ommUserSession, RetBuffer retBuffer, String envFile) { + OmStatusModel omStatusModel = new OmStatusModel(); + try { + String statusCommand = "gs_om -t status --detail"; + statusCommand = addCommandOfLoadEnvironmentVariable(statusCommand, envFile); + String result = opsHostRemoteService.executeCommand(statusCommand, ommUserSession, retBuffer); + Map nodeIdMapHostname = new HashMap<>(); + Map hostnameMapNodeId = new HashMap<>(); + omStatusModel.setInstallCm(result.contains("[ CMServer State ]")); + omStatusModel.setNodeIdMapHostname(nodeIdMapHostname); + omStatusModel.setHostnameMapNodeId(hostnameMapNodeId); + + if (omStatusModel.isInstallCm()) { + int datanodeStateIndex = result.indexOf("[ CMServer State ]"); + if (datanodeStateIndex < 0) { + log.warn("CM server status is empty"); + retBuffer.sendText("CM server status is empty"); + } else { + int splitIndex = result.indexOf("------------------", datanodeStateIndex); + String dataNodeStateStr = result.substring(splitIndex); + String[] dataNode = dataNodeStateStr.split("\n"); + for (String s : dataNode) { + if (StrUtil.isEmpty(s)) { + break; + } + String[] s1 = s.replaceAll(" +", " ").split(" "); + if (s1.length > 2) { + nodeIdMapHostname.put(s1[0], s1[1]); + hostnameMapNodeId.put(s1[1], s1[0]); + } + } + } + } + } catch (Exception e) { + log.error("An exception occurred during startup,gs_om status fail", e); + throw new OpsException("An exception occurred during startup,gs_om status fail"); + } + + return omStatusModel; + } + + /** + * Create a remote user for enterprise clusters + * + * @param installContext installContext + * @param opsClusterContext opsClusterContext + */ + protected void createEnterpriseRemoteUser(InstallContext installContext, OpsClusterContext opsClusterContext) { + RetBuffer retSession = installContext.getRetBuffer(); + opsClusterContext.setRetBuffer(retSession); + opsClusterContext.setHostInfoHolders(installContext.getHostInfoHolders()); + + EnterpriseInstallConfig enterpriseInstallConfig = installContext.getEnterpriseInstallConfig(); + List nodeConfigList = enterpriseInstallConfig.getNodeConfigList(); + String databasePassword = enterpriseInstallConfig.getDatabasePassword(); + Integer port = enterpriseInstallConfig.getPort(); + + for (EnterpriseInstallNodeConfig enterpriseInstallNodeConfig : nodeConfigList) { + String hostId = enterpriseInstallNodeConfig.getHostId(); + String installUserId = enterpriseInstallNodeConfig.getInstallUserId(); + String dataPath = enterpriseInstallNodeConfig.getDataPath(); + Session session = createSessionWithUserId(installContext, false, hostId, installUserId); + String listenerAddress = MessageFormat.format(SshCommandConstants.LISTENER, dataPath); + listenerAddress = addCommandOfLoadEnvironmentVariable(listenerAddress, installContext.getEnvPath()); + opsHostRemoteService.executeCommand(listenerAddress, session, retSession, "modify listening address"); + + String hba = MessageFormat.format(SshCommandConstants.HBA, dataPath); + hba = addCommandOfLoadEnvironmentVariable(hba, installContext.getEnvPath()); + opsHostRemoteService.executeCommand(hba, session, retSession, "modify hba host"); + String clientLoginOpenGauss = MessageFormat.format(SshCommandConstants.LOGIN, String.valueOf(port)); + try { + Map response = new HashMap<>(); + String createUser = MessageFormat.format("CREATE USER gaussdb WITH MONADMIN AUDITADMIN " + + "SYSADMIN PASSWORD \"{0}\";\\q", encryptionUtils.decrypt(databasePassword)); + response.put("openGauss=#", createUser); + if (enterpriseInstallConfig.getDatabaseKernelArch().equals(DatabaseKernelArch.MASTER_SLAVE) + || enterpriseInstallNodeConfig.getIsCMMaster()) { + clientLoginOpenGauss = addCommandOfLoadEnvironmentVariable(clientLoginOpenGauss, + installContext.getEnvPath()); + opsHostRemoteService.executeCommand(clientLoginOpenGauss, session, retSession, response); + } + } catch (Exception e) { + log.error("Failed to create user", e); + throw new OpsException("Failed to create user"); + } + } + + opsClusterContext.setHostInfoHolders(installContext.getHostInfoHolders()); + restart(opsClusterContext); + } + + /** + * get host info holder + * + * @param installContext installContext + * @param hostId hostId + * @return HostInfoHolder + */ + protected HostInfoHolder getHostInfoHolder(InstallContext installContext, String hostId) { + return installContext.getHostInfoHolders() + .stream() + .filter(hostInfoHolder -> hostId.equals(hostInfoHolder.getHostEntity().getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("host information does not exist")); + } + + /** + * get host info holder list + * + * @param hostInfoHolders hostInfoHolders + * @param hostId hostId + * @return HostInfoHolder + */ + protected HostInfoHolder getHostInfoHolder(List hostInfoHolders, String hostId) { + return hostInfoHolders + .stream() + .filter(hostInfoHolder -> hostId.equals(hostInfoHolder.getHostEntity().getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("host information does not exist")); + } + + /** + * get host user info by username + * + * @param hostInfoHolder hostInfoHolder + * @param username username + * @return OpsHostUserEntity + */ + protected OpsHostUserEntity getHostUserInfoByUsername(HostInfoHolder hostInfoHolder, String username) { + return hostInfoHolder.getHostUserEntities() + .stream() + .filter(userInfo -> username.equals(userInfo.getUsername())) + .findFirst() + .orElseThrow(() -> new OpsException("Installation user information does not exist")); + } + + /** + * get host user info by userId + * + * @param hostInfoHolder hostInfoHolder + * @param userId userId + * @return OpsHostUserEntity + */ + protected OpsHostUserEntity getHostUserInfo(HostInfoHolder hostInfoHolder, String userId) { + return hostInfoHolder.getHostUserEntities() + .stream() + .filter(userInfo -> userId.equals(userInfo.getHostUserId())) + .findFirst() + .orElseThrow(() -> new OpsException("Installation user information does not exist")); + } + + /** + * cleanResource + * + * @param installContext installContext + */ + protected void cleanResource(InstallContext installContext) { + String delCmd = prepareCleanClusterDir(installContext); + for (HostInfoHolder hostInfoHolder : installContext.getHostInfoHolders()) { + Session currentRoot = createSessionWithRootUser(hostInfoHolder); + opsHostRemoteService.executeCommand(delCmd, currentRoot, installContext.getRetBuffer(), + "remove install path"); + currentRoot.disconnect(); + } + log.warn("clean resource success"); + } + + /** + * prepareCleanClusterDir + * + * @param installContext installContext + * @return String + */ + protected abstract String prepareCleanClusterDir(InstallContext installContext); + + /** + * addCommandOfLoadEnvironmentVariable + * + * @param command command + * @param env env + * @return String + */ + protected String addCommandOfLoadEnvironmentVariable(String command, String env) { + if (StrUtil.isNotEmpty(env)) { + command = "source " + env + " && " + command; + } + return command; + } + + /** + * sendOperateLog + * + * @param installContext installContext + * @param context context + */ + protected void sendOperateLog(InstallContext installContext, String context) { + installContext.getRetBuffer().sendText(context); + } + + @Override + public void afterPropertiesSet() { + ClusterTaskProviderManager.registry(version(), this); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/EnterpriseOpsProvider.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/EnterpriseOpsProvider.java new file mode 100644 index 000000000..46bc8b494 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/EnterpriseOpsProvider.java @@ -0,0 +1,1390 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * EnterpriseOpsProvider.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/EnterpriseOpsProvider.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.provider; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.jcraft.jsch.Session; +import lombok.Builder; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.HostInfoHolder; +import org.opengauss.admin.plugin.domain.model.ops.InstallContext; +import org.opengauss.admin.plugin.domain.model.ops.JschResult; +import org.opengauss.admin.plugin.domain.model.ops.LunPathManager; +import org.opengauss.admin.plugin.domain.model.ops.OmStatusModel; +import org.opengauss.admin.plugin.domain.model.ops.OpsClusterContext; +import org.opengauss.admin.plugin.domain.model.ops.SharingStorageInstallConfig; +import org.opengauss.admin.plugin.domain.model.ops.SshCommandConstants; +import org.opengauss.admin.plugin.domain.model.ops.UnInstallContext; +import org.opengauss.admin.plugin.domain.model.ops.UpgradeContext; +import org.opengauss.admin.plugin.domain.model.ops.WsSession; +import org.opengauss.admin.plugin.domain.model.ops.node.EnterpriseInstallNodeConfig; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.enums.ops.DatabaseKernelArch; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.enums.ops.UpgradeTypeEnum; +import org.opengauss.admin.plugin.enums.ops.WdrScopeEnum; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterService; +import org.opengauss.admin.plugin.service.ops.impl.function.GenerateClusterConfigXmlInstance; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.plugin.utils.WsUtil; +import org.opengauss.admin.system.service.ISysSettingService; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import jakarta.annotation.Resource; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author lhf + * @date 2022/8/12 09:23 + **/ +@Slf4j +@Service +public class EnterpriseOpsProvider extends AbstractOpsProvider { + + @Autowired + private IOpsClusterService opsClusterService; + @Autowired + private IOpsClusterNodeService opsClusterNodeService; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private ISysSettingService sysSettingService; + @Autowired + private JschUtil jschUtil; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + @Autowired + private WsUtil wsUtil; + @Resource + private GenerateClusterConfigXmlInstance generateClusterConfigXmlInstance; + + @Override + public OpenGaussVersionEnum version() { + return OpenGaussVersionEnum.ENTERPRISE; + } + + @Override + public void install(InstallContext installContext) { + log.info("Start installing Enterprise Edition"); + boolean isInstallSucc = true; + try { + doInstall(installContext); + + OpsClusterContext opsClusterContext = new OpsClusterContext(); + OpsClusterEntity opsClusterEntity = installContext.toOpsClusterEntity(); + List opsClusterNodeEntities = + installContext.getEnterpriseInstallConfig().toOpsClusterNodeEntityList(); + opsClusterContext.setOpsClusterEntity(opsClusterEntity); + opsClusterContext.setOpsClusterNodeEntityList(opsClusterNodeEntities); + + wsUtil.sendText(installContext.getRetSession(), "CREATE_REMOTE_USER"); + createEnterpriseRemoteUser(installContext, opsClusterContext, jschUtil, encryptionUtils); + + wsUtil.sendText(installContext.getRetSession(), "SAVE_INSTALL_CONTEXT"); + saveContext(installContext); + wsUtil.sendText(installContext.getRetSession(), "FINISH"); + log.info("The installation is complete"); + } catch (OpsException e) { + log.error("install failed:", e); + isInstallSucc = false; + throw new OpsException("The installation is failed."); + } finally { + if (!isInstallSucc) { + cleanResource(installContext); + } + } + } + + private String prepareCleanClusterDir(InstallContext installContext) { + String delCmd = ""; + try{ + // remove intall path software + String installPackagePathPath = installContext.getEnterpriseInstallConfig().getInstallPackagePath(); + log.info("install package path : {}", installPackagePathPath); + + String delInstallPath = MessageFormat.format(SshCommandConstants.DEL_FILE, installPackagePathPath + "/*"); + log.info("delete install package path : {}", delInstallPath); + String installDataPath = installContext.getEnterpriseInstallConfig().getInstallPath(); + String delAppPath = MessageFormat.format(SshCommandConstants.DEL_FILE, installDataPath); + log.info("delete app path : {}", delAppPath); + String delTmpPath = MessageFormat.format(SshCommandConstants.DEL_FILE, + installContext.getEnterpriseInstallConfig().getTmpPath()); + log.info("delete tmp path : {}", delTmpPath); + String delOmPath = MessageFormat.format(SshCommandConstants.DEL_FILE, + installContext.getEnterpriseInstallConfig().getOmToolsPath()); + log.info("delete om path : {}", delOmPath); + delCmd = delInstallPath + " || echo \"delInstallPath failed\"; " + + delAppPath + " || echo \"delAppPath failed\"; " + + delTmpPath + " || echo \"delTmpPath failed\"; " + + delOmPath + " || echo \"delOmPath failed\"; "; + if (installContext.getEnterpriseInstallConfig().getDatabaseKernelArch() == + DatabaseKernelArch.SHARING_STORAGE) { + String dssHome = installContext.getEnterpriseInstallConfig(). + getSharingStorageInstallConfig().getDssHome(); + String delDssHome = MessageFormat.format(SshCommandConstants.DEL_FILE, dssHome); + delCmd += delDssHome + " || echo \"delDssHome failed\"; "; + } + + EnterpriseInstallNodeConfig masterNodeConfig = installContext.getEnterpriseInstallConfig(). + getNodeConfigList() + .stream().filter(nodeConfig -> nodeConfig.getClusterRole() == ClusterRoleEnum.MASTER) + .findFirst().orElseThrow(() -> new OpsException("Master node information not found")); + String delDataPath = MessageFormat.format(SshCommandConstants.DEL_FILE, masterNodeConfig.getDataPath()); + delCmd += delDataPath + " || echo \"delDataPath failed\"; "; + if (installContext.getEnterpriseInstallConfig().getIsInstallCM()) { + String delCmPath = MessageFormat.format(SshCommandConstants.DEL_FILE, masterNodeConfig.getCmDataPath()); + delCmd += delCmPath + " || echo \"delCmPath failed\"; "; + + } + return delCmd; + } catch (OpsException e) { + log.error("delete cmd : {}", delCmd); + return delCmd; + } + } + + private void cleanResource(InstallContext installContext) { + String delCmd = prepareCleanClusterDir(installContext); + for (HostInfoHolder hostInfoHolder : installContext.getHostInfoHolders()) { + OpsHostEntity hostEntity = hostInfoHolder.getHostEntity(); + Session currentRoot = loginWithUser(jschUtil, encryptionUtils, + installContext.getHostInfoHolders(), true, hostEntity.getHostId(), null); + try { + JschResult jschResult = jschUtil.executeCommand(delCmd, currentRoot); + if (0 != jschResult.getExitCode()) { + log.error("remove install path failed, exit code: {}, error message: {}", + jschResult.getExitCode(), + jschResult.getResult()); + } + + } catch (OpsException | IOException | InterruptedException e) { + log.error("remove install path failed:", e); + } + + // remove env file + String delEnvFile = MessageFormat.format(SshCommandConstants.DEL_FILE, installContext.getEnvPath()); + try { + JschResult jschResult = jschUtil.executeCommand(delEnvFile, currentRoot); + if (0 != jschResult.getExitCode()) { + log.error("remove env file failed, exit code: {}, error message: {}", jschResult.getExitCode(), + jschResult.getResult()); + } + } catch (OpsException | IOException | InterruptedException e) { + log.error("remove env file failed:", e); + } + currentRoot.disconnect(); + } + log.error("clean resource success"); + } + + private void removeSoftLinkAll(UnInstallContext unInstallContext) { + OpsClusterNodeEntity opsClusterNodeEntity = unInstallContext.getOpsClusterNodeEntityList() + .stream() + .filter(opsClusterNodeEntity1 -> opsClusterNodeEntity1.getClusterRole() == ClusterRoleEnum.MASTER) + .findFirst().orElseThrow(() -> new OpsException("master node not found")); + if (opsClusterNodeEntity.getIsEnableDss() == null || !opsClusterNodeEntity.getIsEnableDss()) { + log.info("enableDss is false."); + return; + } + try { + for (HostInfoHolder hostInfoHolder : unInstallContext.getHostInfoHolders()) { + OpsHostEntity hostEntity = hostInfoHolder.getHostEntity(); + + Session rootSession = loginWithUser(jschUtil, encryptionUtils, + unInstallContext.getHostInfoHolders(), true, hostEntity.getHostId(), null); + removeSoftLink(opsClusterNodeEntity.getDssDataLunLinkPath(), rootSession); + + String xlogLinkPaths = opsClusterNodeEntity.getXlogLunLinkPath(); + String[] xlogPaths = xlogLinkPaths.split(","); + for (String xlogPath : xlogPaths) { + removeSoftLink(xlogPath, rootSession); + } + + removeSoftLink(opsClusterNodeEntity.getCmSharingLunLinkPath(), rootSession); + removeSoftLink(opsClusterNodeEntity.getCmVotingLunLinkPath(), rootSession); + + if (Objects.nonNull(rootSession) && rootSession.isConnected()) { + rootSession.disconnect(); + } + } + } catch (Exception e) { + log.error("remove soft link failed", e); + } + log.info("remove soft links success"); + } + + private void removeSoftLink(String link, Session rootSession) { + try { + String command = MessageFormat.format(SshCommandConstants.DEL_FILE, link); + JschResult jschResult = jschUtil.executeCommand(command, rootSession); + if (0 != jschResult.getExitCode()) { + log.warn("remove soft link failed {}", link); + throw new OpsException("remove soft link failed"); + } + } catch (Exception e) { + log.warn("remove soft link failed {}", link); + throw new OpsException("remove soft link failed"); + } + } + + /** + * create soft link + * + * @param path lun path + * @param prefix link path prefix + * @param rootSession session + * @param isDisasterNeed this parameter function has been deprecated + * @param installUser install user + * @return link path + */ + private String createSoftLink(LunPathManager path, String prefix, Session rootSession, boolean isDisasterNeed, + String installUser) { + try { + String exec = getDiskQueryCmd(path); + JschResult jschResult = jschUtil.executeCommand(exec, rootSession); + if (0 != jschResult.getExitCode()) { + log.error("can not find path with wwn {}, exit code: {}, error message: {}", path.getWwn(), + jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("can not find path with wwn"); + } + + String disk = LunPathManager.getLunDiskName(jschResult.getResult()); + String linkPath = LunPathManager.getLunLinkPath(path, prefix, isDisasterNeed, installUser); + String command = "ls -l " + linkPath; + try { + if (jschUtil.executeCommand(command, rootSession).getExitCode() == 0) { + jschResult = jschUtil.executeCommand( + MessageFormat.format(SshCommandConstants.DEL_FILE, linkPath), rootSession); + if (jschResult.getExitCode() != 0) { + log.error("remove exist soft link failed, exit code: {}, error message: {}", + jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("remove exist soft link failed"); + } + } + } catch (Exception e) { + log.info("command not exist : {}", command); + } + + command = "ln -s " + disk.trim() + " " + linkPath; + jschResult = jschUtil.executeCommand(command, rootSession); + if (0 != jschResult.getExitCode()) { + log.error("create soft link failed, exit code: {}, error message: {}", + jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("create soft link failed"); + } + log.info("create soft link {} success with host {}", command, rootSession.getHost()); + return linkPath; + } catch (Exception e) { + log.error("create soft link failed", e); + throw new OpsException("create soft link failed"); + } + } + + private static String getDiskQueryCmd(LunPathManager path) { + String queryCommand = LunPathManager.lunQueryMethod.getQueryDiskVolumeCmd(); + return String.format(queryCommand, path.getWwn()); + } + + private void createSoftLink(InstallContext installContext) { + if (installContext.getEnterpriseInstallConfig().getDatabaseKernelArch() + .equals(DatabaseKernelArch.MASTER_SLAVE)) { + return; + } + try { + for (HostInfoHolder hostInfoHolder : installContext.getHostInfoHolders()) { + OpsHostEntity hostEntity = hostInfoHolder.getHostEntity(); + + Session rootSession = loginWithUser(jschUtil, encryptionUtils, + installContext.getHostInfoHolders(), true, hostEntity.getHostId(), null); + SharingStorageInstallConfig config = + installContext.getEnterpriseInstallConfig().getSharingStorageInstallConfig(); + String installUser = installContext.getEnterpriseInstallConfig() + .getNodeConfigList() + .stream() + .filter(node -> hostEntity.getHostId().equals(node.getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("root user information not found")).getInstallUsername(); + config.setDssDataLunLinkPath(createSoftLink(new LunPathManager(config.getDssDataLunPath()), + "data", rootSession, true, installUser)); + List xlogPaths = config.getXlogLunPath(); + List linkXlogPaths = new ArrayList<>(); + if (xlogPaths.size() == 1) { + linkXlogPaths.add( + createSoftLink(new LunPathManager(xlogPaths.get(0).trim()), "xlog", rootSession, true, + installUser)); + } else { + for (String xlogPath : xlogPaths) { + linkXlogPaths.add( + createSoftLink(new LunPathManager(xlogPath.trim()), "xlog", rootSession, false, + installUser)); + } + } + config.setXlogLunLinkPath(linkXlogPaths); + config.setCmSharingLunLinkPath(createSoftLink(new LunPathManager( + config.getCmSharingLunPath()), "cm_sharing", rootSession, false, installUser)); + config.setCmVotingLunLinkPath(createSoftLink(new LunPathManager( + config.getCmVotingLunPath()), "cm_voting", rootSession, false, installUser)); + + if (Objects.nonNull(rootSession) && rootSession.isConnected()) { + rootSession.disconnect(); + } + } + } catch (Exception e) { + log.error("create soft link failed", e); + throw new OpsException("create soft link failed"); + } + } + + private void doInstall(InstallContext installContext) { + wsUtil.sendText(installContext.getRetSession(), "START_GEN_XML_CONFIG"); + createSoftLink(installContext); + String xml = generateClusterConfigXmlInstance.generate(installContext); + log.info("Generated xml information:{}", xml); + wsUtil.sendText(installContext.getRetSession(), "END_GEN_XML_CONFIG"); + + WsSession retSession = installContext.getRetSession(); + + // Master node configuration information + EnterpriseInstallNodeConfig masterNodeConfig = installContext.getEnterpriseInstallConfig().getNodeConfigList().stream().filter(nodeConfig -> nodeConfig.getClusterRole() == ClusterRoleEnum.MASTER).findFirst().orElseThrow(() -> new OpsException("Master node information not found")); + String masterHostId = masterNodeConfig.getHostId(); + + for (HostInfoHolder hostInfoHolder : installContext.getHostInfoHolders()) { + OpsHostEntity hostEntity = hostInfoHolder.getHostEntity(); + Session currentRoot = loginWithUser(jschUtil, encryptionUtils, + installContext.getHostInfoHolders(), true, hostEntity.getHostId(), null); + installDependency( jschUtil, currentRoot, retSession, installContext.getOs()); + currentRoot.disconnect(); + } + + // root + Session rootSession = loginWithUser(jschUtil, encryptionUtils, installContext.getHostInfoHolders(), true, masterHostId, null); + + String pkgPath = preparePath(installContext.getEnterpriseInstallConfig().getInstallPackagePath()); + ensureDirExist(jschUtil, rootSession, pkgPath, retSession); + chmodFullPath(jschUtil, rootSession, pkgPath, retSession); + ensureEnvPathPermission(jschUtil, rootSession, installContext.getEnvPath(), retSession); + + // scp + wsUtil.sendText(installContext.getRetSession(), "START_SCP_INSTALL_PACKAGE"); + String installPackageFullPath = scpInstallPackageToMasterNode(jschUtil, rootSession, installContext.getInstallPackagePath(), pkgPath, retSession); + wsUtil.sendText(installContext.getRetSession(), "END_SCP_INSTALL_PACKAGE"); + + // unzip install pack + wsUtil.sendText(installContext.getRetSession(), "START_UNZIP_INSTALL_PACKAGE"); + decompress(jschUtil, rootSession, pkgPath, installPackageFullPath, retSession, "-xvf"); + + // unzip OM + String omPackage = getOMPackage(pkgPath, rootSession); + decompress(jschUtil, rootSession, pkgPath, + pkgPath + System.getProperty("file.separator") + omPackage, retSession, "-zxvf"); + wsUtil.sendText(installContext.getRetSession(), "END_UNZIP_INSTALL_PACKAGE"); + + // write xml + String xmlConfigFullPath = pkgPath + "/cluster_config.xml"; + String createClusterConfigXmlCommand = MessageFormat.format(SshCommandConstants.FILE_CREATE, xml, xmlConfigFullPath); + try { + JschResult jschResult = jschUtil.executeCommand(createClusterConfigXmlCommand, rootSession, retSession); + if (0 != jschResult.getExitCode()) { + log.error("Failed to create xml configuration file, exit code: {}, error message: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to create xml configuration file"); + } + } catch (Exception e) { + log.error("Failed to create xml configuration file:", e); + throw new OpsException("Failed to create xml configuration file"); + } + + String group = null; + String userGroupCommand = "groups " + masterNodeConfig.getInstallUsername() + " | awk -F ':' '{print $2}' | sed 's/\\\"//g'"; + try { + JschResult jschResult = jschUtil.executeCommand(userGroupCommand, rootSession, retSession); + if (0 != jschResult.getExitCode()) { + log.error("Querying user group failed, exit code: {}, error message: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to query user group"); + } + + group = jschResult.getResult(); + } catch (Exception e) { + log.error("Failed to query user group:", e); + throw new OpsException("Failed to query user group"); + } + + List hostInfoHolders = installContext.getHostInfoHolders(); + HostInfoHolder masterHostHolder = hostInfoHolders + .stream() + .filter(holder -> holder.getHostEntity().getHostId().equals(masterHostId)) + .findFirst() + .orElseThrow(() -> new OpsException("Master node host information not found")); + OpsHostUserEntity masterRootUserInfo = masterHostHolder + .getHostUserEntities() + .stream() + .filter(hostUser -> hostUser.getUsername().equals("root")) + .findFirst() + .orElseThrow( + () -> new OpsException("The root user information of the primary node cannot be found")); + OpsHostUserEntity installUserInfo = masterHostHolder.getHostUserEntities().stream().filter(hostUser -> hostUser.getHostUserId().equals(masterNodeConfig.getInstallUserId())).findFirst().orElseThrow(() -> new OpsException("No installation user information found")); + + wsUtil.sendText(installContext.getRetSession(), "START_EXE_PREINSTALL_COMMAND"); + preInstall(group, pkgPath, masterNodeConfig, xmlConfigFullPath, rootSession, retSession, + masterRootUserInfo.getPassword(), installUserInfo.getPassword(), installContext.getEnvPath()); + wsUtil.sendText(installContext.getRetSession(), "END_EXE_PREINSTALL_COMMAND"); + + Session ommSession = loginWithUser(jschUtil, encryptionUtils, installContext.getHostInfoHolders(), false, masterHostId, masterNodeConfig.getInstallUserId()); + + // install + wsUtil.sendText(installContext.getRetSession(), "START_EXE_INSTALL_COMMAND"); + String installCommand = MessageFormat.format(SshCommandConstants.ENTERPRISE_INSTALL, xmlConfigFullPath); + try { + String dbPassport = encryptionUtils.decrypt( + installContext.getEnterpriseInstallConfig().getDatabasePassword()); + Map autoResponse = new HashMap<>(); + autoResponse.put("(yes/no)?", "yes"); + autoResponse.put("Please enter password for database:", dbPassport); + autoResponse.put("Please repeat for database:", dbPassport); + + JschResult jschResult = jschUtil.executeCommand(installContext.getEnvPath(), installCommand, ommSession, retSession, autoResponse); + if (0 != jschResult.getExitCode()) { + log.error("install failed, exit code: {}, error message: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("install failed"); + } + } catch (Exception e) { + log.error("install failed:", e); + throw new OpsException("install failed"); + } + wsUtil.sendText(installContext.getRetSession(), "END_EXE_INSTALL_COMMAND"); + } + + private void chown(Session rootSession, String installUserName, String targetPath, WsSession wsSession) { + String chown = MessageFormat.format(SshCommandConstants.CHOWN, installUserName, targetPath); + + try { + JschResult jschResult = null; + try { + jschResult = jschUtil.executeCommand(chown, rootSession, wsSession); + } catch (InterruptedException e) { + throw new OpsException("thread is interrupted"); + } + if (0 != jschResult.getExitCode()) { + log.error("Failed to grant permission, exit code: {}, error message: {}", + jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to grant permission"); + } + } catch (IOException e) { + log.error("Failed to grant permission", e); + throw new OpsException("Failed to grant permission"); + } + } + + @Override + public void upgrade(UpgradeContext upgradeContext) { + checkUpgrade(upgradeContext); + + Session rootSession = null; + Session ommSession = null; + try { + rootSession = jschUtil.getSession(upgradeContext.getHostPublicIp(), + upgradeContext.getHostPort(), + "root", + encryptionUtils.decrypt(upgradeContext.getRootPassword())) + .orElseThrow( + () -> new OpsException("The root user failed to establish a connection")); + ommSession = jschUtil.getSession(upgradeContext.getHostPublicIp(), upgradeContext.getHostPort(), upgradeContext.getInstallUsername(), encryptionUtils.decrypt(upgradeContext.getInstallUserPassword())).orElseThrow(() -> new OpsException("Install user connection connection failed")); + enableStreamReplication(ommSession, upgradeContext); + checkClusterStatus(ommSession, upgradeContext); + upgradePreinstall(rootSession, upgradeContext); + doUpgrade(ommSession, upgradeContext); + upgradeCheck(ommSession, upgradeContext); + // upgradeCommit(ommSession, upgradeContext); + // updateClusterVersion(upgradeContext); + } finally { + if (Objects.nonNull(rootSession) && rootSession.isConnected()) { + rootSession.disconnect(); + } + + if (Objects.nonNull(ommSession) && ommSession.isConnected()) { + ommSession.disconnect(); + } + } + } + + private void updateClusterVersion(UpgradeContext upgradeContext) { + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(OpsClusterEntity.class) + .set(OpsClusterEntity::getVersionNum, upgradeContext.getVersionNum()) + .eq(OpsClusterEntity::getClusterId, upgradeContext.getClusterEntity().getClusterId()); + opsClusterService.update(updateWrapper); + } + + private void upgradeCommit(Session ommSession, UpgradeContext upgradeContext) { + wsUtil.sendText(upgradeContext.getRetSession(), "COMMIT_UPGRADE"); + try { + String command = "gs_upgradectl -t commit-upgrade -X " + upgradeContext.getClusterConfigXmlPath(); + JschResult jschResult = jschUtil.executeCommand(command, ommSession, upgradeContext.getRetSession()); + if (0 != jschResult.getExitCode()) { + log.error("upgradeCommit failed, exit code: {}, error message: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("upgradeCommit failed"); + } + } catch (Exception e) { + log.error("upgradeCommit failed", e); + throw new OpsException("upgradeCommit failed"); + } + } + + private void upgradeCheck(Session ommSession, UpgradeContext upgradeContext) { + wsUtil.sendText(upgradeContext.getRetSession(), "UPGRADE_CHECK_CLUSTER_STATUS"); + try { + String command = "gs_om -t status"; + JschResult jschResult = jschUtil.executeCommand(command,upgradeContext.getSepEnvFile(), ommSession, upgradeContext.getRetSession()); + if (0 != jschResult.getExitCode()) { + log.error("upgradeCheck failed, exit code: {}, error message: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("upgradeCheck failed"); + } + + String result = jschResult.getResult(); + if (!result.contains("cluster_state") + || !result.substring(result.indexOf("cluster_state"), + result.indexOf("\n",result.indexOf("cluster_state"))) + .contains("Normal")) { + log.error("cluster status:{}", result); + throw new OpsException("cluster status check fail"); + } + } catch (Exception e) { + log.error("upgradeCheck failed", e); + throw new OpsException("upgradeCheck failed"); + } + } + + private void doUpgrade(Session ommSession, UpgradeContext upgradeContext) { + wsUtil.sendText(upgradeContext.getRetSession(), "DO_UPGRADE"); + try { + String command = "gs_upgradectl -t auto-upgrade -X " + upgradeContext.getClusterConfigXmlPath(); + if (upgradeContext.getUpgradeType() == UpgradeTypeEnum.GRAY_UPGRADE) { + command = command + " --grey"; + } + + JschResult jschResult = jschUtil.executeCommand(command,upgradeContext.getSepEnvFile(), ommSession, upgradeContext.getRetSession()); + if (0 != jschResult.getExitCode()) { + log.error("gs_upgradectl failed, exit code: {}, error message: {}", + jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("gs_upgradectl failed"); + } + } catch (Exception e) { + log.error("gs_upgradectl failed", e); + throw new OpsException("gs_upgradectl failed"); + } + } + + private void checkUpgrade(UpgradeContext upgradeContext) { + if (upgradeContext.getUpgradeType() == null) { + throw new OpsException("Upgrade context is null"); + } + + if (upgradeContext.getClusterEntity() == null) { + throw new OpsException("Upgrade cluster entity is null"); + } + String clusterId = upgradeContext.getClusterEntity().getClusterId(); + if (StrUtil.isEmpty(clusterId)) { + throw new OpsException("Upgrade cluster id is null"); + } + OpsClusterEntity clusterEntity = opsClusterService.getById(clusterId); + if (clusterEntity == null) { + throw new OpsException("Upgrade cluster entity not exist, cluster id: " + clusterId); + } + upgradeContext.setClusterConfigXmlPath(clusterEntity.getXmlConfigPath()); + upgradeContext.setSepEnvFile(clusterEntity.getEnvPath()); + + String upgradePackagePath = upgradeContext.getUpgradePackagePath(); + Integer adminId = 1; + String uploadPath = sysSettingService.getSetting(adminId).getUploadPath(); + if (StrUtil.isEmpty(upgradePackagePath)) { + throw new OpsException("Upgrade package path is null"); + } + if (!upgradePackagePath.startsWith(uploadPath)) { + throw new OpsException("Upgrade package path is not invalid, package path is not in datakit system upload " + + "path, upload path: " + uploadPath + ", upgrade package path: " + upgradePackagePath); + } + Path sourcePath = Paths.get(upgradePackagePath); + if (!Files.exists(sourcePath) || !Files.isRegularFile(sourcePath)) { + log.error("Upgrade source package not found, source path: {}", sourcePath); + throw new OpsException("Upgrade source package not found, source path: " + sourcePath); + } + + if (upgradeContext.getRetSession() == null) { + throw new OpsException("Upgrade ret session is null"); + } + } + + private void upgradePreinstall(Session rootSession, UpgradeContext upgradeContext) { + wsUtil.sendText(upgradeContext.getRetSession(), "PREINSTALL"); + String installUser = upgradeContext.getInstallUsername(); + String installUserHome = getInstallUserHome(installUser, rootSession, upgradeContext); + String targetPath = generateTargetPath(installUser, installUserHome); + + prepareUpgradePkgPath(targetPath, rootSession, upgradeContext); + checkUpgradePkgPathSize(targetPath, rootSession, upgradeContext); + uploadUpgradePackage(targetPath, rootSession, upgradeContext); + + decompress(jschUtil, rootSession, targetPath, + targetPath + System.getProperty("file.separator") + + FileUtil.getName(upgradeContext.getUpgradePackagePath()), + upgradeContext.getRetSession(), "-xvf"); + + String omPackage = getOMPackage(targetPath, rootSession); + decompress(jschUtil, rootSession, targetPath, + targetPath + System.getProperty("file.separator") + omPackage, + upgradeContext.getRetSession(), "-zxvf"); + + String group = getUserGroup(installUser, rootSession, upgradeContext); + changeUpgradePathPermission(installUser, group, targetPath, rootSession, upgradeContext); + executeUpgradePreinstall(installUser, group, targetPath, rootSession, upgradeContext); + } + + private void executeUpgradePreinstall( + String installUser, String group, String targetPath, Session rootSession, UpgradeContext upgradeContext) { + try { + StringBuilder commandBuilder = new StringBuilder(); + commandBuilder.append("cd ").append(targetPath).append("/script && ./gs_preinstall -U ").append(installUser) + .append(" -G ").append(group).append(" -X ").append(upgradeContext.getClusterConfigXmlPath()) + .append(" --non-interactive"); + String sepEnvFile = upgradeContext.getSepEnvFile(); + if (StrUtil.isNotEmpty(sepEnvFile)) { + commandBuilder.append(" --sep-env-file=").append(sepEnvFile); + } + + JschResult jschResult = jschUtil.executeCommand(commandBuilder.toString(), sepEnvFile, + rootSession, upgradeContext.getRetSession()); + if (jschResult.getExitCode() != 0) { + log.error("Execute upgrade preinstall failed, exit code: {}, error message: {}", + jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Execute upgrade preinstall failed"); + } + } catch (IOException | InterruptedException e) { + log.error("Execute upgrade preinstall failed", e); + throw new OpsException("Execute upgrade preinstall failed"); + } + } + + private void changeUpgradePathPermission( + String installUser, String group, String targetPath, Session rootSession, UpgradeContext upgradeContext) { + try { + String chmod = MessageFormat.format(SshCommandConstants.CHMOD, targetPath); + String chown = MessageFormat.format(SshCommandConstants.CHOWN_USER_GROUP, installUser, group, targetPath); + JschResult jschResult = jschUtil.executeCommand(chmod + " && " + chown, upgradeContext.getSepEnvFile(), + rootSession, upgradeContext.getRetSession()); + if (jschResult.getExitCode() != 0) { + log.error("Change upgrade path permission failed, exit code: {}, error message: {}", + jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Change upgrade path permission failed"); + } + } catch (IOException | InterruptedException e) { + log.error("Change upgrade path permission failed", e); + throw new OpsException("Change upgrade path permission failed"); + } + } + + private void uploadUpgradePackage(String targetPath, Session rootSession, UpgradeContext upgradeContext) { + wsUtil.sendText(upgradeContext.getRetSession(), "BEFORE_UPLOAD_PACKAGE"); + jschUtil.upload(rootSession, upgradeContext.getRetSession(), upgradeContext.getUpgradePackagePath(), + String.format("%s/%s", targetPath, FileUtil.getName(upgradeContext.getUpgradePackagePath()))); + wsUtil.sendText(upgradeContext.getRetSession(), "END_UPLOAD_PACKAGE"); + } + + private void checkUpgradePkgPathSize(String targetPath, Session rootSession, UpgradeContext upgradeContext) { + try { + String command = String.format("df %s | awk 'NR==2 {print $4}'", targetPath); + JschResult jschResult = jschUtil.executeCommand(command, upgradeContext.getSepEnvFile(), rootSession, + upgradeContext.getRetSession()); + if (jschResult.getExitCode() != 0) { + log.error("Failed to check the remaining space in upgrade package storage directory, path: {}, " + + "error: {}", targetPath, jschResult.getResult()); + throw new OpsException("Failed to check the remaining space in upgrade package storage directory"); + } + long targetPathSize = Long.parseLong(jschResult.getResult().trim()); + + Path sourcePath = Paths.get(upgradeContext.getUpgradePackagePath()); + if (!Files.exists(sourcePath) || !Files.isRegularFile(sourcePath)) { + log.error("Upgrade source package not found, source path: {}", sourcePath); + throw new OpsException("Upgrade source package not found, source path: " + sourcePath); + } + long sourcePkgSize = Files.size(sourcePath); + + if (targetPathSize * 1024 <= sourcePkgSize * 3) { + String errorMsg = String.format("The remaining space is not enough to upload upgrade package, " + + "path: %s, at least: %sB", targetPath, sourcePkgSize * 3); + log.error(errorMsg); + throw new OpsException(errorMsg); + } + } catch (IOException | InterruptedException e) { + log.error("Failed to check the remaining space in upgrade package storage directory", e); + throw new OpsException("Failed to check the remaining space in upgrade package storage directory"); + } + } + + private void prepareUpgradePkgPath(String targetPath, Session rootSession, UpgradeContext upgradeContext) { + try { + String command = MessageFormat.format("[ -d \"{0}\" ] && rm -rf \"{0}\"/* 2>/dev/null || mkdir -p \"{0}\"", + targetPath); + JschResult jschResult = jschUtil.executeCommand(command, upgradeContext.getSepEnvFile(), rootSession, + upgradeContext.getRetSession()); + if (jschResult.getExitCode() != 0) { + log.error("Prepare upgrade package path failed, exit code: {}, error message: {}", + jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Prepare upgrade package path failed"); + } + } catch (IOException | InterruptedException e) { + log.error("Prepare upgrade package path failed", e); + throw new OpsException("Prepare upgrade package path failed"); + } + } + + private String getUserGroup(String installUser, Session rootSession, UpgradeContext upgradeContext) { + String userGroupCommand = "groups " + installUser + " | awk -F ':' '{print $2}' | sed 's/\\\"//g'"; + try { + JschResult jschResult = jschUtil.executeCommand(userGroupCommand, upgradeContext.getSepEnvFile(), + rootSession, upgradeContext.getRetSession()); + if (jschResult.getExitCode() != 0) { + log.error("Querying user group failed, exit code: {}, error message: {}", jschResult.getExitCode(), + jschResult.getResult()); + throw new OpsException("Failed to query user group"); + } + return jschResult.getResult().trim(); + } catch (IOException | InterruptedException e) { + log.error("Failed to query user group. Error: {}", e.getMessage()); + throw new OpsException("Failed to query user group."); + } + } + + private String generateTargetPath(String installUser, String installUserHome) { + String homePath = installUserHome.trim(); + if (StrUtil.isEmpty(homePath)) { + homePath = "/home/" + installUser; + } + return String.format("%s/gaussdb_upgrade", homePath); + } + + private String getInstallUserHome(String installUser, Session rootSession, UpgradeContext upgradeContext) { + String command = MessageFormat.format(SshCommandConstants.GET_INSTALL_USER_HOME, installUser); + try { + JschResult jschResult = jschUtil.executeCommand(command, upgradeContext.getSepEnvFile(), rootSession, + upgradeContext.getRetSession()); + if (jschResult.getExitCode() != 0) { + log.error("Failed to obtain the install user home by command: {}. Exit code: {}", + command, jschResult.getExitCode()); + throw new OpsException("Failed to obtain the install user home."); + } + return jschResult.getResult().trim(); + } catch (IOException | InterruptedException e) { + log.error("Failed to obtain the install user home by command: {}. Error: {}", command, e.getMessage()); + throw new OpsException("Failed to obtain the install user home."); + } + } + + private String getOMPackage(String path, Session rootSession) { + String command = MessageFormat.format(SshCommandConstants.GET_OM_PACKAGE, path); + + try { + JschResult jschResult = jschUtil.executeCommand(command, rootSession); + + if (jschResult.getExitCode() != 0) { + log.error("Failed to obtain the OM package by command: {}. Exit code: {}", + command, jschResult.getExitCode()); + throw new OpsException("Failed to obtain the OM package."); + } + + String commandResult = jschResult.getResult().trim(); + + if (commandResult.contains(String.valueOf((char) 10))) { + String errorMsg = "One OM package is expected to be queried, but multiple OM packages are queried. " + + "Please check the package path: " + path; + log.error(errorMsg); + throw new OpsException(errorMsg); + } + + if (ObjectUtils.isEmpty(commandResult)) { + String errorMsg = "No OM package is found."; + log.error(errorMsg); + throw new OpsException(errorMsg); + } + + return commandResult; + } catch (IOException | InterruptedException e) { + log.error("Failed to obtain the OM package by command: {}. Error: {}", command, e.getMessage()); + throw new OpsException("Failed to obtain the OM package."); + } + } + + private void checkClusterStatus(Session ommSession, UpgradeContext upgradeContext) { + wsUtil.sendText(upgradeContext.getRetSession(), "CHECK_CLUSTER_STATUS"); + try { + String command = "gs_om -t status"; + JschResult jschResult = jschUtil.executeCommand(command, upgradeContext.getSepEnvFile(), ommSession, upgradeContext.getRetSession()); + if (0 != jschResult.getExitCode()) { + log.error("checkClusterStatus failed, exit code: {}, error message: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("checkClusterStatus failed"); + } + + String result = jschResult.getResult(); + if (!result.contains("cluster_state") || !result.substring(result.indexOf("cluster_state"), result.indexOf("\n",result.indexOf("cluster_state"))).contains("Normal")) { + log.error("cluster status:{}", result); + throw new OpsException("cluster status check fail"); + } + } catch (Exception e) { + log.error("checkClusterStatus failed", e); + throw new OpsException("checkClusterStatus failed"); + } + } + + private void enableStreamReplication(Session rootSession, UpgradeContext upgradeContext) { + wsUtil.sendText(upgradeContext.getRetSession(), "ENABLE_STREAM_REPLICATION"); + OpsClusterNodeEntity opsClusterNodeEntity = upgradeContext.getOpsClusterNodeEntity(); + gucCheck(opsClusterNodeEntity, rootSession, upgradeContext); + + gucReload(rootSession, upgradeContext); + } + + private void gucReload(Session rootSession, UpgradeContext upgradeContext) { + try { + String command = "gs_guc reload -I all -c \"enable_stream_replication=on\""; + JschResult jschResult = jschUtil.executeCommand(command,upgradeContext.getSepEnvFile(), rootSession, upgradeContext.getRetSession()); + if (0 != jschResult.getExitCode()) { + log.error("enableStreamReplication failed, exit code: {}, error message: {}", + jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("enableStreamReplication failed"); + } + } catch (Exception e) { + log.error("enableStreamReplication failed", e); + throw new OpsException("enableStreamReplication failed"); + } + + try { + String command = "gs_guc check -I all -c \"enable_stream_replication\""; + JschResult jschResult = jschUtil.executeCommand(command,upgradeContext.getSepEnvFile(), rootSession, upgradeContext.getRetSession()); + if (0 != jschResult.getExitCode()) { + log.error("enableStreamReplication failed, exit code: {}, error message: {}", + jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("enableStreamReplication failed"); + } + } catch (Exception e) { + log.error("enableStreamReplication failed", e); + throw new OpsException("enableStreamReplication failed"); + } + } + + private void gucCheck(OpsClusterNodeEntity opsClusterNodeEntity, Session rootSession, UpgradeContext upgradeContext) { + try { + String command = "gs_guc reload -D " + opsClusterNodeEntity.getDataPath() + " -c \"enable_stream_replication=on\""; + JschResult jschResult = jschUtil.executeCommand(command, upgradeContext.getSepEnvFile(), rootSession, upgradeContext.getRetSession()); + if (0 != jschResult.getExitCode()) { + log.error("enableStreamReplication failed, exit code: {}, error message: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("enableStreamReplication failed"); + } + } catch (Exception e) { + log.error("enableStreamReplication failed", e); + throw new OpsException("enableStreamReplication failed"); + } + + try { + String command = "gs_guc check -D " + + opsClusterNodeEntity.getDataPath() + + " -c \"enable_stream_replication\""; + JschResult jschResult = jschUtil.executeCommand(command, upgradeContext.getSepEnvFile(),rootSession, upgradeContext.getRetSession()); + if (0 != jschResult.getExitCode()) { + log.error("enableStreamReplication failed, exit code: {}, error message: {}", + jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("enableStreamReplication failed"); + } + } catch (Exception e) { + log.error("enableStreamReplication failed", e); + throw new OpsException("enableStreamReplication failed"); + } + } + + private void preInstall(String group, String pkgPath, EnterpriseInstallNodeConfig masterNodeConfig, String xmlConfigFullPath, Session rootSession, WsSession retSession, String rootPassword, String installUserPassword, String envPath) { + String installUsername = masterNodeConfig.getInstallUsername(); + String gsPreInstall = MessageFormat.format(SshCommandConstants.GS_PREINSTALL_INTERACTIVE, + pkgPath + "/script/", installUsername, + group, xmlConfigFullPath); + gsPreInstall = wrapperEnvSep(gsPreInstall, envPath); + try { + Map autoResponse = new HashMap<>(); + autoResponse.put("(yes/no)?", "yes"); + autoResponse.put("Please enter password for root\r\nPassword:", encryptionUtils.decrypt(rootPassword)); + autoResponse.put("Please enter password for current user[" + installUsername + "].\r\nPassword:", + encryptionUtils.decrypt(installUserPassword)); + // for compatibility with 5.1.0 + autoResponse.put("Please enter password for current user[root].\r\nPassword:", + encryptionUtils.decrypt(rootPassword)); + JschResult jschResult = jschUtil.executeCommand(gsPreInstall, rootSession, retSession, autoResponse); + if (0 != jschResult.getExitCode()) { + log.error("gs_preinstall failed, exit code: {}, error message: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("gs_preinstall failed"); + } + } catch (Exception e) { + log.error("gs_preinstall failed:", e); + throw new OpsException("gs_preinstall failed"); + } + } + + private void saveContext(InstallContext installContext) { + OpsClusterEntity opsClusterEntity = installContext.toOpsClusterEntity(); + opsClusterEntity.setDatabasePassword(encryptionUtils.encrypt(opsClusterEntity.getDatabasePassword())); + List opsClusterNodeEntities = installContext.getEnterpriseInstallConfig().toOpsClusterNodeEntityList(); + + opsClusterService.save(opsClusterEntity); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + opsClusterNodeEntity.setClusterId(opsClusterEntity.getClusterId()); + } + opsClusterNodeService.saveBatch(opsClusterNodeEntities); + } + @Override + public void uninstall(UnInstallContext unInstallContext) { + OpsClusterEntity opsClusterEntity = unInstallContext.getOpsClusterEntity(); + if (Objects.isNull(opsClusterEntity)) { + throw new OpsException("Uninstall cluster does not exist"); + } + + OpsClusterNodeEntity opsClusterNodeEntity = unInstallContext.getOpsClusterNodeEntityList() + .stream() + .filter(opsClusterNodeEntity1 -> opsClusterNodeEntity1.getClusterRole() == ClusterRoleEnum.MASTER) + .findFirst().orElseThrow(() -> new OpsException("master node not found")); + + HostInfoHolder hostInfoHolder = unInstallContext.getHostInfoHolders().stream().filter(hostInfoHolder1 -> hostInfoHolder1.getHostEntity().getHostId().equalsIgnoreCase(opsClusterNodeEntity.getHostId())).findFirst().orElseThrow(() -> new OpsException("host information not found")); + OpsHostEntity hostEntity = hostInfoHolder.getHostEntity(); + OpsHostUserEntity hostUserEntity = hostInfoHolder.getHostUserEntities() + .stream() + .filter(userInfo -> opsClusterNodeEntity.getInstallUserId() + .equals(userInfo.getHostUserId())).findFirst() + .orElseThrow(() -> new OpsException("No install user info user found")); + Session session = sshLogin(jschUtil, encryptionUtils, hostEntity, hostUserEntity); + + String uninstallCommand = "gs_uninstall --delete-data"; + WsSession retSession = unInstallContext.getRetSession(); + try { + JschResult jschResult = jschUtil.executeCommand(uninstallCommand, + opsClusterEntity.getEnvPath(), session, retSession); + if (0 != jschResult.getExitCode()) { + throw new OpsException("Uninstall error,exit code " + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("Uninstall error", e); + throw new OpsException("Uninstall error"); + } + + try { + Optional rootUserEntity = hostInfoHolder.getHostUserEntities() + .stream() + .filter(userEntity -> "root".equalsIgnoreCase(userEntity.getUsername())) + .findFirst(); + + CleanEnvClass cleanEnvParam = CleanEnvClass.builder() + .hostInfoHolders(unInstallContext.getHostInfoHolders()) + .hostEntity(hostEntity) + .rootUserEntity(rootUserEntity) + .hostUserEntity(hostUserEntity) + .retSession(retSession) + .installPackagePath(opsClusterEntity.getInstallPackagePath()) + .xmlConfigPath(opsClusterEntity.getXmlConfigPath()) + .envPath(opsClusterEntity.getEnvPath()) + .build(); + + cleanEnv(cleanEnvParam); + wsUtil.sendText(retSession, "\nENV_CLEAN_SUCCESS\n"); + } catch (Exception e) { + log.error("env clean fail:", e); + wsUtil.sendText(retSession, "\nENV_CLEAN_FAIL\n"); + } + + removeSoftLinkAll(unInstallContext); + removeContext(unInstallContext); + } + + @Data + @Builder + static class CleanEnvClass { + private List hostInfoHolders; + private OpsHostEntity hostEntity; + private Optional rootUserEntity; + private OpsHostUserEntity hostUserEntity; + private WsSession retSession; + private String installPackagePath; + private String xmlConfigPath; + private String envPath; + } + + private void cleanEnv(CleanEnvClass param) throws IOException, InterruptedException { + OpsHostEntity hostEntity = param.getHostEntity(); + Optional rootUserEntityOption = param.getRootUserEntity(); + OpsHostUserEntity hostUserEntity = param.getHostUserEntity(); + WsSession retSession = param.getRetSession(); + String installPackagePath = param.getInstallPackagePath(); + String xmlConfigPath = param.getXmlConfigPath(); + String envPath = param.getEnvPath(); + + OpsHostUserEntity rootUserEntity = rootUserEntityOption + .orElseThrow(() -> new OpsException("root user information not found")); + final Session rootSession = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), rootUserEntity.getUsername(), encryptionUtils.decrypt(rootUserEntity.getPassword())).orElseThrow(() -> new OpsException("The root user failed to establish a connection")); + + try { + String commandTemplate = "cd {0} && ./gs_postuninstall -U {1} -X {2}"; + + String command = MessageFormat.format(commandTemplate, installPackagePath + "/script", hostUserEntity.getUsername(), xmlConfigPath); + Map authResponse = new HashMap<>(); + authResponse.put("(yes/no)?", "yes"); + authResponse.put("Password:", encryptionUtils.decrypt(rootUserEntity.getPassword())); + final JschResult jschResult = jschUtil.executeCommand(envPath, command, rootSession, retSession, authResponse); + if (0 != jschResult.getExitCode()) { + log.error("clean env fail,exitCode:{},exitMsg:{}", jschResult.getExitCode(), jschResult.getExitCode()); + throw new OpsException("clean env fail"); + } + } finally { + rootSession.disconnect(); + } + + List hostInfoHolders = param.getHostInfoHolders(); + rmSsh(hostInfoHolders, retSession); + } + + private void rmSsh(List hostInfoHolders, + WsSession retSession) throws IOException, InterruptedException { + String delMutualTrustCommand = "rm -rf ~/.ssh"; + for (HostInfoHolder hostInfoHolder : hostInfoHolders) { + OpsHostEntity currentHost = hostInfoHolder.getHostEntity(); + List hostUserEntities = hostInfoHolder.getHostUserEntities(); + final OpsHostUserEntity rootUser = hostUserEntities.stream().filter(user -> "root".equalsIgnoreCase(user.getUsername())).findFirst().orElseThrow(() -> new OpsException("root user information not found")); + + final Session session = jschUtil.getSession(currentHost.getPublicIp(), currentHost.getPort(), rootUser.getUsername(), encryptionUtils.decrypt(rootUser.getPassword())).orElseThrow(() -> new OpsException("The root user failed to establish a connection")); + try { + final JschResult jschResult = jschUtil.executeCommand(delMutualTrustCommand, session, retSession); + if (0 != jschResult.getExitCode()) { + log.error("del MutualTrust fail,exitCode:{},exitMsg:{}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("del MutualTrust fail"); + } + } finally { + session.disconnect(); + } + } + } + + private void removeContext(UnInstallContext unInstallContext) { + OpsClusterEntity opsClusterEntity = unInstallContext.getOpsClusterEntity(); + opsClusterService.removeById(opsClusterEntity.getClusterId()); + + List opsClusterNodeEntityList = unInstallContext.getOpsClusterNodeEntityList(); + opsClusterNodeService.removeBatchByIds(opsClusterNodeEntityList.stream().map(OpsClusterNodeEntity::getClusterNodeId).collect(Collectors.toList())); + } + + @Override + public void restart(OpsClusterContext opsClusterContext) { + log.info("Start rebooting Enterprise Edition"); + List restartNodeIds = opsClusterContext.getOpNodeIds(); + if (CollUtil.isEmpty(restartNodeIds)) { + restartNodeIds = opsClusterContext.getOpsClusterNodeEntityList().stream().map(OpsClusterNodeEntity::getClusterNodeId).collect(Collectors.toList()); + } + + if (CollUtil.isEmpty(restartNodeIds)) { + log.error("No nodes to restart"); + } + + if (opsClusterContext.getOpsClusterNodeEntityList().size() == restartNodeIds.size()) { + doRestartCluster(opsClusterContext); + } else { + for (String restartNodeId : restartNodeIds) { + doRestartNode(restartNodeId, opsClusterContext); + } + } + } + + private void doRestartCluster(OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity startNodeEntity = opsClusterContext.getOpsClusterNodeEntityList().get(0); + WsSession retSession = opsClusterContext.getRetSession(); + Session ommUserSession = loginWithUser(jschUtil, encryptionUtils, opsClusterContext.getHostInfoHolders(), false, startNodeEntity.getHostId(), startNodeEntity.getInstallUserId()); + OmStatusModel omStatusModel = omStatus(jschUtil, ommUserSession, retSession, opsClusterContext.getOpsClusterEntity().getEnvPath()); + if (Objects.isNull(omStatusModel)) { + throw new OpsException("gs_om status fail"); + } + + String command = null; + if (omStatusModel.isInstallCm()) { + command = "cm_ctl stop && cm_ctl start"; + } else { + command = "gs_om -t restart"; + } + + try { + JschResult jschResult = jschUtil.executeCommand(command, opsClusterContext.getOpsClusterEntity().getEnvPath(), ommUserSession, retSession); + if (0 != jschResult.getExitCode()) { + throw new OpsException("startup error,exit code " + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("An exception occurred during startup", e); + throw new OpsException("An exception occurred during startup"); + } + } + + private void doRestartNode(String restartNodeId, OpsClusterContext opsClusterContext) { + doStopNode(restartNodeId, opsClusterContext); + doStartNode(restartNodeId, opsClusterContext); + } + + @Override + public void start(OpsClusterContext opsClusterContext) { + log.info("Get started with Enterprise Edition"); + List startNodeIds = opsClusterContext.getOpNodeIds(); + if (CollUtil.isEmpty(startNodeIds)) { + startNodeIds = opsClusterContext.getOpsClusterNodeEntityList().stream().map(OpsClusterNodeEntity::getClusterNodeId).collect(Collectors.toList()); + } + + if (CollUtil.isEmpty(startNodeIds)) { + log.error("No node to start"); + } + + if (opsClusterContext.getOpsClusterNodeEntityList().size() == startNodeIds.size()) { + doStartCluster(opsClusterContext); + } else { + for (String startNodeId : startNodeIds) { + doStartNode(startNodeId, opsClusterContext); + } + } + } + + private void doStartCluster(OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity startNodeEntity = opsClusterContext.getOpsClusterNodeEntityList().get(0); + WsSession retSession = opsClusterContext.getRetSession(); + Session ommUserSession = loginWithUser(jschUtil, encryptionUtils, opsClusterContext.getHostInfoHolders(), false, startNodeEntity.getHostId(), startNodeEntity.getInstallUserId()); + OmStatusModel omStatusModel = omStatus(jschUtil, ommUserSession, retSession, opsClusterContext.getOpsClusterEntity().getEnvPath()); + if (Objects.isNull(omStatusModel)) { + throw new OpsException("gs_om status fail"); + } + + String command = null; + if (omStatusModel.isInstallCm()) { + command = "cm_ctl start"; + } else { + command = "gs_om -t start"; + } + + try { + JschResult jschResult = jschUtil.executeCommand(command, opsClusterContext.getOpsClusterEntity().getEnvPath(), ommUserSession, retSession); + if (0 != jschResult.getExitCode()) { + throw new OpsException("startup error,exit code " + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("An exception occurred during startup", e); + throw new OpsException("An exception occurred during startup"); + } + } + + private void doStartNode(String startNodeId, OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity startNodeEntity = opsClusterContext.getOpsClusterNodeEntityList().stream().filter(opsClusterNodeEntity -> opsClusterNodeEntity.getClusterNodeId().equals(startNodeId)).findFirst().orElse(null); + if (Objects.isNull(startNodeEntity)) { + log.error("Boot node information not found:{}", startNodeId); + } + + WsSession retSession = opsClusterContext.getRetSession(); + String dataPath = startNodeEntity.getDataPath(); + log.info("Login to start user"); + Session ommUserSession = loginWithUser(jschUtil, encryptionUtils, opsClusterContext.getHostInfoHolders(), false, startNodeEntity.getHostId(), startNodeEntity.getInstallUserId()); + + OmStatusModel omStatusModel = omStatus(jschUtil, ommUserSession, retSession, opsClusterContext.getOpsClusterEntity().getEnvPath()); + if (Objects.isNull(omStatusModel)) { + throw new OpsException("gs_om status fail"); + } + + String command = null; + if (omStatusModel.isInstallCm()) { + String hostId = startNodeEntity.getHostId(); + OpsHostEntity opsHostEntity = opsClusterContext.getHostInfoHolders().stream().map(HostInfoHolder::getHostEntity).filter(host -> host.getHostId().equalsIgnoreCase(hostId)).findFirst().orElseThrow(() -> new OpsException("no host found")); + String hostname = opsHostEntity.getHostname(); + String nodeId = omStatusModel.getHostnameMapNodeId().get(hostname); + + if (StrUtil.isNotEmpty(nodeId)) { + command = "cm_ctl start -n " + nodeId + " -D " + dataPath; + } + } else { + command = MessageFormat.format(SshCommandConstants.LITE_START, dataPath); + } + + try { + JschResult jschResult = jschUtil.executeCommand(command, opsClusterContext.getOpsClusterEntity().getEnvPath(), ommUserSession, retSession); + if (0 != jschResult.getExitCode()) { + throw new OpsException("startup error,exit code " + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("An exception occurred during startup", e); + throw new OpsException("An exception occurred during startup"); + } + } + + @Override + public void stop(OpsClusterContext opsClusterContext) { + log.info("Start Stop Enterprise Edition"); + List stopNodeIds = opsClusterContext.getOpNodeIds(); + if (CollUtil.isEmpty(stopNodeIds)) { + stopNodeIds = opsClusterContext.getOpsClusterNodeEntityList().stream().map(OpsClusterNodeEntity::getClusterNodeId).collect(Collectors.toList()); + } + + if (CollUtil.isEmpty(stopNodeIds)) { + log.error("no node to stop"); + } + + if (opsClusterContext.getOpsClusterNodeEntityList().size() == stopNodeIds.size()) { + doStopCluster(opsClusterContext); + } else { + for (String stopNodeId : stopNodeIds) { + doStopNode(stopNodeId, opsClusterContext); + } + } + } + + private void doStopCluster(OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity startNodeEntity = opsClusterContext.getOpsClusterNodeEntityList().get(0); + WsSession retSession = opsClusterContext.getRetSession(); + Session ommUserSession = loginWithUser(jschUtil, encryptionUtils, opsClusterContext.getHostInfoHolders(), false, startNodeEntity.getHostId(), startNodeEntity.getInstallUserId()); + OmStatusModel omStatusModel = omStatus(jschUtil, ommUserSession, retSession, opsClusterContext.getOpsClusterEntity().getEnvPath()); + if (Objects.isNull(omStatusModel)) { + throw new OpsException("gs_om status fail"); + } + + String command = null; + if (omStatusModel.isInstallCm()) { + command = "cm_ctl stop"; + } else { + command = "gs_om -t stop"; + } + + try { + JschResult jschResult = jschUtil.executeCommand(command, opsClusterContext.getOpsClusterEntity().getEnvPath(), ommUserSession, retSession); + if (0 != jschResult.getExitCode()) { + throw new OpsException("startup error,exit code " + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("An exception occurred during startup", e); + throw new OpsException("An exception occurred during startup"); + } + } + + @Override + public void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity, List opsClusterNodeEntities, WdrScopeEnum scope, String dataPath) { + String checkCommand; + if (StrUtil.isEmpty(dataPath)) { + checkCommand = "gs_guc check -I all -c \"enable_wdr_snapshot\""; + } else { + checkCommand = "gs_guc check -D " + dataPath + " -c \"enable_wdr_snapshot\""; + } + try { + JschResult jschResult = jschUtil.executeCommand(checkCommand, session, clusterEntity.getEnvPath()); + + if (jschResult.getResult().contains("enable_wdr_snapshot=on")) { + return; + } + } catch (Exception e) { + log.error("Failed to set the enable_wdr_snapshot parameter", e); + throw new OpsException("Failed to set the enable_wdr_snapshot parameter"); + } + + String command; + if (StrUtil.isEmpty(dataPath)) { + command = "gs_guc reload -I all -c \"enable_wdr_snapshot=on\""; + } else { + command = "gs_guc reload -D " + dataPath + " -c \"enable_wdr_snapshot=on\""; + } + + try { + JschResult jschResult = jschUtil.executeCommand(command, session, clusterEntity.getEnvPath()); + if (0 != jschResult.getExitCode()) { + log.error("set enable_wdr_snapshot parameter failed, exit code: {}, error message: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to set the enable_wdr_snapshot parameter"); + } + + } catch (Exception e) { + log.error("Failed to set the enable_wdr_snapshot parameter", e); + throw new OpsException("Failed to set the enable_wdr_snapshot parameter"); + } + } + + private void doStopNode(String stopNodeId, OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity stopNodeEntity = opsClusterContext.getOpsClusterNodeEntityList().stream().filter(opsClusterNodeEntity -> opsClusterNodeEntity.getClusterNodeId().equals(stopNodeId)).findFirst().orElse(null); + if (Objects.isNull(stopNodeEntity)) { + log.error("No stop node information found:{}", stopNodeId); + } + + WsSession retSession = opsClusterContext.getRetSession(); + String dataPath = stopNodeEntity.getDataPath(); + log.info("login stop user"); + Session ommUserSession = loginWithUser(jschUtil, encryptionUtils, opsClusterContext.getHostInfoHolders(), false, stopNodeEntity.getHostId(), stopNodeEntity.getInstallUserId()); + + OmStatusModel omStatusModel = omStatus(jschUtil, ommUserSession, retSession, opsClusterContext.getOpsClusterEntity().getEnvPath()); + if (Objects.isNull(omStatusModel)) { + throw new OpsException("gs_om status fail"); + } + + String command = null; + if (omStatusModel.isInstallCm()) { + String hostId = stopNodeEntity.getHostId(); + OpsHostEntity opsHostEntity = opsClusterContext.getHostInfoHolders().stream().map(HostInfoHolder::getHostEntity).filter(host -> host.getHostId().equalsIgnoreCase(hostId)).findFirst().orElseThrow(() -> new OpsException("no host found")); + String hostname = opsHostEntity.getHostname(); + String nodeId = omStatusModel.getHostnameMapNodeId().get(hostname); + + if (StrUtil.isNotEmpty(nodeId)) { + command = "cm_ctl stop -n " + nodeId + " -D " + dataPath; + } + } else { + command = MessageFormat.format(SshCommandConstants.LITE_STOP, dataPath); + } + + try { + JschResult jschResult = jschUtil.executeCommand(command, opsClusterContext.getOpsClusterEntity().getEnvPath(), ommUserSession, retSession); + if (0 != jschResult.getExitCode()) { + throw new OpsException("stop error,exit code " + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("An exception occurred during the stop process", e); + throw new OpsException("stop exception"); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/LiteOpsProvider.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/LiteOpsProvider.java new file mode 100644 index 000000000..b7199d41b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/LiteOpsProvider.java @@ -0,0 +1,750 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * LiteOpsProvider.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/LiteOpsProvider.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.provider; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.jcraft.jsch.Session; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.HostInfoHolder; +import org.opengauss.admin.plugin.domain.model.ops.InstallContext; +import org.opengauss.admin.plugin.domain.model.ops.JschResult; +import org.opengauss.admin.plugin.domain.model.ops.LiteInstallConfig; +import org.opengauss.admin.plugin.domain.model.ops.OpsClusterContext; +import org.opengauss.admin.plugin.domain.model.ops.SshCommandConstants; +import org.opengauss.admin.plugin.domain.model.ops.UnInstallContext; +import org.opengauss.admin.plugin.domain.model.ops.WsSession; +import org.opengauss.admin.plugin.domain.model.ops.node.LiteInstallNodeConfig; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.enums.ops.WdrScopeEnum; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterService; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.plugin.utils.WsUtil; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author lhf + * @date 2022/8/12 09:26 + **/ +@Slf4j +@Service +public class LiteOpsProvider extends AbstractOpsProvider { + @Autowired + private IOpsClusterService opsClusterService; + @Autowired + private IOpsClusterNodeService opsClusterNodeService; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostUserFacade hostUserFacade; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostFacade hostFacade; + @Autowired + private JschUtil jschUtil; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + @Autowired + private WsUtil wsUtil; + + @Override + public OpenGaussVersionEnum version() { + return OpenGaussVersionEnum.LITE; + } + + @Override + public void install(InstallContext installContext) { + DeployTypeEnum deployType = installContext.getDeployType(); + if (DeployTypeEnum.CLUSTER == deployType) { + installCluster(installContext); + } else { + installSingleNode(installContext); + } + + wsUtil.sendText(installContext.getRetSession(), "CREATE_REMOTE_USER"); + OpsClusterContext opsClusterContext = new OpsClusterContext(); + OpsClusterEntity opsClusterEntity = installContext.toOpsClusterEntity(); + List opsClusterNodeEntities = installContext.getLiteInstallConfig().toOpsClusterNodeEntityList(); + opsClusterContext.setOpsClusterEntity(opsClusterEntity); + opsClusterContext.setOpsClusterNodeEntityList(opsClusterNodeEntities); + + createRemoteUser(installContext, opsClusterContext); + + wsUtil.sendText(installContext.getRetSession(), "SAVE_INSTALL_CONTEXT"); + saveContext(installContext); + wsUtil.sendText(installContext.getRetSession(), "FINISH"); + } + + private void createRemoteUser(InstallContext installContext, OpsClusterContext opsClusterContext) { + WsSession retSession = installContext.getRetSession(); + opsClusterContext.setRetSession(retSession); + opsClusterContext.setHostInfoHolders(installContext.getHostInfoHolders()); + + LiteInstallConfig liteInstallConfig = installContext.getLiteInstallConfig(); + List nodeConfigList = liteInstallConfig.getNodeConfigList(); + String databasePassword = liteInstallConfig.getDatabasePassword(); + Integer port = liteInstallConfig.getPort(); + for (LiteInstallNodeConfig liteInstallNodeConfig : nodeConfigList) { + String hostId = liteInstallNodeConfig.getHostId(); + String installUserId = liteInstallNodeConfig.getInstallUserId(); + String dataPath = liteInstallNodeConfig.getDataPath(); + + + Session session = loginWithUser(jschUtil, encryptionUtils, installContext.getHostInfoHolders(), false, hostId, installUserId); + String listenerAddress = MessageFormat.format(SshCommandConstants.LISTENER, dataPath); + try { + JschResult jschResult = jschUtil.executeCommand(listenerAddress, installContext.getEnvPath(), session, retSession); + if (0 != jschResult.getExitCode()) { + log.error("Failed to modify listening address, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to modify listening address"); + } + + } catch (Exception e) { + log.error("Failed to modify listening address", e); + throw new OpsException("Failed to modify listening address"); + } + + String hba = MessageFormat.format(SshCommandConstants.HBA, dataPath); + try { + JschResult jschResult = jschUtil.executeCommand(hba, installContext.getEnvPath(), session, retSession); + if (0 != jschResult.getExitCode()) { + log.error("Failed to modify host, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to modify host"); + } + + } catch (Exception e) { + log.error("Failed to modify host", e); + throw new OpsException("Failed to modify host"); + } + + String clientLoginOpenGauss = MessageFormat.format(SshCommandConstants.LOGIN, String.valueOf(port)); + try { + Map response = new HashMap<>(); + String createUser = MessageFormat.format( + "CREATE USER gaussdb WITH MONADMIN AUDITADMIN SYSADMIN PASSWORD \"{0}\";\\q", + encryptionUtils.decrypt(databasePassword)); + response.put("openGauss=#", createUser); + JschResult jschResult = jschUtil.executeCommand(installContext.getEnvPath(), clientLoginOpenGauss, session, retSession, response); + if (0 != jschResult.getExitCode()) { + log.error("Failed to create user, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to create user"); + } + + } catch (Exception e) { + log.error("Failed to create user", e); + throw new OpsException("Failed to create user"); + } + } + + opsClusterContext.setHostInfoHolders(installContext.getHostInfoHolders()); + restart(opsClusterContext); + } + + private void installSingleNode(InstallContext installContext) { + wsUtil.sendText(installContext.getRetSession(), "START_SINGLE"); + log.info("Start installing Single Node Lite"); + WsSession retSession = installContext.getRetSession(); + + LiteInstallNodeConfig installNodeConfig = installContext.getLiteInstallConfig().getNodeConfigList().get(0); + + String installPath = preparePath(installNodeConfig.getInstallPath()); + String pkgPath = preparePath(installContext.getLiteInstallConfig().getInstallPackagePath()); + String dataPath = preparePath(installNodeConfig.getDataPath()); + String hostId = installNodeConfig.getHostId(); + String databasePassword = installContext.getLiteInstallConfig().getDatabasePassword(); + Integer port = installContext.getLiteInstallConfig().getPort(); + + String installUserId = installNodeConfig.getInstallUserId(); + String installUserName = null; + if (StrUtil.isEmpty(installUserId)) { + wsUtil.sendText(installContext.getRetSession(), "CREATE_INSTALL_USER"); + OpsHostUserEntity installUser = createInstallUser(installContext, hostId); + installUserId = installUser.getHostUserId(); + installUserName = installUser.getUsername(); + } else { + final String uid = installUserId; + OpsHostUserEntity installUser = installContext.getHostInfoHolders() + .stream() + .filter(hostInfoHolder -> hostId.equals(hostInfoHolder.getHostEntity().getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("host information does not exist")) + .getHostUserEntities() + .stream() + .filter(userInfo -> uid.equals(userInfo.getHostUserId())) + .findFirst() + .orElseThrow(() -> new OpsException("Installation user information does not exist")); + installUserName = installUser.getUsername(); + } + + if (StrUtil.isEmpty(installUserId)) { + throw new OpsException("Installation user ID does not exist"); + } + + Session installUserSession = beforeInstall(jschUtil, encryptionUtils, installContext, pkgPath, dataPath, pkgPath, hostId, installUserId, installUserName, "-xvf"); + + try { + log.info("perform installation"); + // install + String command = MessageFormat.format(SshCommandConstants.LITE_SINGLE_INSTALL, databasePassword, pkgPath, dataPath, installPath, String.valueOf(port)); + + command = wrapperLiteEnvSep(command, installContext.getEnvPath()); + + wsUtil.sendText(installContext.getRetSession(), "START_EXE_INSTALL_COMMAND"); + try { + JschResult jschResult = jschUtil.executeCommand(command, installUserSession, retSession); + if (0 != jschResult.getExitCode()) { + log.error("Lite single node installation log: {}", jschResult.getResult()); + throw new OpsException("Lite single node installation failed with exit code:" + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("Lite single node installation fails", e); + throw new OpsException("Lite single node installation fails"); + } + wsUtil.sendText(installContext.getRetSession(), "END_EXE_INSTALL_COMMAND"); + + log.info("The installation is complete"); + } finally { + if (Objects.nonNull(installUserSession) && installUserSession.isConnected()) { + installUserSession.disconnect(); + } + } + } + + private OpsHostUserEntity createInstallUser(InstallContext installContext, String hostId) { + OpsHostEntity hostEntity = hostFacade.getById(hostId); + OpsHostUserEntity hostUserEntity = hostUserFacade.getOmmUserByHostId(hostId); + if (Objects.nonNull(hostUserEntity)) { + return hostUserEntity; + } + + String rootPassword = installContext.getLiteInstallConfig().getNodeConfigList().stream().filter(nodeConfig -> nodeConfig.getHostId().equalsIgnoreCase(hostId)).map(LiteInstallNodeConfig::getRootPassword).findFirst().orElseThrow(() -> new OpsException("root password not found")); + Session rootSession = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), "root", encryptionUtils.decrypt(rootPassword)).orElseThrow(() -> new OpsException("with the host[" + hostEntity.getPublicIp() + "]Failed to establish session")); + try { + try { + JschResult jschResult = jschUtil.executeCommand(SshCommandConstants.OMM_USER, rootSession, installContext.getRetSession()); + if (0 != jschResult.getExitCode()) { + log.error("Failed to query omm user, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to query omm user"); + } + + if (StrUtil.isNotEmpty(jschResult.getResult()) && "1".equals(jschResult.getResult())) { + throw new OpsException("Please enter the omm user login password"); + } + } catch (Exception e) { + log.error("Failed to query omm user", e); + throw new OpsException("Failed to query omm user"); + } + + String password = StrUtil.uuid(); + + try { + JschResult jschResult = null; + try { + jschResult = jschUtil.executeCommand(SshCommandConstants.CREATE_OMM_USER, rootSession, installContext.getRetSession()); + } catch (InterruptedException e) { + throw new OpsException("thread is interrupted"); + } + if (0 != jschResult.getExitCode()) { + log.error("Failed to create omm user, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to create omm user"); + } + } catch (IOException e) { + log.error("Failed to create omm user", e); + throw new OpsException("Failed to create omm user"); + } + + try { + Map autoResponse = new HashMap<>(); + autoResponse.put("password:", password); + autoResponse.put("password:", password); + JschResult jschResult = null; + try { + jschResult = jschUtil.executeCommand(SshCommandConstants.CHANGE_OMM_PASSWORD, rootSession, installContext.getRetSession(), autoResponse); + } catch (InterruptedException e) { + throw new OpsException("thread is interrupted"); + } + if (0 != jschResult.getExitCode()) { + log.error("Failed to modify omm user password, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to modify omm user password"); + } + } catch (IOException e) { + log.error("Failed to modify omm user password", e); + throw new OpsException("Failed to modify omm user password"); + } + + hostUserEntity = new OpsHostUserEntity(); + hostUserEntity.setHostId(hostId); + hostUserEntity.setUsername("omm"); + hostUserEntity.setPassword(encryptionUtils.encrypt(password)); + + hostUserFacade.save(hostUserEntity); + + List hostInfoHolders = installContext.getHostInfoHolders(); + if (Objects.isNull(hostInfoHolders)) { + hostInfoHolders = new ArrayList<>(); + installContext.setHostInfoHolders(hostInfoHolders); + } + + HostInfoHolder hostHolder = hostInfoHolders.stream().filter(holder -> holder.getHostEntity().getHostId().equals(hostId)).findFirst().orElseThrow(() -> new OpsException("host information not found")); + List hostUserEntities = hostHolder.getHostUserEntities(); + if (Objects.isNull(hostUserEntities)) { + hostUserEntities = new ArrayList<>(); + hostHolder.setHostUserEntities(hostUserEntities); + } + + hostUserEntities.add(hostUserEntity); + return hostUserEntity; + } finally { + if (Objects.nonNull(rootSession) && rootSession.isConnected()) { + rootSession.disconnect(); + } + } + } + + private void installCluster(InstallContext installContext) { + wsUtil.sendText(installContext.getRetSession(), "START_CLUSTER"); + log.info("Start installing the Lite cluster"); + List installNodeConfigList = installContext.getLiteInstallConfig().getNodeConfigList(); + + LiteInstallNodeConfig masterNodeConfig = installNodeConfigList.stream().filter(nodeConfig -> ClusterRoleEnum.MASTER == nodeConfig.getClusterRole()).findFirst().orElseThrow(() -> new OpsException("Master node configuration not found")); + LiteInstallNodeConfig slaveNodeConfig = installNodeConfigList.stream().filter(nodeConfig -> ClusterRoleEnum.SLAVE == nodeConfig.getClusterRole()).findFirst().orElseThrow(() -> new OpsException("Master node configuration not found")); + + installMaster(masterNodeConfig, slaveNodeConfig, installContext); + installSlave(masterNodeConfig, slaveNodeConfig, installContext); + + log.info("Cluster installation is complete"); + } + + private void installSlave(LiteInstallNodeConfig masterNodeConfig, LiteInstallNodeConfig slaveNodeConfig, InstallContext installContext) { + wsUtil.sendText(installContext.getRetSession(), "START_SLAVE"); + log.info("Start installing the standby node"); + String installPath = preparePath(slaveNodeConfig.getInstallPath()); + String pkgPath = preparePath(installContext.getLiteInstallConfig().getInstallPackagePath()); + String dataPath = preparePath(slaveNodeConfig.getDataPath()); + String masterHostId = masterNodeConfig.getHostId(); + String slaveHostId = slaveNodeConfig.getHostId(); + String hostId = slaveNodeConfig.getHostId(); + String databasePassword = installContext.getLiteInstallConfig().getDatabasePassword(); + HostInfoHolder masterHostInfo = installContext.getHostInfoHolders().stream().filter(hostInfoHolder -> masterHostId.equals(hostInfoHolder.getHostEntity().getHostId())).findFirst().orElseThrow(() -> new OpsException("Master node configuration not found")); + HostInfoHolder slaveHostInfo = installContext.getHostInfoHolders().stream().filter(hostInfoHolder -> slaveHostId.equals(hostInfoHolder.getHostEntity().getHostId())).findFirst().orElseThrow(() -> new OpsException("The host information of the standby node does not exist")); + + String installUserId = slaveNodeConfig.getInstallUserId(); + String installUserName = null; + if (StrUtil.isEmpty(installUserId)) { + wsUtil.sendText(installContext.getRetSession(), "CREATE_INSTALL_USER"); + OpsHostUserEntity installUser = createInstallUser(installContext, hostId); + installUserId = installUser.getHostUserId(); + installUserName = installUser.getUsername(); + } else { + final String uid = installUserId; + OpsHostUserEntity installUser = installContext.getHostInfoHolders() + .stream() + .filter(hostInfoHolder -> hostId.equals(hostInfoHolder.getHostEntity().getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("host information does not exist")) + .getHostUserEntities() + .stream() + .filter(userInfo -> uid.equals(userInfo.getHostUserId())) + .findFirst() + .orElseThrow(() -> new OpsException("Installation user information does not exist")); + installUserName = installUser.getUsername(); + } + + if (StrUtil.isEmpty(installUserId)) { + throw new OpsException("Installation user ID does not exist"); + } + + slaveNodeConfig.setInstallUserId(installUserId); + Session installUserSession = beforeInstall(jschUtil, encryptionUtils, installContext, pkgPath, dataPath, pkgPath, slaveHostId, installUserId, installUserName, "-xvf"); + + try { + log.info("perform installation"); + // install + String command = MessageFormat.format(SshCommandConstants.LITE_SLAVE_INSTALL, databasePassword, pkgPath, dataPath, installPath, + slaveHostInfo.getHostEntity().getPrivateIp(), + String.valueOf(installContext.getLiteInstallConfig().getPort() + 1), + masterHostInfo.getHostEntity().getPrivateIp(), + String.valueOf(installContext.getLiteInstallConfig().getPort() + 1), + String.valueOf(installContext.getLiteInstallConfig().getPort())); + command = wrapperLiteEnvSep(command, installContext.getEnvPath()); + wsUtil.sendText(installContext.getRetSession(), "START_EXE_INSTALL_COMMAND"); + try { + JschResult jschResult = jschUtil.executeCommand(command, installUserSession, installContext.getRetSession()); + if (0 != jschResult.getExitCode()) { + log.error("Lite{}node installation log:{}", ClusterRoleEnum.SLAVE, jschResult.getResult()); + throw new OpsException("light version" + ClusterRoleEnum.SLAVE + "Node installation failed with exit code:" + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("light version" + ClusterRoleEnum.SLAVE + "Node installation failed", e); + throw new OpsException("light version" + ClusterRoleEnum.SLAVE + "Node installation failed"); + } + wsUtil.sendText(installContext.getRetSession(), "END_EXE_INSTALL_COMMAND"); + log.info("Standby node installation is complete"); + } finally { + if (Objects.nonNull(installUserSession) && installUserSession.isConnected()) { + installUserSession.disconnect(); + } + } + } + + private void installMaster(LiteInstallNodeConfig masterNodeConfig, LiteInstallNodeConfig slaveNodeConfig, InstallContext installContext) { + wsUtil.sendText(installContext.getRetSession(), "START_MASTER"); + log.info("Start installing the master node"); + String installPath = preparePath(masterNodeConfig.getInstallPath()); + String pkgPath = preparePath(installContext.getLiteInstallConfig().getInstallPackagePath()); + String dataPath = preparePath(masterNodeConfig.getDataPath()); + String masterHostId = masterNodeConfig.getHostId(); + String slaveHostId = slaveNodeConfig.getHostId(); + String hostId = masterNodeConfig.getHostId(); + String databasePassword = installContext.getLiteInstallConfig().getDatabasePassword(); + HostInfoHolder masterHostInfo = installContext.getHostInfoHolders().stream().filter(hostInfoHolder -> masterHostId.equals(hostInfoHolder.getHostEntity().getHostId())).findFirst().orElseThrow(() -> new OpsException("Master node host information does not exist")); + HostInfoHolder slaveHostInfo = installContext.getHostInfoHolders().stream().filter(hostInfoHolder -> slaveHostId.equals(hostInfoHolder.getHostEntity().getHostId())).findFirst().orElseThrow(() -> new OpsException("The host information of the standby node does not exist")); + + String installUserId = masterNodeConfig.getInstallUserId(); + String installUserName = null; + if (StrUtil.isEmpty(installUserId)) { + wsUtil.sendText(installContext.getRetSession(), "CREATE_INSTALL_USER"); + OpsHostUserEntity installUser = createInstallUser(installContext, hostId); + installUserId = installUser.getHostUserId(); + installUserName = installUser.getUsername(); + } else { + final String uid = installUserId; + OpsHostUserEntity installUser = installContext.getHostInfoHolders() + .stream() + .filter(hostInfoHolder -> hostId.equals(hostInfoHolder.getHostEntity().getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("host information does not exist")) + .getHostUserEntities() + .stream() + .filter(userInfo -> uid.equals(userInfo.getHostUserId())) + .findFirst() + .orElseThrow(() -> new OpsException("Installation user information does not exist")); + installUserName = installUser.getUsername(); + } + + if (StrUtil.isEmpty(installUserId)) { + throw new OpsException("Installation user ID does not exist"); + } + + masterNodeConfig.setInstallUserId(installUserId); + + Session installUserSession = beforeInstall(jschUtil, encryptionUtils, installContext, pkgPath, dataPath, pkgPath, masterHostId, installUserId, installUserName, "-xvf"); + try { + log.info("perform installation"); + // install + String command = MessageFormat.format(SshCommandConstants.LITE_MASTER_INSTALL, databasePassword, pkgPath, dataPath, installPath, + masterHostInfo.getHostEntity().getPrivateIp(), + String.valueOf(installContext.getLiteInstallConfig().getPort() + 1), + slaveHostInfo.getHostEntity().getPrivateIp(), + String.valueOf(installContext.getLiteInstallConfig().getPort() + 1), + String.valueOf(installContext.getLiteInstallConfig().getPort())); + command = wrapperLiteEnvSep(command, installContext.getEnvPath()); + wsUtil.sendText(installContext.getRetSession(), "START_EXE_INSTALL_COMMAND"); + try { + JschResult jschResult = jschUtil.executeCommand(command, installUserSession, installContext.getRetSession()); + if (0 != jschResult.getExitCode()) { + log.error("Lite{}node installation log:{}", ClusterRoleEnum.MASTER, jschResult.getResult()); + throw new OpsException("light version" + ClusterRoleEnum.MASTER + "Node installation failed with exit code:" + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("light version" + ClusterRoleEnum.MASTER + "Node installation failed", e); + throw new OpsException("light version" + ClusterRoleEnum.MASTER + "Node installation failed"); + } + wsUtil.sendText(installContext.getRetSession(), "END_EXE_INSTALL_COMMAND"); + log.info("Master node installation is complete"); + } finally { + if (Objects.nonNull(installUserSession) && installUserSession.isConnected()) { + installUserSession.disconnect(); + } + } + } + + private String judgePkgPath(String installPath) { + while (installPath.endsWith("/")) { + installPath = installPath.substring(0, installPath.length() - 1); + } + + return installPath.substring(0, installPath.lastIndexOf("/")); + } + + private void saveContext(InstallContext installContext) { + OpsClusterEntity opsClusterEntity = installContext.toOpsClusterEntity(); + List opsClusterNodeEntities = installContext.getLiteInstallConfig().toOpsClusterNodeEntityList(); + + opsClusterService.save(opsClusterEntity); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + opsClusterNodeEntity.setClusterId(opsClusterEntity.getClusterId()); + } + opsClusterNodeService.saveBatch(opsClusterNodeEntities); + } + + @Override + public void uninstall(UnInstallContext unInstallContext) { + OpsClusterEntity opsClusterEntity = unInstallContext.getOpsClusterEntity(); + if (Objects.isNull(opsClusterEntity)) { + throw new OpsException("Uninstall cluster does not exist"); + } + + DeployTypeEnum deployType = opsClusterEntity.getDeployType(); + if (DeployTypeEnum.CLUSTER == deployType) { + unInstallCluster(unInstallContext); + } else { + unInstallSingleNode(unInstallContext); + } + } + + @Override + public void restart(OpsClusterContext opsClusterContext) { + log.info("Start restarting the lite version"); + List restartNodeIds = opsClusterContext.getOpNodeIds(); + if (CollUtil.isEmpty(restartNodeIds)) { + restartNodeIds = opsClusterContext.getOpsClusterNodeEntityList().stream().map(OpsClusterNodeEntity::getClusterNodeId).collect(Collectors.toList()); + } + + if (CollUtil.isEmpty(restartNodeIds)) { + log.error("No nodes to restart"); + } + + for (String restartNodeId : restartNodeIds) { + doRestartNode(restartNodeId, opsClusterContext); + } + } + + @Override + public void start(OpsClusterContext opsClusterContext) { + log.info("start up lite version"); + List startNodeIds = opsClusterContext.getOpNodeIds(); + if (CollUtil.isEmpty(startNodeIds)) { + startNodeIds = opsClusterContext.getOpsClusterNodeEntityList().stream().map(OpsClusterNodeEntity::getClusterNodeId).collect(Collectors.toList()); + } + + if (CollUtil.isEmpty(startNodeIds)) { + log.error("No node to start"); + } + + for (String startNodeId : startNodeIds) { + doStartNode(startNodeId, opsClusterContext); + } + } + + @Override + public void stop(OpsClusterContext opsClusterContext) { + log.info("start stop lite version"); + List stopNodeIds = opsClusterContext.getOpNodeIds(); + if (CollUtil.isEmpty(stopNodeIds)) { + stopNodeIds = opsClusterContext.getOpsClusterNodeEntityList() + .stream() + .map(OpsClusterNodeEntity::getClusterNodeId) + .collect(Collectors.toList()); + } + + if (CollUtil.isEmpty(stopNodeIds)) { + log.error("no node to stop"); + } + + for (String stopNodeId : stopNodeIds) { + doStopNode(stopNodeId, opsClusterContext); + } + } + + @Override + public void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity, List opsClusterNodeEntities, WdrScopeEnum scope, String dataPath) { + if (StrUtil.isEmpty(dataPath)) { + dataPath = opsClusterNodeEntities.stream().filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER).findFirst().orElseThrow(() -> new OpsException("Master node configuration not found")).getDataPath(); + } + + String checkCommand = "gs_guc check -D " + dataPath + " -c \"enable_wdr_snapshot\""; + try { + JschResult jschResult = jschUtil.executeCommand(checkCommand, session, clusterEntity.getEnvPath()); + if (jschResult.getResult().contains("enable_wdr_snapshot=on")) { + return; + } + } catch (Exception e) { + log.error("Failed to set the enable_wdr_snapshot parameter", e); + throw new OpsException("Failed to set the enable_wdr_snapshot parameter"); + } + + String command = "gs_guc reload -D " + dataPath + " -c \"enable_wdr_snapshot=on\""; + + try { + JschResult jschResult = jschUtil.executeCommand(command, session, clusterEntity.getEnvPath()); + if (0 != jschResult.getExitCode()) { + log.error("set enable_wdr_snapshot parameter failed, exit code: {}, error message: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to set the enable_wdr_snapshot parameter"); + } + } catch (Exception e) { + log.error("Failed to set the enable_wdr_snapshot parameter", e); + throw new OpsException("Failed to set the enable_wdr_snapshot parameter"); + } + } + + private void doStopNode(String stopNodeId, OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity stopNodeEntity = opsClusterContext.getOpsClusterNodeEntityList().stream().filter(opsClusterNodeEntity -> opsClusterNodeEntity.getClusterNodeId().equals(stopNodeId)).findFirst().orElse(null); + if (Objects.isNull(stopNodeEntity)) { + log.error("Stop node info not found: {}", stopNodeId); + } + + WsSession retSession = opsClusterContext.getRetSession(); + String dataPath = stopNodeEntity.getDataPath(); + log.info("login stop user"); + Session restartUserSession = loginWithUser(jschUtil, encryptionUtils, opsClusterContext.getHostInfoHolders(), false, stopNodeEntity.getHostId(), stopNodeEntity.getInstallUserId()); + + String restartCommand = MessageFormat.format(SshCommandConstants.LITE_STOP, dataPath); + try { + JschResult jschResult = jschUtil.executeCommand(restartCommand, opsClusterContext.getOpsClusterEntity().getEnvPath(), restartUserSession, retSession); + if (0 != jschResult.getExitCode()) { + throw new OpsException("stop error, exit code" + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("An exception occurred during the stop process", e); + throw new OpsException("stop exception"); + } + } + + private void doStartNode(String startNodeId, OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity startNodeEntity = opsClusterContext.getOpsClusterNodeEntityList().stream().filter(opsClusterNodeEntity -> opsClusterNodeEntity.getClusterNodeId().equals(startNodeId)).findFirst().orElse(null); + if (Objects.isNull(startNodeEntity)) { + log.error("Boot node information not found:{}", startNodeId); + } + + WsSession retSession = opsClusterContext.getRetSession(); + String dataPath = startNodeEntity.getDataPath(); + log.info("Login to start user"); + Session restartUserSession = loginWithUser(jschUtil, encryptionUtils, opsClusterContext.getHostInfoHolders(), false, startNodeEntity.getHostId(), startNodeEntity.getInstallUserId()); + + String restartCommand = MessageFormat.format(SshCommandConstants.LITE_START, dataPath); + try { + JschResult jschResult = jschUtil.executeCommand(restartCommand, opsClusterContext.getOpsClusterEntity().getEnvPath(), restartUserSession, retSession); + if (0 != jschResult.getExitCode()) { + throw new OpsException("startup error,exit code " + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("startup error", e); + throw new OpsException("startup error"); + } + } + + private void doRestartNode(String restartNodeId, OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity restartNodeEntity = opsClusterContext.getOpsClusterNodeEntityList().stream().filter(opsClusterNodeEntity -> opsClusterNodeEntity.getClusterNodeId().equals(restartNodeId)).findFirst().orElse(null); + if (Objects.isNull(restartNodeEntity)) { + log.error("No restart node information found:{}", restartNodeId); + } + + WsSession retSession = opsClusterContext.getRetSession(); + String dataPath = restartNodeEntity.getDataPath(); + log.info("login restart user"); + Session restartUserSession = loginWithUser(jschUtil, encryptionUtils, opsClusterContext.getHostInfoHolders(), false, restartNodeEntity.getHostId(), restartNodeEntity.getInstallUserId()); + + String restartCommand = MessageFormat.format(SshCommandConstants.LITE_RESTART, dataPath); + try { + JschResult jschResult = jschUtil.executeCommand(restartCommand, opsClusterContext.getOpsClusterEntity().getEnvPath(), restartUserSession, retSession); + if (0 != jschResult.getExitCode()) { + throw new OpsException("restart error,exit code " + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("restart error", e); + throw new OpsException("restart error"); + } + } + + private void unInstallSingleNode(UnInstallContext unInstallContext) { + WsSession retSession = unInstallContext.getRetSession(); + + OpsClusterNodeEntity opsClusterNodeEntity = unInstallContext.getOpsClusterNodeEntityList().get(0); + + HostInfoHolder hostInfoHolder = unInstallContext.getHostInfoHolders().get(0); + OpsHostEntity hostEntity = hostInfoHolder.getHostEntity(); + OpsHostUserEntity hostUserEntity = hostInfoHolder.getHostUserEntities().stream().filter(userInfo -> opsClusterNodeEntity.getInstallUserId().equals(userInfo.getHostUserId())).findFirst().orElseThrow(() -> new OpsException("Installation user info user not found")); + Session session = sshLogin(jschUtil, encryptionUtils, hostEntity, hostUserEntity); + + doUnInstall(retSession, session, unInstallContext.getOpsClusterEntity().getInstallPackagePath(), unInstallContext.getOpsClusterEntity().getEnvPath()); + removeContext(unInstallContext); + } + + private void removeContext(UnInstallContext unInstallContext) { + OpsClusterEntity opsClusterEntity = unInstallContext.getOpsClusterEntity(); + opsClusterService.removeById(opsClusterEntity.getClusterId()); + + List opsClusterNodeEntityList = unInstallContext.getOpsClusterNodeEntityList(); + opsClusterNodeService.removeBatchByIds(opsClusterNodeEntityList.stream().map(OpsClusterNodeEntity::getClusterNodeId).collect(Collectors.toList())); + } + + private void doUnInstall(WsSession retSession, Session session, String path, String envPath) { + String command = MessageFormat.format(SshCommandConstants.LITE_UNINSTALL, path); + + try { + JschResult result = null; + try { + result = jschUtil.executeCommand(command, envPath, session, retSession); + } catch (InterruptedException e) { + throw new OpsException("thread is interrupted"); + } + if (0 != result.getExitCode()) { + log.error("Lite version uninstall failed, exit code {}, log content: {}", + result.getExitCode(), result.getResult()); + throw new OpsException("Uninstallation of Lite version failed"); + } + } catch (IOException e) { + log.error("Uninstallation of Lite version failed", e); + throw new OpsException("Uninstallation of Lite version failed"); + } + } + + private void unInstallCluster(UnInstallContext unInstallContext) { + WsSession retSession = unInstallContext.getRetSession(); + List opsClusterNodeEntityList = unInstallContext.getOpsClusterNodeEntityList(); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntityList) { + HostInfoHolder hostInfoHolder = unInstallContext.getHostInfoHolders().stream().filter(hostHolder -> hostHolder.getHostEntity().getHostId().equals(opsClusterNodeEntity.getHostId())).findFirst().orElseThrow(() -> new OpsException("Host information not found")); + OpsHostEntity hostEntity = hostInfoHolder.getHostEntity(); + OpsHostUserEntity hostUserEntity = hostInfoHolder.getHostUserEntities().stream().filter(userInfo -> opsClusterNodeEntity.getInstallUserId().equals(userInfo.getHostUserId())).findFirst().orElseThrow(() -> new OpsException("Installation user info user not found")); + Session session = sshLogin(jschUtil, encryptionUtils, hostEntity, hostUserEntity); + + doUnInstall(retSession, session, unInstallContext.getOpsClusterEntity().getInstallPackagePath(), unInstallContext.getOpsClusterEntity().getEnvPath()); + } + + removeContext(unInstallContext); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/MinimaListOpsProvider.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/MinimaListOpsProvider.java new file mode 100644 index 000000000..beeb50d0d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/MinimaListOpsProvider.java @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * MinimaListOpsProvider.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/MinimaListOpsProvider.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.provider; + +import cn.hutool.core.util.StrUtil; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.*; +import org.opengauss.admin.plugin.domain.model.ops.node.MinimalistInstallNodeConfig; +import org.opengauss.admin.plugin.enums.ops.*; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterService; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.plugin.utils.WsUtil; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import com.jcraft.jsch.Session; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author lhf + * @date 2022/8/12 09:25 + **/ +@Slf4j +@Service +public class MinimaListOpsProvider extends AbstractOpsProvider { + + @Autowired + private IOpsClusterService opsClusterService; + @Autowired + private IOpsClusterNodeService opsClusterNodeService; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostFacade hostFacade; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostUserFacade hostUserFacade; + @Autowired + private WsUtil wsUtil; + @Autowired + private JschUtil jschUtil; + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + + @Override + public OpenGaussVersionEnum version() { + return OpenGaussVersionEnum.MINIMAL_LIST; + } + + @Override + public void install(InstallContext installContext) { + log.info("Start installing the minimalist version"); + WsSession retSession = installContext.getRetSession(); + + MinimalistInstallNodeConfig nodeConfig = getSingleInstallNodeConfig(installContext); + String hostId = nodeConfig.getHostId(); + String installPath = preparePath(nodeConfig.getInstallPath()); + String dataPath = installPath + "data"; + + String installUserId = nodeConfig.getInstallUserId(); + String installUserName = null; + if (StrUtil.isEmpty(installUserId)) { + wsUtil.sendText(installContext.getRetSession(), "CREATE_INSTALL_USER"); + OpsHostUserEntity installUser = createInstallUser(installContext, hostId); + installUserId = installUser.getHostUserId(); + installUserName = installUser.getUsername(); + } else { + String uid = installUserId; + OpsHostUserEntity installUser = installContext.getHostInfoHolders() + .stream() + .filter(hostInfoHolder -> hostId.equals(hostInfoHolder.getHostEntity().getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("host information does not exist")) + .getHostUserEntities() + .stream() + .filter(userInfo -> uid.equals(userInfo.getHostUserId())) + .findFirst() + .orElseThrow(() -> new OpsException("Installation user information does not exist")); + installUserName = installUser.getUsername(); + } + + if (StrUtil.isEmpty(installUserId)){ + throw new OpsException("Installation user ID does not exist"); + } + + nodeConfig.setInstallUserId(installUserId); + + wsUtil.sendText(installContext.getRetSession(), "BEFORE INSTALL"); + Session installUserSession = beforeInstall( + jschUtil, encryptionUtils, installContext, installPath, dataPath, + installContext.getMinimalistInstallConfig().getInstallPackagePath(), + hostId, installUserId, installUserName, "-xvf"); + try { + log.info("perform installation"); + // install + wsUtil.sendText(installContext.getRetSession(), "INSTALL"); + doInstall(installUserSession, retSession, installPath, installContext, nodeConfig); + + OpsClusterContext opsClusterContext = new OpsClusterContext(); + OpsClusterEntity opsClusterEntity = installContext.toOpsClusterEntity(); + List opsClusterNodeEntities = installContext.getMinimalistInstallConfig() + .toOpsClusterNodeEntityList(opsClusterEntity); + + opsClusterContext.setOpsClusterEntity(opsClusterEntity); + opsClusterContext.setOpsClusterNodeEntityList(opsClusterNodeEntities); + + wsUtil.sendText(installContext.getRetSession(), "CREATE REMOTE USER"); + wsUtil.sendText(installContext.getRetSession(),"CREATE_REMOTE_USER"); + createRemoteUser(installUserSession, retSession, installContext, dataPath, opsClusterContext); + + wsUtil.sendText(installContext.getRetSession(), "SAVE CONTEXT"); + wsUtil.sendText(installContext.getRetSession(),"SAVE_INSTALL_CONTEXT"); + saveContext(opsClusterEntity, opsClusterNodeEntities); + wsUtil.sendText(installContext.getRetSession(),"FINISH"); + log.info("The installation is complete"); + }finally { + if (Objects.nonNull(installUserSession) && installUserSession.isConnected()){ + installUserSession.disconnect(); + } + } + } + + private void createRemoteUser(Session installUserSession, WsSession retSession, InstallContext installContext, String dataPath, OpsClusterContext opsClusterContext) { + if (DeployTypeEnum.CLUSTER == installContext.getDeployType()) { + String masterListenerAddress = MessageFormat.format(SshCommandConstants.LISTENER, dataPath + "/master"); + try { + JschResult jschResult = jschUtil.executeCommand(masterListenerAddress, installUserSession, retSession); + if (0 != jschResult.getExitCode()) { + log.error("Failed to modify listening address, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to modify listening address"); + } + + } catch (Exception e) { + log.error("Failed to modify listening address", e); + throw new OpsException("Failed to modify listening address"); + } + + String masterHba = MessageFormat.format(SshCommandConstants.HBA, dataPath + "/master"); + try { + JschResult jschResult = jschUtil.executeCommand(masterHba, installUserSession, retSession); + if (0 != jschResult.getExitCode()) { + log.error("Failed to modify host, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to modify host"); + } + + } catch (Exception e) { + log.error("Failed to modify host", e); + throw new OpsException("Failed to modify host"); + } + + String slaveListenerAddress = MessageFormat.format(SshCommandConstants.LISTENER, dataPath + "/slave"); + try { + JschResult jschResult = jschUtil.executeCommand(slaveListenerAddress, installUserSession, retSession); + if (0 != jschResult.getExitCode()) { + log.error("Failed to modify listening address, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to modify listening address"); + } + + } catch (Exception e) { + log.error("Failed to modify listening address", e); + throw new OpsException("Failed to modify listening address"); + } + + String slaveHba = MessageFormat.format(SshCommandConstants.HBA, dataPath + "/slave"); + try { + JschResult jschResult = jschUtil.executeCommand(slaveHba, installUserSession, retSession); + if (0 != jschResult.getExitCode()) { + log.error("Failed to modify host, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to modify host"); + } + + } catch (Exception e) { + log.error("Failed to modify host", e); + throw new OpsException("Failed to modify host"); + } + + } else { + String listenerAddress = MessageFormat.format(SshCommandConstants.LISTENER, dataPath + "/single_node"); + try { + JschResult jschResult = jschUtil.executeCommand(listenerAddress, installUserSession, retSession); + if (0 != jschResult.getExitCode()) { + log.error("Failed to modify listening address, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to modify listening address"); + } + + } catch (Exception e) { + log.error("Failed to modify listening address", e); + throw new OpsException("Failed to modify listening address"); + } + + String hba = MessageFormat.format(SshCommandConstants.HBA, dataPath + "/single_node"); + try { + JschResult jschResult = jschUtil.executeCommand(hba, installUserSession, retSession); + if (0 != jschResult.getExitCode()) { + log.error("Failed to modify host, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to modify host"); + } + + } catch (Exception e) { + log.error("Failed to modify host", e); + throw new OpsException("Failed to modify host"); + } + } + + opsClusterContext.setRetSession(retSession); + opsClusterContext.setHostInfoHolders(installContext.getHostInfoHolders()); + restart(opsClusterContext); + + String clientLoginOpenGauss = MessageFormat.format(SshCommandConstants.LOGIN, String.valueOf(installContext.getMinimalistInstallConfig().getPort())); + try { + Map response = new HashMap<>(); + String createUser = MessageFormat.format( + "CREATE USER gaussdb WITH MONADMIN AUDITADMIN SYSADMIN PASSWORD \"{0}\";\\q", + encryptionUtils.decrypt(installContext.getMinimalistInstallConfig().getDatabasePassword())); + response.put("openGauss=#", createUser); + JschResult jschResult = jschUtil.executeCommand(clientLoginOpenGauss, installUserSession, retSession, response); + if (0 != jschResult.getExitCode()) { + log.error("Failed to create user, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to create user"); + } + + } catch (Exception e) { + log.error("Failed to create user", e); + throw new OpsException("Failed to create user"); + } + } + + private OpsHostUserEntity createInstallUser(InstallContext installContext, String hostId) { + OpsHostEntity hostEntity = hostFacade.getById(hostId); + OpsHostUserEntity hostUserEntity = hostUserFacade.getOmmUserByHostId(hostId); + if (Objects.nonNull(hostUserEntity)) { + return hostUserEntity; + } + + String rootPassword = null; + + for (HostInfoHolder hostInfoHolder : installContext.getHostInfoHolders()) { + final OpsHostEntity currentHost = hostInfoHolder.getHostEntity(); + if (hostId.equalsIgnoreCase(currentHost.getHostId())){ + rootPassword = hostInfoHolder.getHostUserEntities().stream().filter(user->"root".equalsIgnoreCase(user.getUsername())).map(OpsHostUserEntity::getPassword).findFirst().orElseThrow(()->new OpsException("root password not found")); + } + } + + if (StrUtil.isEmpty(rootPassword)){ + throw new OpsException("root password not found"); + } + + Session rootSession = jschUtil.getSession(hostEntity.getPublicIp(), hostEntity.getPort(), "root", encryptionUtils.decrypt(rootPassword)).orElseThrow(() -> new OpsException("with the host[" + hostEntity.getPublicIp() + "]Failed to establish session")); + try { + String ommUserName = null; + for (int i = 0; i < Integer.MAX_VALUE; i++) { + + if (i==0){ + ommUserName = "omm"; + }else { + ommUserName = "omm"+i; + } + try { + String command = "cat /etc/passwd | awk -F \":\" \"{print $1}\"|grep "+ommUserName+" | wc -l"; + JschResult jschResult = jschUtil.executeCommand(command, rootSession, installContext.getRetSession()); + if (0 != jschResult.getExitCode()) { + log.error("Failed to query omm user, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to query omm user"); + } + + log.info("user count {},{}",ommUserName,jschResult.getResult()); + if (StrUtil.isNotEmpty(jschResult.getResult()) && 0 == Integer.parseInt(jschResult.getResult())) { + log.info("ommUserName:{}",ommUserName); + break; + } + } catch (Exception e) { + log.error("Failed to query omm user", e); + } + } + + + String password = StrUtil.uuid(); + + try { + JschResult jschResult = null; + try { + String command = "useradd "+ommUserName; + jschResult = jschUtil.executeCommand(command, rootSession, installContext.getRetSession()); + } catch (InterruptedException e) { + throw new OpsException("thread is interrupted"); + } + if (0 != jschResult.getExitCode()) { + log.error("Failed to create omm user, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to create omm user"); + } + } catch (IOException e) { + log.error("Failed to create omm user", e); + throw new OpsException("Failed to create omm user"); + } + + try { + Map autoResponse = new HashMap<>(); + autoResponse.put("password:", password); + autoResponse.put("password:", password); + JschResult jschResult = null; + try { + String command = "passwd "+ommUserName; + jschResult = jschUtil.executeCommand(command, rootSession, installContext.getRetSession(), autoResponse); + } catch (InterruptedException e) { + throw new OpsException("thread interruption"); + } + if (0 != jschResult.getExitCode()) { + log.error("Failed to modify omm user password, exit code: {}, log: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to modify omm user password"); + } + } catch (IOException e) { + log.error("Failed to modify omm user password", e); + throw new OpsException("Failed to modify omm user password"); + } + + hostUserEntity = new OpsHostUserEntity(); + hostUserEntity.setHostId(hostId); + hostUserEntity.setUsername(ommUserName); + hostUserEntity.setPassword(encryptionUtils.encrypt(password)); + + hostUserFacade.save(hostUserEntity); + + List hostInfoHolders = installContext.getHostInfoHolders(); + if (Objects.isNull(hostInfoHolders)) { + hostInfoHolders = new ArrayList<>(); + installContext.setHostInfoHolders(hostInfoHolders); + } + + HostInfoHolder hostHolder = hostInfoHolders.stream().filter(holder -> holder.getHostEntity().getHostId().equals(hostId)).findFirst().orElseThrow(() -> new OpsException("host information not found")); + List hostUserEntities = hostHolder.getHostUserEntities(); + if (Objects.isNull(hostUserEntities)){ + hostUserEntities = new ArrayList<>(); + hostHolder.setHostUserEntities(hostUserEntities); + } + + hostUserEntities.add(hostUserEntity); + return hostUserEntity; + }finally { + if (Objects.nonNull(rootSession) && rootSession.isConnected()){ + rootSession.disconnect(); + } + } + } + + @Override + public void uninstall(UnInstallContext unInstallContext) { + log.info("Start uninstalling the minimalist version"); + WsSession retSession = unInstallContext.getRetSession(); + + HostInfoHolder hostInfoHolder = unInstallContext.getHostInfoHolders().get(0); + if (Objects.isNull(hostInfoHolder)) { + throw new OpsException("host information does not exist"); + } + + OpsClusterNodeEntity clusterNode = unInstallContext.getOpsClusterNodeEntityList().get(0); + if (Objects.isNull(clusterNode)) { + throw new OpsException("Cluster node information does not exist"); + } + + log.info("Login to uninstall user"); + Session unInstallUserSession = loginWithUser(jschUtil,encryptionUtils,unInstallContext.getHostInfoHolders(), false, clusterNode.getHostId(), clusterNode.getInstallUserId()); + // uninstall + log.info("perform uninstall"); + doUnInstall(unInstallUserSession, retSession, unInstallContext); + log.info("Uninstall is complete"); + removeContext(unInstallContext); + + log.info("Uninstall complete"); + } + + @Override + public void restart(OpsClusterContext opsClusterContext) { + log.info("Start restarting the minimalist version"); + WsSession retSession = opsClusterContext.getRetSession(); + + HostInfoHolder hostInfoHolder = opsClusterContext.getHostInfoHolders().get(0); + if (Objects.isNull(hostInfoHolder)) { + throw new OpsException("host information does not exist"); + } + + OpsClusterNodeEntity clusterNode = opsClusterContext.getOpsClusterNodeEntityList().get(0); + if (Objects.isNull(clusterNode)) { + throw new OpsException("Cluster node information does not exist"); + } + + log.info("login restart user"); + Session restartUserSession = loginWithUser(jschUtil,encryptionUtils,opsClusterContext.getHostInfoHolders(), false, clusterNode.getHostId(), clusterNode.getInstallUserId()); + log.info("perform a restart"); + doRestart(restartUserSession, retSession, opsClusterContext); + log.info("restart complete"); + } + + @Override + public void start(OpsClusterContext opsClusterContext) { + log.info("Start the minimalist version"); + WsSession retSession = opsClusterContext.getRetSession(); + + HostInfoHolder hostInfoHolder = opsClusterContext.getHostInfoHolders().get(0); + if (Objects.isNull(hostInfoHolder)) { + throw new OpsException("host information does not exist"); + } + + OpsClusterNodeEntity clusterNode = opsClusterContext.getOpsClusterNodeEntityList().get(0); + if (Objects.isNull(clusterNode)) { + throw new OpsException("Cluster node information does not exist"); + } + + log.info("Login to start user"); + Session startUserSession = loginWithUser(jschUtil,encryptionUtils,opsClusterContext.getHostInfoHolders(), false, clusterNode.getHostId(), clusterNode.getInstallUserId()); + log.info("execute start"); + doStart(startUserSession, retSession, opsClusterContext); + log.info("Startup complete"); + } + + private void doStart(Session startUserSession, WsSession retSession, OpsClusterContext opsClusterContext) { + OpsClusterEntity opsClusterEntity = opsClusterContext.getOpsClusterEntity(); + List opsClusterNodeEntities = opsClusterContext.getOpsClusterNodeEntityList(); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + String dataPath = opsClusterNodeEntity.getDataPath(); + String startCommand = MessageFormat.format(SshCommandConstants.LITE_START, dataPath); + try { + JschResult jschResult = jschUtil.executeCommand(startCommand, opsClusterEntity.getEnvPath(), + startUserSession, retSession); + if (0 != jschResult.getExitCode()) { + throw new OpsException("startup error,exit code " + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("startup error", e); + throw new OpsException("startup error"); + } + } + } + + @Override + public void stop(OpsClusterContext opsClusterContext) { + log.info("start stop minimalist"); + WsSession retSession = opsClusterContext.getRetSession(); + + HostInfoHolder hostInfoHolder = opsClusterContext.getHostInfoHolders().get(0); + if (Objects.isNull(hostInfoHolder)) { + throw new OpsException("host information does not exist"); + } + + OpsClusterNodeEntity clusterNode = opsClusterContext.getOpsClusterNodeEntityList().get(0); + if (Objects.isNull(clusterNode)) { + throw new OpsException("Cluster node information does not exist"); + } + + log.info("login stop user"); + Session stopUserSession = loginWithUser(jschUtil,encryptionUtils,opsClusterContext.getHostInfoHolders(), false, clusterNode.getHostId(), clusterNode.getInstallUserId()); + log.info("execution stops"); + doStop(stopUserSession, retSession, opsClusterContext); + log.info("stop complete"); + } + + @Override + public void enableWdrSnapshot(Session session, OpsClusterEntity clusterEntity, List opsClusterNodeEntities, WdrScopeEnum scope, String dataPath) { + if (StrUtil.isEmpty(dataPath)) { + dataPath = opsClusterNodeEntities.stream().filter(node -> node.getClusterRole() == ClusterRoleEnum.MASTER).findFirst().orElseThrow(() -> new OpsException("Master node configuration not found")).getDataPath(); + + if (clusterEntity.getDeployType() == DeployTypeEnum.CLUSTER) { + dataPath = dataPath + "/master"; + } else { + dataPath = dataPath + "/single_node"; + } + } + + String checkCommand = "gs_guc check -D " + dataPath + " -c \"enable_wdr_snapshot\""; + try { + JschResult jschResult = jschUtil.executeCommand(checkCommand, session, clusterEntity.getEnvPath()); + if (jschResult.getResult().contains("enable_wdr_snapshot=on")){ + return; + } + } catch (Exception e) { + String msg = "Failed to set the enable_wdr_snapshot parameter"; + if (e instanceof OpsException) { + msg = e.getMessage(); + } + log.error(msg, e); + throw new OpsException(msg); + } + + String command = "gs_guc reload -D " + dataPath + " -c \"enable_wdr_snapshot=on\""; + + try { + JschResult jschResult = jschUtil.executeCommand(command, session, clusterEntity.getEnvPath()); + if (0 != jschResult.getExitCode()) { + log.error("set enable_wdr_snapshot parameter failed, exit code: {}, error message: {}", jschResult.getExitCode(), jschResult.getResult()); + throw new OpsException("Failed to query the enable_wdr_snapshot parameter"); + } + } catch (Exception e) { + String msg = "Failed to set the enable_wdr_snapshot parameter"; + if (e instanceof OpsException) { + msg = e.getMessage(); + } + log.error(msg, e); + throw new OpsException(msg); + } + } + + private void doStop(Session stopUserSession, WsSession retSession, OpsClusterContext opsClusterContext) { + OpsClusterEntity opsClusterEntity = opsClusterContext.getOpsClusterEntity(); + List opsClusterNodeEntities = opsClusterContext.getOpsClusterNodeEntityList(); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + String dataPath = opsClusterNodeEntity.getDataPath(); + String stopCommand = MessageFormat.format(SshCommandConstants.LITE_STOP, dataPath); + try { + JschResult jschResult = jschUtil.executeCommand(stopCommand,opsClusterEntity.getEnvPath(), stopUserSession, retSession); + if (0 != jschResult.getExitCode()) { + throw new OpsException("stop error,exit code " + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("stop error", e); + throw new OpsException("stop error"); + } + } + } + + private void doRestart(Session restartUserSession, WsSession retSession, OpsClusterContext opsClusterContext) { + OpsClusterEntity opsClusterEntity = opsClusterContext.getOpsClusterEntity(); + List opsClusterNodeEntities = opsClusterContext.getOpsClusterNodeEntityList(); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + String dataPath = opsClusterNodeEntity.getDataPath(); + String restartCommand = MessageFormat.format(SshCommandConstants.LITE_RESTART, dataPath); + try { + JschResult jschResult = jschUtil.executeCommand(restartCommand, opsClusterEntity.getEnvPath(), + restartUserSession, retSession); + if (0 != jschResult.getExitCode()) { + throw new OpsException("restart error,exit code " + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("restart error", e); + throw new OpsException("restart error"); + } + } + } + + private void doUnInstall(Session unInstallUserSession, WsSession retSession, UnInstallContext unInstallContext) { + OpsClusterEntity opsClusterEntity = unInstallContext.getOpsClusterEntity(); + List opsClusterNodeEntities = unInstallContext.getOpsClusterNodeEntityList(); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + String dataPath = opsClusterNodeEntity.getDataPath(); + // single uninstall + String command = MessageFormat.format(SshCommandConstants.LITE_STOP, dataPath); + try { + JschResult jschResult = jschUtil.executeCommand(command, opsClusterEntity.getEnvPath(), unInstallUserSession, retSession); + if (0 != jschResult.getExitCode()) { + throw new OpsException("Uninstall error,exit code " + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("Uninstall error", e); + throw new OpsException("Uninstall error"); + } + + try { + JschResult jschResult = jschUtil.executeCommand("rm -rf " + dataPath, unInstallUserSession, retSession); + if (0 != jschResult.getExitCode()) { + log.error("Failed to clean data, response code: {}, response message: {}", jschResult.getExitCode(), jschResult.getResult()); + } + } catch (Exception e) { + log.error("Failed to clean data", e); + } + } + } + + private void removeContext(UnInstallContext unInstallContext) { + OpsClusterEntity opsClusterEntity = unInstallContext.getOpsClusterEntity(); + opsClusterService.removeById(opsClusterEntity.getClusterId()); + + List opsClusterNodeEntityList = unInstallContext.getOpsClusterNodeEntityList(); + opsClusterNodeService.removeBatchByIds(opsClusterNodeEntityList.stream().map(OpsClusterNodeEntity::getClusterNodeId).collect(Collectors.toList())); + } + + private void saveContext(OpsClusterEntity opsClusterEntity, List opsClusterNodeEntities) { + opsClusterEntity.setDatabasePassword(encryptionUtils.encrypt(opsClusterEntity.getDatabasePassword())); + opsClusterService.save(opsClusterEntity); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + opsClusterNodeEntity.setClusterId(opsClusterEntity.getClusterId()); + } + opsClusterNodeService.saveBatch(opsClusterNodeEntities); + } + + private void doInstall(Session installUserSession, WsSession retSession, String pkgPath, InstallContext installContext, MinimalistInstallNodeConfig nodeConfig) { + String installCommandTemplate = getInstallCommandTemplate(installContext.getDeployType()); + + Integer port = installContext.getMinimalistInstallConfig().getPort(); + String databasePassword = installContext.getMinimalistInstallConfig().getDatabasePassword(); + + String command = MessageFormat.format(installCommandTemplate, pkgPath + "simpleInstall/", databasePassword, String.valueOf(port)); + + HashMap autoResponse = new HashMap<>(); + autoResponse.put("Would you like to create a demo database (yes/no)? ", (nodeConfig.getIsInstallDemoDatabase() ? "yes" : "no")); + + wsUtil.sendText(installContext.getRetSession(),"START_EXE_INSTALL_COMMAND"); + try { + JschResult jschResult = jschUtil.executeCommand(command, installUserSession, retSession, autoResponse); + if (0 != jschResult.getExitCode()) { + log.error("install output:{}", jschResult.getResult()); + throw new OpsException("installation error,exit code " + jschResult.getExitCode()); + } + } catch (Exception e) { + log.error("installation error", e); + throw new OpsException("installation error"); + } + wsUtil.sendText(installContext.getRetSession(),"END_EXE_INSTALL_COMMAND"); + } + + private void backOld(Session rootSession, String targetPath, WsSession retSession) { + String command = MessageFormat.format(SshCommandConstants.MV, targetPath); + try { + try { + jschUtil.executeCommand(command, rootSession, retSession); + } catch (InterruptedException e) { + throw new OpsException("thread is interrupted"); + } + } catch (IOException e) { + log.error("Backup directory failed:", e); + throw new OpsException("Backup directory failed"); + } + } + + private MinimalistInstallNodeConfig getSingleInstallNodeConfig(InstallContext installContext) { + return installContext + .getMinimalistInstallConfig() + .getNodeConfigList() + .stream() + .filter(config -> ClusterRoleEnum.MASTER == config.getClusterRole()) + .findFirst() + .orElseThrow(() -> new OpsException("Master node configuration not found")); + } + + private String getInstallCommandTemplate(DeployTypeEnum deployType) { + if (DeployTypeEnum.SINGLE_NODE == deployType) { + return SshCommandConstants.MINIMAL_LIST_SINGLE_INSTALL; + } + + if (DeployTypeEnum.CLUSTER == deployType) { + return SshCommandConstants.MINIMAL_LIST_CLUSTER_INSTALL; + } + + throw new OpsException("Unsupported deployment method:" + deployType); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/ProviderManager.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/ProviderManager.java new file mode 100644 index 000000000..1e90303c9 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/ProviderManager.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * TaskLiteProvider.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/ProviderManager.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.provider; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.UploadInfo; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.enums.ops.OpsClusterTaskStatusEnum; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterTaskNodeEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackageManagerEntity; +import org.opengauss.admin.plugin.domain.model.ops.HostInfoHolder; +import org.opengauss.admin.plugin.domain.model.ops.InstallContext; +import org.opengauss.admin.plugin.domain.model.ops.RetBuffer; +import org.opengauss.admin.plugin.domain.model.ops.SshCommandConstants; +import org.opengauss.admin.plugin.domain.model.ops.cache.TaskManager; +import org.opengauss.admin.plugin.enums.ops.InstallModeEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussSupportOSEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.factory.OperateLogFactory; +import org.opengauss.admin.plugin.service.ops.IOpsClusterTaskNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterTaskService; +import org.opengauss.admin.plugin.service.ops.IOpsPackageManagerV2Service; +import org.opengauss.admin.plugin.service.ops.impl.ClusterTaskProviderManager; +import org.opengauss.admin.plugin.service.ops.impl.function.InstallContextConfigFunction; +import org.opengauss.admin.plugin.service.ops.impl.function.InstallContextConfigFunctionInstance; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import jakarta.annotation.Resource; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * ProviderManager + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@Slf4j +@Service +public class ProviderManager { + private static final String TASK_PROVIDER_NAME = "business_InstallProvider_"; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostFacade hostFacade; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostUserFacade hostUserFacade; + private BlockingQueue taskQueue = new LinkedBlockingQueue<>(); + @Resource + private InstallContextConfigFunctionInstance installContextConfigFunctionInstance; + @Resource + private ClusterTaskProviderManager clusterTaskProviderManager; + @Resource + private IOpsPackageManagerV2Service opsPackageManagerV2Service; + + @Resource + @Lazy + private IOpsClusterTaskNodeService opsClusterTaskNodeService; + @Resource + @Lazy + private IOpsClusterTaskService opsClusterTaskService; + + /** + * add task to queue + * + * @param taskId taskId + */ + public void addClusterTask(String taskId) { + taskQueue.add(taskId); + } + + /** + * current task queue is not empty, and the current provider task count is less than 5 + * + * @return boolean + */ + public boolean hasLimitResource() { + return !taskQueue.isEmpty() && TaskManager.executeCount(TASK_PROVIDER_NAME) < 5; + } + + /** + * Retrieve the local path corresponding to the saved binary installation package for the specified packageId + * + * @param packageId packageId + * @return package local storage path + */ + private String getPackageLocalPath(String packageId) { + OpsPackageManagerEntity packageEntity = opsPackageManagerV2Service.getById(packageId); + Assert.isTrue(Objects.nonNull(packageEntity), "package " + packageId + " not exist"); + UploadInfo packagePath = packageEntity.getPackagePath(); + return packagePath.getRealPath() + "/" + packagePath.getName(); + } + + /** + * Check whether there is a task waiting in the queue + * + * @return boolean + */ + public boolean hasTaskWaiting() { + return !taskQueue.isEmpty(); + } + + /** + * Retrieve the taskId from the queue + * + * @return task + */ + public String poll() { + return taskQueue.poll(); + } + + /** + * Retrieve the businessId based on the clusterId + * + * @param clusterId clusterId + * @return businessId + */ + public String getBusinessId(String clusterId) { + return "business_InstallProvider_" + clusterId; + } + + /** + * populate the installContext + * + * @param task task + * @return installContext + */ + public InstallContext populateInstallContext(OpsClusterTaskEntity task) { + InstallContext installContext = new InstallContext(); + try { + installContext.setClusterId(task.getClusterId()); + installContext.setOpenGaussVersion(task.getVersion()); + installContext.setOpenGaussVersionNum(task.getVersionNum()); + installContext.setInstallMode(InstallModeEnum.OFF_LINE); + installContext.setInstallPackagePath(task.getInstallPackagePath()); + OpsHostUserEntity installUser = hostUserFacade.getById(task.getHostUserId()); + installContext.setInstallUsername(installUser.getUsername()); + installContext.setInstallPackageLocalPath(getPackageLocalPath(task.getPackageId())); + installContext.setEnvPath(task.getEnvPath()); + installContext.setDeployType(task.getDeployType()); + String clusterId = installContext.getClusterId(); + List taskNodeList = opsClusterTaskNodeService.listByClusterTaskId(clusterId); + populateInstallEnvPath(installContext); + populateInstallContextHostHolder(installContext, taskNodeList); + populateInstallContextOs(installContext); + populateInstallContextInstallConfig(installContext, task, taskNodeList); + } catch (Exception e) { + throw new OpsException("Failed to populate install context , error : " + e.getMessage()); + } + return installContext; + } + + private void populateInstallEnvPath(InstallContext installContext) { + String envPath = installContext.getEnvPath(); + if (StrUtil.isEmpty(envPath)) { + envPath = SshCommandConstants.DEFAULT_ENV_BASHRC; + installContext.setEnvPath(envPath); + } + if (SshCommandConstants.DEFAULT_ENV_BASHRC.equals(envPath)) { + installContext.setEnvAbsolutePath("/home/" + installContext.getInstallUsername() + "/.bashrc"); + } else { + installContext.setEnvAbsolutePath(envPath); + } + } + + private void populateInstallContextInstallConfig(InstallContext installContext, OpsClusterTaskEntity task, + List taskNodeList) { + OpenGaussVersionEnum version = installContext.getOpenGaussVersion(); + InstallContextConfigFunction installContextConfig = + installContextConfigFunctionInstance.getInstallContextConfig(version); + installContextConfig.accept(installContext, task, taskNodeList); + } + + /** + * Fill in the os information in the installation context according to the context HostHolder . + * must be called after populateInstallContextHostHolder + * + * @param installContext installation context + */ + private void populateInstallContextOs(InstallContext installContext) { + List hostInfoHolders = installContext.getHostInfoHolders(); + Assert.isTrue(CollUtil.isNotEmpty(hostInfoHolders), "HostInfoHolder is empty"); + OpsHostEntity host = hostInfoHolders.get(0).getHostEntity(); + Assert.isTrue(Objects.nonNull(host), "HostEntity is null"); + installContext.setOs(OpenGaussSupportOSEnum.of(host.getOs(), host.getOsVersion(), host.getCpuArch())); + } + + private void populateInstallContextHostHolder(InstallContext installContext, + List entityNodeList) { + List hostInfoHolders = new ArrayList<>(); + Set hostIdSet = new HashSet<>(); + entityNodeList.forEach(node -> { + Assert.isTrue(!hostIdSet.contains(node.getHostId()), "HostId is repeated"); + hostIdSet.add(node.getHostId()); + OpsHostEntity host = hostFacade.getById(node.getHostId()); + List hostUserList = hostUserFacade.listHostUserByHostId(node.getHostId()); + HostInfoHolder hostInfoHolder = new HostInfoHolder(); + hostInfoHolder.setHostEntity(host); + hostInfoHolder.setHostUserEntities(hostUserList); + hostInfoHolders.add(hostInfoHolder); + }); + installContext.setHostInfoHolders(hostInfoHolders); + } + + /** + * do install task base on installContext + * + * @param installContext installContext + */ + public void doInstall(InstallContext installContext) { + String clusterId = installContext.getClusterId(); + RetBuffer retBuffer = installContext.getRetBuffer(); + boolean isInstallSucc = true; + String remark = ""; + try { + retBuffer.sendText("START_EXECUTE_INSTALL_PROVIDER"); + clusterTaskProviderManager + .provider(installContext.getOpenGaussVersion()) + .orElseThrow(() -> new OpsException("The current version does not support")) + .install(installContext); + retBuffer.sendText("FINAL_EXECUTE_EXIT_CODE:0"); + OperateLogFactory.operateInstall(clusterId, retBuffer.getRetBuffer()); + } catch (Exception e) { + retBuffer.sendText(e.toString()); + retBuffer.sendText("FINAL_EXECUTE_EXIT_CODE:-1"); + OperateLogFactory.operateInstall(clusterId, retBuffer.getRetBuffer()); + isInstallSucc = false; + remark = e.getMessage(); + log.error("doInstall error:", e); + throw new OpsException(e.getMessage()); + } finally { + if (!isInstallSucc) { + opsClusterTaskService.updateClusterTaskStatus(clusterId, OpsClusterTaskStatusEnum.FAILED, remark); + } else { + opsClusterTaskService.updateClusterTaskStatus(clusterId, OpsClusterTaskStatusEnum.SUCCESS, + "Install completed"); + } + } + } + + /** + * force remove install task + * + * @param taskId taskId + */ + public void remove(String taskId) { + taskQueue.remove(taskId); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/TaskEnterpriseProvider.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/TaskEnterpriseProvider.java new file mode 100644 index 000000000..bbc05962e --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/TaskEnterpriseProvider.java @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AbstractTaskProvider.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/AbstractTaskProvider.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.provider; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.jcraft.jsch.Session; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.*; +import org.opengauss.admin.plugin.domain.model.ops.node.EnterpriseInstallNodeConfig; +import org.opengauss.admin.plugin.enums.ops.*; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterService; +import org.opengauss.admin.plugin.service.ops.impl.function.GenerateClusterConfigXmlInstance; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import jakarta.annotation.Resource; +import java.text.MessageFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + * TaskEnterpriseProvider + * + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Slf4j +@Service +public class TaskEnterpriseProvider extends AbstractTaskProvider { + @Resource + private IOpsClusterService opsClusterService; + @Resource + private IOpsClusterNodeService opsClusterNodeService; + @Resource + private GenerateClusterConfigXmlInstance generateClusterConfigXmlInstance; + + @Override + public OpenGaussVersionEnum version() { + return OpenGaussVersionEnum.ENTERPRISE; + } + + @Override + public void install(InstallContext installContext) { + log.info("Start installing Enterprise Edition"); + boolean isInstallSucc = true; + try { + installContext.getRetBuffer().sendText("Start installing Enterprise Edition"); + doInstall(installContext); + + OpsClusterContext opsClusterContext = new OpsClusterContext(); + OpsClusterEntity opsClusterEntity = installContext.toOpsClusterEntity(); + List opsClusterNodeEntities = + installContext.getEnterpriseInstallConfig().toOpsClusterNodeEntityList(); + opsClusterContext.setOpsClusterEntity(opsClusterEntity); + opsClusterContext.setOpsClusterNodeEntityList(opsClusterNodeEntities); + + sendOperateLog(installContext, "CREATE_REMOTE_USER"); + createEnterpriseRemoteUser(installContext, opsClusterContext); + + sendOperateLog(installContext, "SAVE_INSTALL_CONTEXT"); + saveContext(installContext); + sendOperateLog(installContext, "FINISH"); + log.info("The installation is complete"); + } catch (OpsException e) { + log.error("install failed:", e); + isInstallSucc = false; + throw new OpsException("The installation is failed " + e.getMessage()); + } finally { + if (!isInstallSucc) { + cleanResource(installContext); + } + } + } + + protected String prepareCleanClusterDir(InstallContext installContext) { + String delCmd = ""; + try { + // remove intall path software + String installPackagePathPath = installContext.getEnterpriseInstallConfig().getInstallPackagePath(); + log.info("install package path : {}", installPackagePathPath); + + String delInstallPath = MessageFormat.format(SshCommandConstants.DEL_FILE, installPackagePathPath + "/*"); + log.info("delete install package path : {}", delInstallPath); + String installDataPath = installContext.getEnterpriseInstallConfig().getInstallPath(); + String delAppPath = MessageFormat.format(SshCommandConstants.DEL_FILE, installDataPath); + log.info("delete app path : {}", delAppPath); + String delTmpPath = MessageFormat.format(SshCommandConstants.DEL_FILE, + installContext.getEnterpriseInstallConfig().getTmpPath()); + log.info("delete tmp path : {}", delTmpPath); + String delOmPath = MessageFormat.format(SshCommandConstants.DEL_FILE, + installContext.getEnterpriseInstallConfig().getOmToolsPath()); + log.info("delete om path : {}", delOmPath); + delCmd = delInstallPath + " || echo \"delInstallPath failed\"; " + + delAppPath + " || echo \"delAppPath failed\"; " + + delTmpPath + " || echo \"delTmpPath failed\"; " + + delOmPath + " || echo \"delOmPath failed\"; "; + if (installContext.getEnterpriseInstallConfig().getDatabaseKernelArch() == + DatabaseKernelArch.SHARING_STORAGE) { + String dssHome = installContext.getEnterpriseInstallConfig(). + getSharingStorageInstallConfig().getDssHome(); + String delDssHome = MessageFormat.format(SshCommandConstants.DEL_FILE, dssHome); + delCmd += delDssHome + " || echo \"delDssHome failed\"; "; + } + + EnterpriseInstallNodeConfig masterNodeConfig = getMasterInstallNodeConfig(installContext); + String delDataPath = MessageFormat.format(SshCommandConstants.DEL_FILE, masterNodeConfig.getDataPath()); + delCmd += delDataPath + " || echo \"delDataPath failed\"; "; + if (installContext.getEnterpriseInstallConfig().getIsInstallCM()) { + String delCmPath = MessageFormat.format(SshCommandConstants.DEL_FILE, masterNodeConfig.getCmDataPath()); + delCmd += delCmPath + " || echo \"delCmPath failed\"; "; + + } + return delCmd; + } catch (OpsException e) { + log.error("delete cmd : {}", delCmd); + return delCmd; + } + } + + + private void doInstall(InstallContext installContext) { + // Master node configuration information + Set top2DirSet = new HashSet(); + getClusterCommonTopTwoPath(installContext, top2DirSet); + EnterpriseInstallNodeConfig masterNodeConfig = getMasterInstallNodeConfig(installContext); + String installUsername = masterNodeConfig.getInstallUsername(); + RetBuffer retBuffer = installContext.getRetBuffer(); + for (HostInfoHolder hostInfoHolder : installContext.getHostInfoHolders()) { + Session currentRoot = createSessionWithRootUser(hostInfoHolder); + installDependency(currentRoot, retBuffer, installContext.getOs()); + top2DirSet.forEach(top2Path -> { + ensureDirExist(currentRoot, top2Path, retBuffer); + ensureLevel2DirPermission(currentRoot, installUsername, top2Path, retBuffer); + }); + closeSession(currentRoot); + } + String masterHostId = masterNodeConfig.getHostId(); + HostInfoHolder masterHostInfoHolder = getHostInfoHolder(installContext, masterHostId); + Session rootSession = createSessionWithRootUser(masterHostInfoHolder); + String pkgPath = preparePath(installContext.getEnterpriseInstallConfig().getInstallPackagePath()); + ensureDirExist(rootSession, pkgPath, retBuffer); + + // scp + sendOperateLog(installContext, "START_SCP_INSTALL_PACKAGE"); + String localPath = installContext.getInstallPackageLocalPath(); + String installPackageFullPath = scpInstallPackageToMasterNode(rootSession, localPath, pkgPath, retBuffer); + sendOperateLog(installContext, "END_SCP_INSTALL_PACKAGE"); + + // unzip install pack + sendOperateLog(installContext, "START_UNZIP_INSTALL_PACKAGE"); + decompress(rootSession, pkgPath, installPackageFullPath, retBuffer, "-xvf"); + // unzip CM + String omPackage = getOMPackage(pkgPath, rootSession); + decompress(rootSession, pkgPath, pkgPath + omPackage, retBuffer, "-zxvf"); + sendOperateLog(installContext, "END_UNZIP_INSTALL_PACKAGE"); + + // write xml + sendOperateLog(installContext, "START_GEN_XML_CONFIG"); + String xml = generateClusterConfigXmlInstance.generate(installContext); + log.info("Generated xml information:{}", xml); + String xmlConfigFullPath = ensureXmlConfigFullPath(rootSession, pkgPath, xml, retBuffer); + sendOperateLog(installContext, "END_GEN_XML_CONFIG"); + + // 设置解压后安装包目录用户,用户组以及操作权限 仅需设置主节点权限,备机由预安装脚本设置 + ensureLevel2DirPermission(rootSession, installUsername, pkgPath, retBuffer); + + HostInfoHolder masterHostHolder = getHostInfoHolder(installContext, masterHostId); + OpsHostUserEntity masterRootUserInfo = getHostUserInfoByUsername(masterHostHolder, "root"); + OpsHostUserEntity installUserInfo = getHostUserInfo(masterHostHolder, masterNodeConfig.getInstallUserId()); + + sendOperateLog(installContext, "START_EXE_PREINSTALL_COMMAND"); + String group = checkInstallUserGroup(installUsername, rootSession, retBuffer); + preInstall(group, pkgPath, masterNodeConfig, xmlConfigFullPath, rootSession, retBuffer, + masterRootUserInfo.getPassword(), installUserInfo.getPassword(), installContext.getEnvPath()); + sendOperateLog(installContext, "END_EXE_PREINSTALL_COMMAND"); + + Session ommSession = createSessionWithUserId(masterHostHolder, false, masterNodeConfig.getInstallUserId()); + // install + sendOperateLog(installContext, "START_EXE_INSTALL_COMMAND"); + ensureInstallCommand(installContext, xmlConfigFullPath, ommSession, retBuffer); + sendOperateLog(installContext, "END_EXE_INSTALL_COMMAND"); + closeSession(ommSession); + closeSession(rootSession); + } + + private EnterpriseInstallNodeConfig getMasterInstallNodeConfig(InstallContext installContext) { + return installContext + .getEnterpriseInstallConfig() + .getNodeConfigList() + .stream() + .filter(nodeConfig -> nodeConfig.getClusterRole() == ClusterRoleEnum.MASTER) + .findFirst() + .orElseThrow(() -> new OpsException("Master node information not found")); + } + + private void getClusterCommonTopTwoPath(InstallContext installContext, Set top2DirSet) { + String pkgPath = preparePath(installContext.getEnterpriseInstallConfig().getInstallPackagePath()); + String logPath = preparePath(installContext.getEnterpriseInstallConfig().getLogPath()); + String omPath = preparePath(installContext.getEnterpriseInstallConfig().getOmToolsPath()); + String installPath = preparePath(installContext.getEnterpriseInstallConfig().getInstallPath()); + String corePath = preparePath(installContext.getEnterpriseInstallConfig().getCorePath()); + String tmpPath = preparePath(installContext.getEnterpriseInstallConfig().getTmpPath()); + top2DirSet.add(getLevel2DirPath(pkgPath)); + top2DirSet.add(getLevel2DirPath(logPath)); + top2DirSet.add(getLevel2DirPath(omPath)); + top2DirSet.add(getLevel2DirPath(installPath)); + top2DirSet.add(getLevel2DirPath(corePath)); + top2DirSet.add(getLevel2DirPath(tmpPath)); + } + + private void closeSession(Session session) { + opsHostRemoteService.closeSession(session); + } + + private void ensureInstallCommand(InstallContext installContext, String xmlConfigFullPath, + Session ommSession, RetBuffer retBuffer) { + String installCommand = MessageFormat.format(SshCommandConstants.ENTERPRISE_INSTALL, xmlConfigFullPath); + try { + Map autoResponse = new HashMap<>(); + autoResponse.put("(yes/no)?", "yes"); + String databasePassword = encryptionUtils.decrypt( + installContext.getEnterpriseInstallConfig().getDatabasePassword()); + autoResponse.put("Please enter password for database:", databasePassword); + autoResponse.put("Please repeat for database:", databasePassword); + + installCommand = addCommandOfLoadEnvironmentVariable(installCommand, installContext.getEnvPath()); + opsHostRemoteService.executeCommand(installCommand, ommSession, retBuffer, autoResponse); + } catch (Exception e) { + log.error("install failed:", e); + throw new OpsException("install failed"); + } + } + + private String getOMPackage(String path, Session rootSession) { + String command = MessageFormat.format(SshCommandConstants.GET_OM_PACKAGE, path); + try { + String commandResult = opsHostRemoteService.executeCommand(command, rootSession, "get om package name"); + if (commandResult.contains(String.valueOf((char) 10))) { + String errorMsg = "One OM package is expected to be queried, but multiple OM packages are queried. " + + "Please check the package path: " + path; + log.error(errorMsg); + throw new OpsException(errorMsg); + } + if (ObjectUtils.isEmpty(commandResult)) { + String errorMsg = "No OM package is found."; + log.error(errorMsg); + throw new OpsException(errorMsg); + } + return commandResult; + } catch (OpsException e) { + log.error("Failed to obtain the OM package by command: {}. Error: {}", command, e.getMessage()); + throw new OpsException("Failed to obtain the OM package."); + } + } + + + private String checkInstallUserGroup(String installUsername, Session rootSession, RetBuffer retBuffer) { + String userGroupCommand = "groups " + installUsername + " | awk -F ':' '{print $2}' | sed 's/\\\"//g'"; + return opsHostRemoteService.executeCommand(userGroupCommand, rootSession, retBuffer, "query user group"); + } + + private String ensureXmlConfigFullPath(Session rootSession, String pkgPath, String xml, RetBuffer retBuffer) { + String xmlConfigFullPath = pkgPath + "cluster_config.xml"; + String createClusterConfigXmlCommand = MessageFormat.format(SshCommandConstants.FILE_CREATE, xml, xmlConfigFullPath); + opsHostRemoteService.executeCommand(createClusterConfigXmlCommand, rootSession, retBuffer, "create xml configuration file"); + return xmlConfigFullPath; + } + + + private void preInstall(String group, String pkgPath, EnterpriseInstallNodeConfig masterNodeConfig, String xmlConfigFullPath, Session rootSession, RetBuffer retBuffer, String rootPassword, String installUserPassword, String envPath) { + String installUsername = masterNodeConfig.getInstallUsername(); + String gsPreInstall = MessageFormat.format(SshCommandConstants.GS_PREINSTALL_INTERACTIVE, + pkgPath + "script/", installUsername, + group, xmlConfigFullPath); + gsPreInstall = wrapperEnvSep(gsPreInstall, envPath); + Map autoResponse = new HashMap<>(); + autoResponse.put("(yes/no)?", "yes"); + autoResponse.put("Please enter password for root\r\nPassword:", encryptionUtils.decrypt(rootPassword)); + autoResponse.put("Please enter password for current user[" + installUsername + "].\r\nPassword:", + encryptionUtils.decrypt(installUserPassword)); + // for compatibility with 5.1.0 + autoResponse.put("Please enter password for current user[root].\r\nPassword:", + encryptionUtils.decrypt(rootPassword)); + opsHostRemoteService.executeCommand(gsPreInstall, rootSession, retBuffer, autoResponse); + } + + private void saveContext(InstallContext installContext) { + OpsClusterEntity opsClusterEntity = installContext.toOpsClusterEntity(); + List opsClusterNodeEntities = installContext.getEnterpriseInstallConfig().toOpsClusterNodeEntityList(); + + opsClusterService.save(opsClusterEntity); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + opsClusterNodeEntity.setClusterId(opsClusterEntity.getClusterId()); + } + opsClusterNodeService.saveBatch(opsClusterNodeEntities); + } + + @Override + public void restart(OpsClusterContext opsClusterContext) { + log.info("Start rebooting Enterprise Edition"); + List restartNodeIds = opsClusterContext.getOpNodeIds(); + if (CollUtil.isEmpty(restartNodeIds)) { + restartNodeIds = opsClusterContext.getOpsClusterNodeEntityList().stream().map(OpsClusterNodeEntity::getClusterNodeId).collect(Collectors.toList()); + } + + if (CollUtil.isEmpty(restartNodeIds)) { + log.error("No nodes to restart"); + } + + if (opsClusterContext.getOpsClusterNodeEntityList().size() == restartNodeIds.size()) { + doRestartCluster(opsClusterContext); + } else { + for (String restartNodeId : restartNodeIds) { + doRestartNode(restartNodeId, opsClusterContext); + } + } + } + + private void doRestartCluster(OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity startNodeEntity = opsClusterContext.getOpsClusterNodeEntityList().get(0); + RetBuffer retBuffer = opsClusterContext.getRetBuffer(); + Session ommUserSession = createSessionWithUserId(opsClusterContext.getHostInfoHolders(), false, startNodeEntity.getHostId(), startNodeEntity.getInstallUserId()); + OmStatusModel omStatusModel = omStatus(ommUserSession, retBuffer, opsClusterContext.getOpsClusterEntity().getEnvPath()); + if (Objects.isNull(omStatusModel)) { + throw new OpsException("gs_om status fail"); + } + String command = omStatusModel.isInstallCm() ? "cm_ctl stop && cm_ctl start" : "gs_om -t restart"; + command = addCommandOfLoadEnvironmentVariable(command, opsClusterContext.getOpsClusterEntity().getEnvPath()); + opsHostRemoteService.executeCommand(command, ommUserSession, retBuffer, "restart cluster"); + } + + + private void doRestartNode(String restartNodeId, OpsClusterContext opsClusterContext) { + doStopNode(restartNodeId, opsClusterContext); + doStartNode(restartNodeId, opsClusterContext); + } + + @Override + public void start(OpsClusterContext opsClusterContext) { + log.info("Get started with Enterprise Edition"); + List startNodeIds = opsClusterContext.getOpNodeIds(); + if (CollUtil.isEmpty(startNodeIds)) { + startNodeIds = opsClusterContext.getOpsClusterNodeEntityList().stream().map(OpsClusterNodeEntity::getClusterNodeId).collect(Collectors.toList()); + } + + if (CollUtil.isEmpty(startNodeIds)) { + log.error("No node to start"); + } + + if (opsClusterContext.getOpsClusterNodeEntityList().size() == startNodeIds.size()) { + doStartCluster(opsClusterContext); + } else { + for (String startNodeId : startNodeIds) { + doStartNode(startNodeId, opsClusterContext); + } + } + } + + private void doStartCluster(OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity startNodeEntity = opsClusterContext.getOpsClusterNodeEntityList().get(0); + RetBuffer retBuffer = opsClusterContext.getRetBuffer(); + Session ommUserSession = createSessionWithUserId(opsClusterContext.getHostInfoHolders(), false, startNodeEntity.getHostId(), startNodeEntity.getInstallUserId()); + OmStatusModel omStatusModel = omStatus(ommUserSession, retBuffer, opsClusterContext.getOpsClusterEntity().getEnvPath()); + if (Objects.isNull(omStatusModel)) { + throw new OpsException("gs_om status fail"); + } + String command = omStatusModel.isInstallCm() ? " cm_ctl start " : " gs_om -t start "; + command = addCommandOfLoadEnvironmentVariable(command, opsClusterContext.getOpsClusterEntity().getEnvPath()); + opsHostRemoteService.executeCommand(command, ommUserSession, retBuffer, "start cluster"); + } + + private void doStartNode(String startNodeId, OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity startNodeEntity = opsClusterContext.getOpsClusterNodeEntityList().stream().filter(opsClusterNodeEntity -> opsClusterNodeEntity.getClusterNodeId().equals(startNodeId)).findFirst().orElse(null); + if (Objects.isNull(startNodeEntity)) { + log.error("Boot node information not found:{}", startNodeId); + } + + RetBuffer retBuffer = opsClusterContext.getRetBuffer(); + String dataPath = startNodeEntity.getDataPath(); + log.info("Login to start user"); + Session ommUserSession = createSessionWithUserId(opsClusterContext.getHostInfoHolders(), false, startNodeEntity.getHostId(), startNodeEntity.getInstallUserId()); + OmStatusModel omStatusModel = omStatus(ommUserSession, retBuffer, opsClusterContext.getOpsClusterEntity().getEnvPath()); + if (Objects.isNull(omStatusModel)) { + throw new OpsException("gs_om status fail"); + } + + String command = null; + if (omStatusModel.isInstallCm()) { + String hostId = startNodeEntity.getHostId(); + OpsHostEntity opsHostEntity = opsClusterContext.getHostInfoHolders().stream().map(HostInfoHolder::getHostEntity).filter(host -> host.getHostId().equalsIgnoreCase(hostId)).findFirst().orElseThrow(() -> new OpsException("no host found")); + String hostname = opsHostEntity.getHostname(); + String nodeId = omStatusModel.getHostnameMapNodeId().get(hostname); + + if (StrUtil.isNotEmpty(nodeId)) { + command = "cm_ctl start -n " + nodeId + " -D " + dataPath; + } + } else { + command = MessageFormat.format(SshCommandConstants.LITE_START, dataPath); + } + + command = addCommandOfLoadEnvironmentVariable(command, opsClusterContext.getOpsClusterEntity().getEnvPath()); + opsHostRemoteService.executeCommand(command, ommUserSession, retBuffer, "start cluster node"); + } + + @Override + public void stop(OpsClusterContext opsClusterContext) { + log.info("Start Stop Enterprise Edition"); + List stopNodeIds = opsClusterContext.getOpNodeIds(); + if (CollUtil.isEmpty(stopNodeIds)) { + stopNodeIds = opsClusterContext.getOpsClusterNodeEntityList().stream().map(OpsClusterNodeEntity::getClusterNodeId).collect(Collectors.toList()); + } + + if (CollUtil.isEmpty(stopNodeIds)) { + log.error("no node to stop"); + } + + if (opsClusterContext.getOpsClusterNodeEntityList().size() == stopNodeIds.size()) { + doStopCluster(opsClusterContext); + } else { + for (String stopNodeId : stopNodeIds) { + doStopNode(stopNodeId, opsClusterContext); + } + } + } + + private void doStopCluster(OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity startNodeEntity = opsClusterContext.getOpsClusterNodeEntityList().get(0); + RetBuffer retBuffer = opsClusterContext.getRetBuffer(); + Session ommUserSession = createSessionWithUserId(opsClusterContext.getHostInfoHolders(), false, startNodeEntity.getHostId(), startNodeEntity.getInstallUserId()); + OmStatusModel omStatusModel = omStatus(ommUserSession, retBuffer, opsClusterContext.getOpsClusterEntity().getEnvPath()); + if (Objects.isNull(omStatusModel)) { + throw new OpsException("gs_om status fail"); + } + + String command = omStatusModel.isInstallCm() ? "cm_ctl stop" : "gs_om -t stop"; + command = addCommandOfLoadEnvironmentVariable(command, opsClusterContext.getOpsClusterEntity().getEnvPath()); + opsHostRemoteService.executeCommand(command, ommUserSession, retBuffer, "stop cluster"); + } + + private void doStopNode(String stopNodeId, OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity stopNodeEntity = opsClusterContext.getOpsClusterNodeEntityList().stream().filter(opsClusterNodeEntity -> opsClusterNodeEntity.getClusterNodeId().equals(stopNodeId)).findFirst().orElse(null); + if (Objects.isNull(stopNodeEntity)) { + log.error("No stop node information found:{}", stopNodeId); + } + + RetBuffer retBuffer = opsClusterContext.getRetBuffer(); + String dataPath = stopNodeEntity.getDataPath(); + log.info("login stop user"); + Session ommUserSession = createSessionWithUserId(opsClusterContext.getHostInfoHolders(), false, stopNodeEntity.getHostId(), stopNodeEntity.getInstallUserId()); + OmStatusModel omStatusModel = omStatus(ommUserSession, retBuffer, opsClusterContext.getOpsClusterEntity().getEnvPath()); + if (Objects.isNull(omStatusModel)) { + throw new OpsException("gs_om status fail"); + } + + String command = null; + if (omStatusModel.isInstallCm()) { + String hostId = stopNodeEntity.getHostId(); + OpsHostEntity opsHostEntity = opsClusterContext.getHostInfoHolders().stream().map(HostInfoHolder::getHostEntity).filter(host -> host.getHostId().equalsIgnoreCase(hostId)).findFirst().orElseThrow(() -> new OpsException("no host found")); + String hostname = opsHostEntity.getHostname(); + String nodeId = omStatusModel.getHostnameMapNodeId().get(hostname); + + if (StrUtil.isNotEmpty(nodeId)) { + command = "cm_ctl stop -n " + nodeId + " -D " + dataPath; + } + } else { + command = MessageFormat.format(SshCommandConstants.LITE_STOP, dataPath); + } + + command = addCommandOfLoadEnvironmentVariable(command, opsClusterContext.getOpsClusterEntity().getEnvPath()); + opsHostRemoteService.executeCommand(command, ommUserSession, retBuffer, "stop cluster node"); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/TaskLiteProvider.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/TaskLiteProvider.java new file mode 100644 index 000000000..04ed83e9c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/TaskLiteProvider.java @@ -0,0 +1,609 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * TaskLiteProvider.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/TaskLiteProvider.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.provider; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.jcraft.jsch.Session; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.*; +import org.opengauss.admin.plugin.domain.model.ops.node.LiteInstallNodeConfig; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterService; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.text.MessageFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Cluster Installation Service Provider Specification + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@Slf4j +@Service +public class TaskLiteProvider extends AbstractTaskProvider { + private static final String LITE_SINGLE_INSTALL = "echo {0} | sh {1}install.sh --mode single -D {2} -R {3}" + + " --start -C port={4}"; + private static final String LITE_MASTER_INSTALL = "echo {0} | sh {1}install.sh --mode primary -D {2} -R {3} " + + "-C \"replconninfo1=''localhost={4} localport={5} remotehost={6} remoteport={7}''\" " + + "-C port={8} --start"; + private static final String LITE_SLAVE_INSTALL = "echo {0} | sh {1}install.sh --mode standby -D {2} -R {3} " + + "-C \"replconninfo1=''localhost={4} localport={5} remotehost={6} remoteport={7}''\" " + + "-C port={8} --start"; + + @Resource + private IOpsClusterService opsClusterService; + @Resource + private IOpsClusterNodeService opsClusterNodeService; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostUserFacade hostUserFacade; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostFacade hostFacade; + + @Override + public OpenGaussVersionEnum version() { + return OpenGaussVersionEnum.LITE; + } + + @Override + public void install(InstallContext installContext) { + DeployTypeEnum deployType = installContext.getDeployType(); + if (DeployTypeEnum.CLUSTER == deployType) { + installCluster(installContext); + } else { + installSingleNode(installContext); + } + sendOperateLog(installContext, "CREATE_REMOTE_USER"); + OpsClusterContext opsClusterContext = new OpsClusterContext(); + OpsClusterEntity opsClusterEntity = installContext.toOpsClusterEntity(); + List clusterNodes = installContext.getLiteInstallConfig().toOpsClusterNodeEntityList(); + opsClusterContext.setOpsClusterEntity(opsClusterEntity); + opsClusterContext.setOpsClusterNodeEntityList(clusterNodes); + + createRemoteUser(installContext, opsClusterContext); + + sendOperateLog(installContext, "SAVE_INSTALL_CONTEXT"); + saveContext(installContext); + sendOperateLog(installContext, "FINISH"); + } + + private void createRemoteUser(InstallContext installContext, OpsClusterContext opsClusterContext) { + RetBuffer retBuffer = installContext.getRetBuffer(); + opsClusterContext.setRetBuffer(retBuffer); + opsClusterContext.setHostInfoHolders(installContext.getHostInfoHolders()); + + LiteInstallConfig liteInstallConfig = installContext.getLiteInstallConfig(); + List nodeConfigList = liteInstallConfig.getNodeConfigList(); + String databasePassword = liteInstallConfig.getDatabasePassword(); + Integer port = liteInstallConfig.getPort(); + for (LiteInstallNodeConfig liteInstallNodeConfig : nodeConfigList) { + String hostId = liteInstallNodeConfig.getHostId(); + String installUserId = liteInstallNodeConfig.getInstallUserId(); + String dataPath = liteInstallNodeConfig.getDataPath(); + + Session session = createSessionWithUserId(installContext.getHostInfoHolders(), false, hostId, installUserId); + String listenerAddress = MessageFormat.format(SshCommandConstants.LISTENER, dataPath); + listenerAddress = addCommandOfLoadEnvironmentVariable(listenerAddress, installContext.getEnvPath()); + opsHostRemoteService.executeCommand(listenerAddress, session, retBuffer, "modify listening address"); + + + String hba = MessageFormat.format(SshCommandConstants.HBA, dataPath); + hba = addCommandOfLoadEnvironmentVariable(hba, installContext.getEnvPath()); + opsHostRemoteService.executeCommand(hba, session, retBuffer, "modify hba host"); + + String clientLoginOpenGauss = MessageFormat.format(SshCommandConstants.LOGIN, String.valueOf(port)); + try { + Map response = new HashMap<>(); + String createUser = MessageFormat.format( + "CREATE USER gaussdb WITH MONADMIN AUDITADMIN SYSADMIN" + " PASSWORD \"{0}\";\\q", + encryptionUtils.decrypt(databasePassword)); + response.put("openGauss=#", createUser); + clientLoginOpenGauss = addCommandOfLoadEnvironmentVariable(clientLoginOpenGauss, installContext.getEnvPath()); + opsHostRemoteService.executeCommand(clientLoginOpenGauss, session, retBuffer, response); + } catch (Exception e) { + log.error("Failed to create user", e); + throw new OpsException("Failed to create user"); + } + } + + opsClusterContext.setHostInfoHolders(installContext.getHostInfoHolders()); + restart(opsClusterContext); + } + + private void installSingleNode(InstallContext installContext) { + boolean isInstallSucc = true; + Session installUserSession = null; + try { + sendOperateLog(installContext, "START_SINGLE"); + log.info("Start installing Single Node Lite"); + LiteInstallNodeConfig installNodeConfig = installContext.getLiteInstallConfig().getNodeConfigList().get(0); + + String pkgPath = preparePath(installContext.getLiteInstallConfig().getInstallPackagePath()); + + String hostId = installNodeConfig.getHostId(); + + String installUserId = installNodeConfig.getInstallUserId(); + String installUserName; + if (StrUtil.isEmpty(installUserId)) { + sendOperateLog(installContext, "CREATE_INSTALL_USER"); + OpsHostUserEntity installUser = createInstallUser(installContext, hostId); + installUserId = installUser.getHostUserId(); + installUserName = installUser.getUsername(); + } else { + HostInfoHolder hostInfoHolder = getHostInfoHolder(installContext, hostId); + OpsHostUserEntity installUser = getHostUserInfo(hostInfoHolder, installUserId); + installUserName = installUser.getUsername(); + } + + if (StrUtil.isEmpty(installUserId)) { + throw new OpsException("Installation user ID does not exist"); + } + + String dataPath = preparePath(installNodeConfig.getDataPath()); + installUserSession = beforeInstall(installContext, pkgPath, dataPath, pkgPath, hostId, installUserId, + installUserName, "-xvf"); + + log.info("perform installation"); + sendOperateLog(installContext, "START_EXE_INSTALL_COMMAND"); + // install + String databasePassword = installContext.getLiteInstallConfig().getDatabasePassword(); + Integer port = installContext.getLiteInstallConfig().getPort(); + String installPath = preparePath(installNodeConfig.getInstallPath()); + String command = MessageFormat.format(LITE_SINGLE_INSTALL, encryptionUtils.decrypt(databasePassword), + pkgPath, dataPath, installPath, String.valueOf(port)); + command = wrapperLiteEnvSep(command, installContext.getEnvPath()); + opsHostRemoteService.executeCommand(command, installUserSession, installContext.getRetBuffer(), + "Lite single node installation"); + sendOperateLog(installContext, "END_EXE_INSTALL_COMMAND"); + log.info("The installation is complete"); + } catch (OpsException ex) { + isInstallSucc = false; + throw new OpsException(ex.getMessage()); + } finally { + if (Objects.nonNull(installUserSession) && installUserSession.isConnected()) { + installUserSession.disconnect(); + } + if (!isInstallSucc) { + cleanResource(installContext); + } + } + } + + private OpsHostUserEntity createInstallUser(InstallContext installContext, String hostId) { + OpsHostEntity hostEntity = hostFacade.getById(hostId); + OpsHostUserEntity hostUserEntity = hostUserFacade.getOmmUserByHostId(hostId); + if (Objects.nonNull(hostUserEntity)) { + return hostUserEntity; + } + OpsHostUserEntity hostRootUser = opsHostRemoteService.getHostRootUser(hostId); + Session rootSession = opsHostRemoteService.createHostUserSession(hostEntity, hostRootUser); + try { + String result = opsHostRemoteService.executeCommand(SshCommandConstants.OMM_USER, rootSession, + installContext.getRetBuffer(), "query omm user"); + if (StrUtil.isNotEmpty(result) && "1".equals(result)) { + throw new OpsException("Please enter the omm user login password"); + } + + String password = StrUtil.uuid(); + opsHostRemoteService.executeCommand(SshCommandConstants.CREATE_OMM_USER, rootSession, + installContext.getRetBuffer(), "create omm user"); + + try { + Map autoResponse = new HashMap<>(); + autoResponse.put("password:", password); + opsHostRemoteService.executeCommand(SshCommandConstants.CHANGE_OMM_PASSWORD, rootSession, + installContext.getRetBuffer(), autoResponse); + } catch (OpsException e) { + log.error("Failed to modify omm user password", e); + throw new OpsException("Failed to modify omm user password"); + } + + hostUserEntity = new OpsHostUserEntity(); + hostUserEntity.setHostId(hostId); + hostUserEntity.setUsername("omm"); + hostUserEntity.setPassword(encryptionUtils.encrypt(password)); + + hostUserFacade.save(hostUserEntity); + + List hostInfoHolders = installContext.getHostInfoHolders(); + if (Objects.isNull(hostInfoHolders)) { + hostInfoHolders = new ArrayList<>(); + installContext.setHostInfoHolders(hostInfoHolders); + } + + HostInfoHolder hostHolder = hostInfoHolders + .stream() + .filter(holder -> holder.getHostEntity().getHostId().equals(hostId)) + .findFirst().orElseThrow(() -> new OpsException("host information not found")); + List hostUserEntities = hostHolder.getHostUserEntities(); + if (Objects.isNull(hostUserEntities)) { + hostUserEntities = new ArrayList<>(); + hostHolder.setHostUserEntities(hostUserEntities); + } + + hostUserEntities.add(hostUserEntity); + return hostUserEntity; + } finally { + if (Objects.nonNull(rootSession) && rootSession.isConnected()) { + rootSession.disconnect(); + } + } + } + + private void installCluster(InstallContext installContext) { + boolean isInstallSucc = true; + try { + sendOperateLog(installContext, "START_CLUSTER"); + log.info("Start installing the Lite cluster"); + List installNodeConfigList = installContext + .getLiteInstallConfig() + .getNodeConfigList(); + + LiteInstallNodeConfig masterNodeConfig = installNodeConfigList.stream() + .filter(nodeConfig -> Objects.equals(ClusterRoleEnum.MASTER, nodeConfig.getClusterRole())) + .findFirst().orElseThrow(() -> new OpsException("Master node configuration not found")); + LiteInstallNodeConfig slaveNodeConfig = installNodeConfigList.stream() + .filter(nodeConfig -> Objects.equals(ClusterRoleEnum.SLAVE, nodeConfig.getClusterRole())) + .findFirst().orElseThrow(() -> new OpsException("Master node configuration not found")); + + installMaster(masterNodeConfig, slaveNodeConfig, installContext); + installSlave(masterNodeConfig, slaveNodeConfig, installContext); + + log.info("Cluster installation is complete"); + } catch (OpsException ex) { + isInstallSucc = false; + throw new OpsException(ex.getMessage()); + } finally { + if (!isInstallSucc) { + cleanResource(installContext); + } + } + } + + private void installSlave(LiteInstallNodeConfig masterNodeConfig, LiteInstallNodeConfig slaveNodeConfig, + InstallContext installContext) { + sendOperateLog(installContext, "START_SLAVE"); + log.info("Start installing the standby node"); + String installPath = preparePath(slaveNodeConfig.getInstallPath()); + + String dataPath = preparePath(slaveNodeConfig.getDataPath()); + String masterHostId = masterNodeConfig.getHostId(); + String slaveHostId = slaveNodeConfig.getHostId(); + String hostId = slaveNodeConfig.getHostId(); + String databasePassword = installContext.getLiteInstallConfig().getDatabasePassword(); + HostInfoHolder masterHostInfo = installContext.getHostInfoHolders() + .stream() + .filter(hostInfoHolder -> masterHostId.equals(hostInfoHolder.getHostEntity().getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("Master node configuration not found")); + HostInfoHolder slaveHostInfo = installContext.getHostInfoHolders() + .stream() + .filter(hostInfoHolder -> slaveHostId.equals(hostInfoHolder.getHostEntity().getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("The host information of the standby node does not exist")); + + String installUserId = slaveNodeConfig.getInstallUserId(); + String installUserName = null; + if (StrUtil.isEmpty(installUserId)) { + sendOperateLog(installContext, "CREATE_INSTALL_USER"); + OpsHostUserEntity installUser = createInstallUser(installContext, hostId); + installUserId = installUser.getHostUserId(); + installUserName = installUser.getUsername(); + } else { + final String uid = installUserId; + OpsHostUserEntity installUser = installContext.getHostInfoHolders() + .stream() + .filter(hostInfoHolder -> hostId.equals(hostInfoHolder.getHostEntity().getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("host information does not exist")) + .getHostUserEntities() + .stream() + .filter(userInfo -> uid.equals(userInfo.getHostUserId())) + .findFirst() + .orElseThrow(() -> new OpsException("Installation user information does not exist")); + installUserName = installUser.getUsername(); + } + + if (StrUtil.isEmpty(installUserId)) { + throw new OpsException("Installation user ID does not exist"); + } + + slaveNodeConfig.setInstallUserId(installUserId); + String pkgPath = preparePath(installContext.getLiteInstallConfig().getInstallPackagePath()); + Session installUserSession = beforeInstall(installContext, pkgPath, dataPath, pkgPath, slaveHostId, + installUserId, installUserName, "-xvf"); + + try { + log.info("perform installation"); + // install + String command = MessageFormat.format(LITE_SLAVE_INSTALL, databasePassword, + pkgPath, dataPath, installPath, slaveHostInfo.getHostEntity().getPrivateIp(), + String.valueOf(installContext.getLiteInstallConfig().getPort() + 1), + masterHostInfo.getHostEntity().getPrivateIp(), + String.valueOf(installContext.getLiteInstallConfig().getPort() + 1), + String.valueOf(installContext.getLiteInstallConfig().getPort())); + command = wrapperLiteEnvSep(command, installContext.getEnvPath()); + sendOperateLog(installContext, "START_EXE_INSTALL_COMMAND"); + opsHostRemoteService.executeCommand(command, installUserSession, installContext.getRetBuffer(), + "light version" + ClusterRoleEnum.SLAVE + "Node installation "); + sendOperateLog(installContext, "END_EXE_INSTALL_COMMAND"); + log.info("Standby node installation is complete"); + } finally { + if (Objects.nonNull(installUserSession) && installUserSession.isConnected()) { + installUserSession.disconnect(); + } + } + } + + private void installMaster(LiteInstallNodeConfig masterNodeConfig, LiteInstallNodeConfig slaveNodeConfig, + InstallContext installContext) { + sendOperateLog(installContext, "START_MASTER"); + log.info("Start installing the master node"); + String installPath = preparePath(masterNodeConfig.getInstallPath()); + String pkgPath = preparePath(installContext.getLiteInstallConfig().getInstallPackagePath()); + String dataPath = preparePath(masterNodeConfig.getDataPath()); + String masterHostId = masterNodeConfig.getHostId(); + String slaveHostId = slaveNodeConfig.getHostId(); + String hostId = masterNodeConfig.getHostId(); + String databasePassword = installContext.getLiteInstallConfig().getDatabasePassword(); + HostInfoHolder masterHostInfo = installContext.getHostInfoHolders() + .stream() + .filter(hostInfoHolder -> masterHostId.equals(hostInfoHolder.getHostEntity().getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("Master node host information does not exist")); + HostInfoHolder slaveHostInfo = installContext.getHostInfoHolders() + .stream() + .filter(hostInfoHolder -> slaveHostId.equals(hostInfoHolder.getHostEntity().getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("The host information of the standby node does not exist")); + + String installUserId = masterNodeConfig.getInstallUserId(); + String installUserName = null; + if (StrUtil.isEmpty(installUserId)) { + installContext.getRetBuffer().sendText("CREATE_INSTALL_USER"); + OpsHostUserEntity installUser = createInstallUser(installContext, hostId); + installUserId = installUser.getHostUserId(); + installUserName = installUser.getUsername(); + } else { + final String uid = installUserId; + OpsHostUserEntity installUser = installContext.getHostInfoHolders() + .stream() + .filter(hostInfoHolder -> hostId.equals(hostInfoHolder.getHostEntity().getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("host information does not exist")) + .getHostUserEntities() + .stream() + .filter(userInfo -> uid.equals(userInfo.getHostUserId())) + .findFirst() + .orElseThrow(() -> new OpsException("Installation user information does not exist")); + installUserName = installUser.getUsername(); + } + + if (StrUtil.isEmpty(installUserId)) { + throw new OpsException("Installation user ID does not exist"); + } + + masterNodeConfig.setInstallUserId(installUserId); + + Session installUserSession = beforeInstall(installContext, pkgPath, dataPath, pkgPath, masterHostId, + installUserId, installUserName, "-xvf"); + try { + log.info("perform installation"); + // install + String command = MessageFormat.format(LITE_MASTER_INSTALL, databasePassword, + pkgPath, dataPath, installPath, masterHostInfo.getHostEntity().getPrivateIp(), + String.valueOf(installContext.getLiteInstallConfig().getPort() + 1), + slaveHostInfo.getHostEntity().getPrivateIp(), + String.valueOf(installContext.getLiteInstallConfig().getPort() + 1), + String.valueOf(installContext.getLiteInstallConfig().getPort())); + command = wrapperLiteEnvSep(command, installContext.getEnvPath()); + installContext.getRetBuffer().sendText("START_EXE_INSTALL_COMMAND"); + opsHostRemoteService.executeCommand(command, installUserSession, installContext.getRetBuffer(), + "light version" + ClusterRoleEnum.MASTER + "Node installation"); + installContext.getRetBuffer().sendText("END_EXE_INSTALL_COMMAND"); + log.info("Master node installation is complete"); + } finally { + if (Objects.nonNull(installUserSession) && installUserSession.isConnected()) { + installUserSession.disconnect(); + } + } + } + + private void saveContext(InstallContext installContext) { + OpsClusterEntity opsClusterEntity = installContext.toOpsClusterEntity(); + List opsClusterNodeEntities = installContext + .getLiteInstallConfig() + .toOpsClusterNodeEntityList(); + + opsClusterService.save(opsClusterEntity); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + opsClusterNodeEntity.setClusterId(opsClusterEntity.getClusterId()); + } + opsClusterNodeService.saveBatch(opsClusterNodeEntities); + } + + @Override + public void restart(OpsClusterContext opsClusterContext) { + log.info("Start restarting the lite version"); + List restartNodeIds = opsClusterContext.getOpNodeIds(); + if (CollUtil.isEmpty(restartNodeIds)) { + restartNodeIds = opsClusterContext.getOpsClusterNodeEntityList() + .stream().map(OpsClusterNodeEntity::getClusterNodeId).collect(Collectors.toList()); + } + if (CollUtil.isEmpty(restartNodeIds)) { + log.error("No nodes to restart"); + } + for (String restartNodeId : restartNodeIds) { + doRestartNode(restartNodeId, opsClusterContext); + } + } + + @Override + public void start(OpsClusterContext opsClusterContext) { + log.info("start up lite version"); + List startNodeIds = opsClusterContext.getOpNodeIds(); + if (CollUtil.isEmpty(startNodeIds)) { + startNodeIds = opsClusterContext.getOpsClusterNodeEntityList() + .stream() + .map(OpsClusterNodeEntity::getClusterNodeId) + .collect(Collectors.toList()); + } + if (CollUtil.isEmpty(startNodeIds)) { + log.error("No node to start"); + } + for (String startNodeId : startNodeIds) { + doStartNode(startNodeId, opsClusterContext); + } + } + + @Override + public void stop(OpsClusterContext opsClusterContext) { + log.info("start stop lite version"); + List stopNodeIds = opsClusterContext.getOpNodeIds(); + if (CollUtil.isEmpty(stopNodeIds)) { + stopNodeIds = opsClusterContext.getOpsClusterNodeEntityList() + .stream().map(OpsClusterNodeEntity::getClusterNodeId).collect(Collectors.toList()); + } + + if (CollUtil.isEmpty(stopNodeIds)) { + log.error("no node to stop"); + } + + for (String stopNodeId : stopNodeIds) { + doStopNode(stopNodeId, opsClusterContext); + } + } + + + private void doStopNode(String stopNodeId, OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity stopNodeEntity = opsClusterContext.getOpsClusterNodeEntityList() + .stream() + .filter(opsClusterNodeEntity -> opsClusterNodeEntity.getClusterNodeId().equals(stopNodeId)) + .findFirst().orElse(null); + if (Objects.isNull(stopNodeEntity)) { + log.error("Stop node info not found: {}", stopNodeId); + } + + RetBuffer retBuffer = opsClusterContext.getRetBuffer(); + String dataPath = stopNodeEntity.getDataPath(); + log.info("login stop user"); + Session restartUserSession = createSessionWithUserId(opsClusterContext.getHostInfoHolders(), false, + stopNodeEntity.getHostId(), stopNodeEntity.getInstallUserId()); + String restartCommand = MessageFormat.format(SshCommandConstants.LITE_STOP, dataPath); + String envPath = opsClusterContext.getOpsClusterEntity().getEnvPath(); + restartCommand = addCommandOfLoadEnvironmentVariable(restartCommand, envPath); + opsHostRemoteService.executeCommand(restartCommand, restartUserSession, retBuffer, "stop cluster node"); + } + + private void doStartNode(String startNodeId, OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity startNodeEntity = opsClusterContext.getOpsClusterNodeEntityList() + .stream() + .filter(opsClusterNodeEntity -> opsClusterNodeEntity.getClusterNodeId().equals(startNodeId)) + .findFirst().orElse(null); + if (Objects.isNull(startNodeEntity)) { + log.error("Boot node information not found:{}", startNodeId); + } + + String dataPath = startNodeEntity.getDataPath(); + log.info("Login to start user"); + Session restartUserSession = createSessionWithUserId(opsClusterContext.getHostInfoHolders(), false, + startNodeEntity.getHostId(), startNodeEntity.getInstallUserId()); + String restartCommand = MessageFormat.format(SshCommandConstants.LITE_START, dataPath); + String envPath = opsClusterContext.getOpsClusterEntity().getEnvPath(); + restartCommand = addCommandOfLoadEnvironmentVariable(restartCommand, envPath); + opsHostRemoteService.executeCommand(restartCommand, restartUserSession, opsClusterContext.getRetBuffer(), + "start cluster node"); + } + + private void doRestartNode(String restartNodeId, OpsClusterContext opsClusterContext) { + OpsClusterNodeEntity restartNodeEntity = opsClusterContext.getOpsClusterNodeEntityList() + .stream() + .filter(opsClusterNodeEntity -> opsClusterNodeEntity.getClusterNodeId().equals(restartNodeId)) + .findFirst().orElse(null); + if (Objects.isNull(restartNodeEntity)) { + log.error("No restart node information found:{}", restartNodeId); + } + log.info("login restart user"); + Session restartUserSession = createSessionWithUserId(opsClusterContext.getHostInfoHolders(), false, + restartNodeEntity.getHostId(), restartNodeEntity.getInstallUserId()); + String restartCommand = MessageFormat.format(SshCommandConstants.LITE_RESTART, restartNodeEntity.getDataPath()); + String envPath = opsClusterContext.getOpsClusterEntity().getEnvPath(); + restartCommand = addCommandOfLoadEnvironmentVariable(restartCommand, envPath); + opsHostRemoteService.executeCommand(restartCommand, restartUserSession, opsClusterContext.getRetBuffer(), + "restart cluster node"); + } + + @Override + protected String prepareCleanClusterDir(InstallContext installContext) { + String delCmd = ""; + try { + LiteInstallNodeConfig nodeConfig = getSingleInstallNodeConfig(installContext); + String installPath = preparePath(nodeConfig.getInstallPath()); + String pkgPath = preparePath(installContext.getLiteInstallConfig().getInstallPackagePath()); + // remove intall path software + log.info("install package path : {}", pkgPath); + + String delInstallPath = MessageFormat.format(SshCommandConstants.DEL_FILE, pkgPath + "*"); + log.info("delete install package path : {}", delInstallPath); + + delCmd = delInstallPath + " || echo \"delInstallPath failed\"; "; + + String delDataPath = MessageFormat.format(SshCommandConstants.DEL_FILE, installPath); + + delCmd += delDataPath + " || echo \"delDataPath failed\"; "; + + return delCmd; + } catch (OpsException e) { + log.error("delete cmd : {}", delCmd); + return delCmd; + } + } + + private LiteInstallNodeConfig getSingleInstallNodeConfig(InstallContext installContext) { + return installContext + .getLiteInstallConfig() + .getNodeConfigList() + .stream() + .filter(config -> Objects.equals(ClusterRoleEnum.MASTER, config.getClusterRole())) + .findFirst() + .orElseThrow(() -> new OpsException("Master node configuration not found")); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/TaskMinimaListProvider.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/TaskMinimaListProvider.java new file mode 100644 index 000000000..8809a6cb6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/TaskMinimaListProvider.java @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * TaskMinimaListProvider.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/service/ops/impl/provider/TaskMinimaListProvider.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.service.ops.impl.provider; + +import cn.hutool.core.util.StrUtil; +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import com.jcraft.jsch.Session; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.*; +import org.opengauss.admin.plugin.domain.model.ops.node.MinimalistInstallNodeConfig; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterService; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.text.MessageFormat; +import java.util.*; + +/** + * TaskMinimaListProvider + * + * @author wangchao + * @date 2024/06/15 09:26 + */ +@Slf4j +@Service +public class TaskMinimaListProvider extends AbstractTaskProvider { + @Resource + private IOpsClusterService opsClusterService; + @Resource + private IOpsClusterNodeService opsClusterNodeService; + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private HostUserFacade hostUserFacade; + + @Override + public OpenGaussVersionEnum version() { + return OpenGaussVersionEnum.MINIMAL_LIST; + } + + @Override + public void install(InstallContext installContext) { + log.info("Start installing the minimalist version"); + RetBuffer retBuffer = installContext.getRetBuffer(); + + MinimalistInstallNodeConfig nodeConfig = getSingleInstallNodeConfig(installContext); + String hostId = nodeConfig.getHostId(); + String installPath = preparePath(nodeConfig.getInstallPath()); + String dataPath = preparePath(installPath + "data"); + String pkgPath = preparePath(installContext.getMinimalistInstallConfig().getInstallPackagePath()); + + String installUserId = nodeConfig.getInstallUserId(); + String installUserName = null; + if (StrUtil.isEmpty(installUserId)) { + sendOperateLog(installContext, "CREATE_INSTALL_USER"); + OpsHostUserEntity installUser = createInstallUser(installContext, hostId); + installUserId = installUser.getHostUserId(); + installUserName = installUser.getUsername(); + } else { + String uid = installUserId; + OpsHostUserEntity installUser = installContext.getHostInfoHolders() + .stream() + .filter(hostInfoHolder -> hostId.equals(hostInfoHolder.getHostEntity().getHostId())) + .findFirst() + .orElseThrow(() -> new OpsException("host information does not exist : " + hostId)) + .getHostUserEntities() + .stream() + .filter(userInfo -> uid.equals(userInfo.getHostUserId())) + .findFirst() + .orElseThrow(() -> new OpsException("Installation user information does not exist :" + uid)); + installUserName = installUser.getUsername(); + } + + if (StrUtil.isEmpty(installUserId)) { + throw new OpsException("Installation user ID does not exist :" + installUserId); + } + + nodeConfig.setInstallUserId(installUserId); + sendOperateLog(installContext, "BEFORE INSTALL"); + Session installUserSession = beforeInstall(installContext, installPath, dataPath, pkgPath, hostId, installUserId, installUserName, "-jxf"); + boolean isInstallSucc = true; + try { + log.info("perform installation"); + // install + sendOperateLog(installContext, "INSTALL"); + doInstall(installUserSession, retBuffer, installPath, installContext, nodeConfig); + + OpsClusterContext opsClusterContext = new OpsClusterContext(); + OpsClusterEntity opsClusterEntity = installContext.toOpsClusterEntity(); + List opsClusterNodeEntities = installContext.getMinimalistInstallConfig() + .toOpsClusterNodeEntityList(opsClusterEntity); + opsClusterContext.setOpsClusterEntity(opsClusterEntity); + opsClusterContext.setOpsClusterNodeEntityList(opsClusterNodeEntities); + + sendOperateLog(installContext, "CREATE REMOTE USER"); + sendOperateLog(installContext, "CREATE_REMOTE_USER"); + createRemoteUser(installUserSession, retBuffer, installContext, dataPath, opsClusterContext); + + sendOperateLog(installContext, "SAVE CONTEXT"); + sendOperateLog(installContext, "SAVE_INSTALL_CONTEXT"); + + saveContext(opsClusterEntity, opsClusterNodeEntities); + sendOperateLog(installContext, "FINISH"); + log.info("The installation is complete"); + } catch (OpsException ex) { + isInstallSucc = false; + sendOperateLog(installContext, ex.getMessage()); + log.info("The installation is error ", ex); + throw new OpsException("Installation failed " + ex.getMessage()); + } finally { + if (Objects.nonNull(installUserSession) && installUserSession.isConnected()) { + installUserSession.disconnect(); + } + if (!isInstallSucc) { + cleanResource(installContext); + } + } + } + + private void createRemoteUser(Session installUserSession, RetBuffer retBuffer, InstallContext installContext, String dataPath, OpsClusterContext opsClusterContext) { + if (DeployTypeEnum.CLUSTER == installContext.getDeployType()) { + String masterListenerAddress = MessageFormat.format(SshCommandConstants.LISTENER, dataPath + "master"); + opsHostRemoteService.executeCommand(masterListenerAddress, installUserSession, retBuffer, "modify listening address"); + + String masterHba = MessageFormat.format(SshCommandConstants.HBA, dataPath + "master"); + opsHostRemoteService.executeCommand(masterHba, installUserSession, retBuffer, "modify hba host"); + + String slaveListenerAddress = MessageFormat.format(SshCommandConstants.LISTENER, dataPath + "slave"); + opsHostRemoteService.executeCommand(slaveListenerAddress, installUserSession, retBuffer, "modify listening address"); + + String slaveHba = MessageFormat.format(SshCommandConstants.HBA, dataPath + "slave"); + opsHostRemoteService.executeCommand(slaveHba, installUserSession, retBuffer, "modify hba host"); + + } else { + String listenerAddress = MessageFormat.format(SshCommandConstants.LISTENER, dataPath + "single_node"); + opsHostRemoteService.executeCommand(listenerAddress, installUserSession, retBuffer, "modify listening address"); + + String hba = MessageFormat.format(SshCommandConstants.HBA, dataPath + "single_node"); + opsHostRemoteService.executeCommand(hba, installUserSession, retBuffer, "modify hba host"); + } + + opsClusterContext.setRetBuffer(retBuffer); + opsClusterContext.setHostInfoHolders(installContext.getHostInfoHolders()); + restart(opsClusterContext); + + try { + String clientLoginOpenGauss = MessageFormat.format(SshCommandConstants.LOGIN, String.valueOf(installContext.getMinimalistInstallConfig().getPort())); + Map response = new HashMap<>(); + String createUser = MessageFormat.format( + "CREATE USER gaussdb WITH MONADMIN AUDITADMIN SYSADMIN PASSWORD \"{0}\";\\q", + encryptionUtils.decrypt(installContext.getMinimalistInstallConfig().getDatabasePassword())); + response.put("openGauss=#", createUser); + opsHostRemoteService.executeCommand(clientLoginOpenGauss, installUserSession, retBuffer, response); + } catch (Exception e) { + log.error("Failed to create user", e); + throw new OpsException("Failed to create user"); + } + } + + private OpsHostUserEntity createInstallUser(InstallContext installContext, String hostId) { + OpsHostUserEntity hostUserEntity = hostUserFacade.getOmmUserByHostId(hostId); + if (Objects.nonNull(hostUserEntity)) { + return hostUserEntity; + } + + + Session rootSession = createSessionWithUserId(installContext, true, hostId, null); + try { + String ommUserName = null; + for (int i = 0; i < Integer.MAX_VALUE; i++) { + + if (i == 0) { + ommUserName = "omm"; + } else { + ommUserName = "omm" + i; + } + String command = "cat /etc/passwd | awk -F \":\" \"{print $1}\"|grep " + ommUserName + " | wc -l"; + String result = opsHostRemoteService.executeCommand(command, rootSession, installContext.getRetBuffer(), "query omm user"); + log.info("user count {},{}", ommUserName, result); + if (StrUtil.isNotEmpty(result) && 0 == Integer.parseInt(result)) { + log.info("ommUserName:{}", ommUserName); + break; + } + } + + String password = StrUtil.uuid(); + String createUserCommand = "useradd " + ommUserName; + opsHostRemoteService.executeCommand(createUserCommand, rootSession, installContext.getRetBuffer(), "create omm user"); + + try { + String passwdCommand = "passwd " + ommUserName; + Map autoResponse = new HashMap<>(); + autoResponse.put("password:", password); + autoResponse.put("password:", password); + opsHostRemoteService.executeCommand(passwdCommand, rootSession, installContext.getRetBuffer(), autoResponse); + } catch (OpsException e) { + log.error("Failed to modify omm user password", e); + throw new OpsException("Failed to modify omm user password"); + } + + hostUserEntity = new OpsHostUserEntity(); + hostUserEntity.setHostId(hostId); + hostUserEntity.setUsername(ommUserName); + hostUserEntity.setPassword(encryptionUtils.encrypt(password)); + + hostUserFacade.save(hostUserEntity); + + List hostInfoHolders = installContext.getHostInfoHolders(); + if (Objects.isNull(hostInfoHolders)) { + hostInfoHolders = new ArrayList<>(); + installContext.setHostInfoHolders(hostInfoHolders); + } + + HostInfoHolder hostHolder = hostInfoHolders.stream().filter(holder -> holder.getHostEntity().getHostId().equals(hostId)).findFirst().orElseThrow(() -> new OpsException("host information not found")); + List hostUserEntities = hostHolder.getHostUserEntities(); + if (Objects.isNull(hostUserEntities)) { + hostUserEntities = new ArrayList<>(); + hostHolder.setHostUserEntities(hostUserEntities); + } + + hostUserEntities.add(hostUserEntity); + return hostUserEntity; + } finally { + if (Objects.nonNull(rootSession) && rootSession.isConnected()) { + rootSession.disconnect(); + } + } + } + + + @Override + public void restart(OpsClusterContext opsClusterContext) { + log.info("Start restarting the minimalist version"); + RetBuffer retBuffer = opsClusterContext.getRetBuffer(); + + HostInfoHolder hostInfoHolder = opsClusterContext.getHostInfoHolders().get(0); + if (Objects.isNull(hostInfoHolder)) { + throw new OpsException("host information does not exist"); + } + + OpsClusterNodeEntity clusterNode = opsClusterContext.getOpsClusterNodeEntityList().get(0); + if (Objects.isNull(clusterNode)) { + throw new OpsException("Cluster node information does not exist"); + } + + log.info("login restart user"); + Session restartUserSession = createSessionWithUserId(opsClusterContext.getHostInfoHolders(), false, clusterNode.getHostId(), clusterNode.getInstallUserId()); + log.info("perform a restart"); + doRestart(restartUserSession, retBuffer, opsClusterContext); + log.info("restart complete"); + } + + @Override + public void start(OpsClusterContext opsClusterContext) { + log.info("Start the minimalist version"); + RetBuffer retBuffer = opsClusterContext.getRetBuffer(); + + HostInfoHolder hostInfoHolder = opsClusterContext.getHostInfoHolders().get(0); + if (Objects.isNull(hostInfoHolder)) { + throw new OpsException("host information does not exist"); + } + + OpsClusterNodeEntity clusterNode = opsClusterContext.getOpsClusterNodeEntityList().get(0); + if (Objects.isNull(clusterNode)) { + throw new OpsException("Cluster node information does not exist"); + } + + log.info("Login to start user"); + Session startUserSession = createSessionWithUserId(opsClusterContext.getHostInfoHolders(), false, clusterNode.getHostId(), clusterNode.getInstallUserId()); + log.info("execute start"); + doStart(startUserSession, retBuffer, opsClusterContext); + log.info("Startup complete"); + } + + private void doStart(Session startUserSession, RetBuffer retBuffer, OpsClusterContext opsClusterContext) { + OpsClusterEntity opsClusterEntity = opsClusterContext.getOpsClusterEntity(); + List opsClusterNodeEntities = opsClusterContext.getOpsClusterNodeEntityList(); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + String dataPath = opsClusterNodeEntity.getDataPath(); + String startCommand = MessageFormat.format(SshCommandConstants.LITE_START, dataPath); + startCommand = addCommandOfLoadEnvironmentVariable(startCommand, opsClusterEntity.getEnvPath()); + opsHostRemoteService.executeCommand(startCommand, startUserSession, retBuffer, "startup node"); + } + } + + @Override + public void stop(OpsClusterContext opsClusterContext) { + log.info("start stop minimalist"); + RetBuffer retBuffer = opsClusterContext.getRetBuffer(); + + HostInfoHolder hostInfoHolder = opsClusterContext.getHostInfoHolders().get(0); + if (Objects.isNull(hostInfoHolder)) { + throw new OpsException("host information does not exist"); + } + + OpsClusterNodeEntity clusterNode = opsClusterContext.getOpsClusterNodeEntityList().get(0); + if (Objects.isNull(clusterNode)) { + throw new OpsException("Cluster node information does not exist"); + } + + log.info("login stop user"); + Session stopUserSession = createSessionWithUserId(opsClusterContext.getHostInfoHolders(), false, clusterNode.getHostId(), clusterNode.getInstallUserId()); + log.info("execution stops"); + doStop(stopUserSession, retBuffer, opsClusterContext); + log.info("stop complete"); + } + + private void doStop(Session stopUserSession, RetBuffer retBuffer, OpsClusterContext opsClusterContext) { + OpsClusterEntity opsClusterEntity = opsClusterContext.getOpsClusterEntity(); + List opsClusterNodeEntities = opsClusterContext.getOpsClusterNodeEntityList(); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + String dataPath = opsClusterNodeEntity.getDataPath(); + String stopCommand = MessageFormat.format(SshCommandConstants.LITE_STOP, dataPath); + stopCommand = addCommandOfLoadEnvironmentVariable(stopCommand, opsClusterEntity.getEnvPath()); + opsHostRemoteService.executeCommand(stopCommand, stopUserSession, retBuffer, "stop node"); + } + } + + private void doRestart(Session restartUserSession, RetBuffer retBuffer, OpsClusterContext opsClusterContext) { + OpsClusterEntity opsClusterEntity = opsClusterContext.getOpsClusterEntity(); + List opsClusterNodeEntities = opsClusterContext.getOpsClusterNodeEntityList(); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + String dataPath = opsClusterNodeEntity.getDataPath(); + String restartCommand = MessageFormat.format(SshCommandConstants.LITE_RESTART, dataPath); + restartCommand = addCommandOfLoadEnvironmentVariable(restartCommand, opsClusterEntity.getEnvPath()); + opsHostRemoteService.executeCommand(restartCommand, restartUserSession, retBuffer, "restart node"); + } + } + + private void saveContext(OpsClusterEntity opsClusterEntity, List opsClusterNodeEntities) { + opsClusterService.save(opsClusterEntity); + for (OpsClusterNodeEntity opsClusterNodeEntity : opsClusterNodeEntities) { + opsClusterNodeEntity.setClusterId(opsClusterEntity.getClusterId()); + } + opsClusterNodeService.saveBatch(opsClusterNodeEntities); + } + + private void doInstall(Session installUserSession, RetBuffer retBuffer, String pkgPath, + InstallContext installContext, MinimalistInstallNodeConfig nodeConfig) { + Integer port = installContext.getMinimalistInstallConfig().getPort(); + String databasePassword = installContext.getMinimalistInstallConfig().getDatabasePassword(); + String command = getMinimaLitInstallCommandTemplate(installContext.getDeployType(), pkgPath, + encryptionUtils.decrypt(databasePassword), port); + HashMap autoResponse = new HashMap<>(); + autoResponse.put("Would you like to create a demo database (yes/no)? ", + (nodeConfig.getIsInstallDemoDatabase() ? "yes" : "no")); + + sendOperateLog(installContext, "START_EXE_INSTALL_COMMAND"); + try { + opsHostRemoteService.executeCommand(command, installUserSession, retBuffer, autoResponse); + } catch (Exception e) { + log.error("installation error", e); + throw new OpsException("installation error " + e.getMessage()); + } + sendOperateLog(installContext, "END_EXE_INSTALL_COMMAND"); + } + + private String getMinimaLitInstallCommandTemplate(DeployTypeEnum deployType, String pkgPath, + String dbPwd, Integer port) { + String installCommandTemplate = ""; + if (Objects.equals(DeployTypeEnum.SINGLE_NODE, deployType)) { + installCommandTemplate = SshCommandConstants.MINIMAL_LIST_SINGLE_INSTALL; + } else if (Objects.equals(DeployTypeEnum.CLUSTER, deployType)) { + installCommandTemplate = SshCommandConstants.MINIMAL_LIST_CLUSTER_INSTALL; + } else { + throw new OpsException("Unsupported deployment method:" + deployType); + } + return MessageFormat.format(installCommandTemplate, + pkgPath + "simpleInstall/", dbPwd, String.valueOf(port)); + } + + private MinimalistInstallNodeConfig getSingleInstallNodeConfig(InstallContext installContext) { + return installContext + .getMinimalistInstallConfig() + .getNodeConfigList() + .stream() + .filter(config -> ClusterRoleEnum.MASTER == config.getClusterRole()) + .findFirst() + .orElseThrow(() -> new OpsException("Master node configuration not found")); + } + + @Override + protected String prepareCleanClusterDir(InstallContext installContext) { + String delCmd = ""; + try { + MinimalistInstallNodeConfig nodeConfig = getSingleInstallNodeConfig(installContext); + String installPath = preparePath(nodeConfig.getInstallPath()); + String pkgPath = preparePath(installContext.getMinimalistInstallConfig().getInstallPackagePath()); + // remove intall path software + log.info("install package path : {}", pkgPath); + + String delInstallPath = MessageFormat.format(SshCommandConstants.DEL_FILE, pkgPath + "*"); + log.info("delete install package path : {}", delInstallPath); + + delCmd = delInstallPath + " || echo \"delInstallPath failed\"; "; + + String delDataPath = MessageFormat.format(SshCommandConstants.DEL_FILE, installPath); + + delCmd += delDataPath + " || echo \"delDataPath failed\"; "; + + return delCmd; + } catch (OpsException e) { + log.error("delete cmd : {}", delCmd); + return delCmd; + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/DBUtil.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/DBUtil.java new file mode 100644 index 000000000..b0133d60e --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/DBUtil.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DBUtil.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/utils/DBUtil.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.utils; + +import cn.hutool.crypto.SecureUtil; +import org.opengauss.admin.common.exception.ops.OpsException; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Objects; +import java.util.Optional; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author lhf + * @date 2022/10/10 11:14 + **/ +@Slf4j +@Component +public class DBUtil { + + private static final ConcurrentHashMap CONNECTION_CACHE = new ConcurrentHashMap<>(); + + public Optional getSession(String host, Integer port, String username, String password) throws SQLException, ClassNotFoundException { + String driver = "org.opengauss.Driver"; + String sourceURL = "jdbc:opengauss://" + host + ":" + port + "/postgres"; + Properties info = new Properties(); + info.setProperty("user", username); + info.setProperty("password", password); + info.setProperty("connectTimeout", "30"); + Class.forName(driver); + Connection conn = DriverManager.getConnection(sourceURL, info); + + return Optional.ofNullable(conn); + } + + public void closeConn(Connection connection) { + if (Objects.nonNull(connection)) { + try { + connection.close(); + } catch (SQLException e) { + log.error("database close fail!"); + } + } + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + private static class ConnectionInfoSummary { + private String host; + private Integer port; + private String username; + private String password; + + public String summary() { + return SecureUtil.md5(this.host + "_" + this.port + "_" + this.username + "_" + this.password); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/DecryptionUtil.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/DecryptionUtil.java new file mode 100644 index 000000000..7d1680a5c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/DecryptionUtil.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * EncryptionController.java + * + * IDENTIFICATION + * openGauss-visualtool/visualtool-api/src/main/java/org/opengauss/admin/web/controller/ops/EncryptionController.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.utils; + +import com.gitee.starblues.bootstrap.annotation.AutowiredType; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.model.ops.EnterpriseInstallConfig; +import org.opengauss.admin.plugin.domain.model.ops.ImportClusterBody; +import org.opengauss.admin.plugin.domain.model.ops.InstallBody; +import org.opengauss.admin.plugin.domain.model.ops.InstallContext; +import org.opengauss.admin.plugin.domain.model.ops.LiteInstallConfig; +import org.opengauss.admin.plugin.domain.model.ops.MinimalistInstallConfig; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.function.Function; + +/** + * DecryptionUtil + * + * @since 2025/6/26 + */ +@Slf4j +@Component +public class DecryptionUtil { + @Autowired + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + + /** + * decrypt database password + * + * @param installBody installBody + */ + public void decryptDatabasePassword(InstallBody installBody) { + OpenGaussVersionEnum openGaussVersion = installBody.getInstallContext().getOpenGaussVersion(); + decryptDatabasePassword(installBody.getInstallContext(), openGaussVersion, true); + } + + /** + * decrypt database password + * + * @param importClusterBody importClusterBody + */ + public void decryptDatabasePassword(ImportClusterBody importClusterBody) { + OpenGaussVersionEnum openGaussVersion = importClusterBody.getOpenGaussVersion(); + decryptDatabasePassword(importClusterBody, openGaussVersion, false); + } + + private void decryptDatabasePassword(Object installConfig, OpenGaussVersionEnum openGaussVersion, + boolean isInstallBody) { + switch (openGaussVersion) { + case ENTERPRISE: + EnterpriseInstallConfig enterpriseConfig = getInstallConfig(installConfig, isInstallBody, + InstallContext::getEnterpriseInstallConfig, ImportClusterBody::getEnterpriseInstallConfig); + enterpriseConfig.setDatabasePassword(encryptionUtils.decrypt(enterpriseConfig.getDatabasePassword())); + break; + case MINIMAL_LIST: + MinimalistInstallConfig minimalistConfig = getInstallConfig(installConfig, isInstallBody, + InstallContext::getMinimalistInstallConfig, ImportClusterBody::getMinimalistInstallConfig); + minimalistConfig.setDatabasePassword(encryptionUtils.decrypt(minimalistConfig.getDatabasePassword())); + break; + case LITE: + LiteInstallConfig liteConfig = getInstallConfig(installConfig, isInstallBody, + InstallContext::getLiteInstallConfig, ImportClusterBody::getLiteInstallConfig); + liteConfig.setDatabasePassword(encryptionUtils.decrypt(liteConfig.getDatabasePassword())); + break; + default: + throw new OpsException("unsupported version:[" + openGaussVersion + "]"); + } + } + + private T getInstallConfig(Object installConfig, boolean isInstallBody, + Function installBodyMapper, Function importClusterBodyMapper) { + if (isInstallBody && installConfig instanceof InstallContext installCfg) { + return installBodyMapper.apply(installCfg); + } else if (!isInstallBody && installConfig instanceof ImportClusterBody importClusterBody) { + return importClusterBodyMapper.apply(importClusterBody); + } else { + throw new IllegalArgumentException("Invalid installConfig type or mismatched flag"); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/DownloadUtil.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/DownloadUtil.java new file mode 100644 index 000000000..903343902 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/DownloadUtil.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DownloadUtil.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/utils/DownloadUtil.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.utils; + +import com.gitee.starblues.bootstrap.annotation.AutowiredType; + +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.model.ops.WsSession; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.system.plugin.facade.SysSettingFacade; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.*; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.util.Objects; + +import jakarta.annotation.Resource; + +/** + * @author lhf + * @date 2022/8/15 22:32 + **/ +@Slf4j +@Component +public class DownloadUtil { + @Autowired + private WsUtil wsUtil; + + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private SysSettingFacade sysSettingFacade; + + /** + * download file to target path + * + * @param resourceUrl url + * @param targetPath target path + * @param fileName file name + * @param wsSession ws + */ + public void download(String resourceUrl, String targetPath, String fileName, WsSession wsSession) { + DownloadMonitor downloadMonitor = new DownloadMonitor(); + InputStream inputStream = null; + OutputStream outputStream = null; + try { + File existFile = new File(targetPath + "/" + fileName); + if (existFile.exists()) { + wsUtil.sendText(wsSession, "1"); + return; + } + File file = new File(targetPath); + if (!file.exists()) { + if (!file.mkdirs()) { + throw new OpsException("Failed to create a directory"); + } + } + URL url = new URL(resourceUrl); + URLConnection urlConnection = openConnectionWithProxy(url); + long max = urlConnection.getContentLengthLong(); + downloadMonitor.init(max, 0, wsSession); + inputStream = urlConnection.getInputStream(); + if (inputStream == null) { + throw new IOException("Failed to get input stream form URL: " + url); + } + byte[] bs = new byte[10240]; + int len; + outputStream = new FileOutputStream(targetPath + "/" + fileName, false); + while ((len = inputStream.read(bs)) != -1) { + outputStream.write(bs, 0, len); + downloadMonitor.count(len); + } + } catch (Exception e) { + log.error("File download Failed", e); + wsUtil.sendText(wsSession, "File download Failed"); + } finally { + downloadMonitor.end(); + if (Objects.nonNull(outputStream)) { + try { + outputStream.close(); + } catch (IOException e) { + log.error("Failed to close the output stream. Procedure"); + } + } + if (Objects.nonNull(inputStream)) { + try { + inputStream.close(); + } catch (IOException e) { + log.error("Failed to close the output stream. Procedure"); + } + } + } + } + + private URLConnection openConnectionWithProxy(URL url) throws IOException { + Proxy proxy = sysSettingFacade.getSysNetProxy(); + if (Objects.isNull(proxy)) { + return url.openConnection(); + } else { + return url.openConnection(proxy); + } + } + + class DownloadMonitor { + private long max; + private long count; + private long percent; + private WsSession wsSession; + + public void init(long max, long count, WsSession wsSession) { + this.max = max; + this.count = count; + this.percent = 0; + this.wsSession = wsSession; + } + + /** + * download monitor + * + * @param bytes byte + */ + public void count(long bytes) { + count += bytes; + long currentPercent = count * 100 / max; + if (currentPercent > percent) { + this.percent = currentPercent; + if (currentPercent % 10 == 0) { + log.info("========{}========", this.percent + "%"); + } + wsUtil.sendText(wsSession, this.percent / 100.00 + ""); + } + } + + public void end() { + wsUtil.sendText(wsSession, "DOWNLOAD_FINISH"); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/GeoUtils.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/GeoUtils.java new file mode 100644 index 000000000..4e167bf7d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/GeoUtils.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * GeoUtils.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/utils/GeoUtils.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.utils; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +/** + * @author LZW + */ +public class GeoUtils { + + public static double[] getGeoJsonCenter(String geoJson) { + JSONObject geoJsonObject = JSONObject.parseObject(geoJson); + JSONArray features = geoJsonObject.getJSONArray("features"); + + double sumX = 0; + double sumY = 0; + int count = 0; + + for (int i = 0; i < features.size(); i++) { + JSONObject feature = features.getJSONObject(i); + JSONObject geometry = feature.getJSONObject("geometry"); + if (geometry.getString("type").equals("Polygon")) { + JSONArray coordinates = geometry.getJSONArray("coordinates").getJSONArray(0); + for (int j = 0; j < coordinates.size(); j++) { + JSONArray point = coordinates.getJSONArray(j); + sumX += point.getDoubleValue(0); + sumY += point.getDoubleValue(1); + count++; + } + } else if (geometry.getString("type").equals("MultiPolygon")) { + JSONArray polygons = geometry.getJSONArray("coordinates"); + for (int j = 0; j < polygons.size(); j++) { + JSONArray coordinates = polygons.getJSONArray(j).getJSONArray(0); + for (int k = 0; k < coordinates.size(); k++) { + JSONArray point = coordinates.getJSONArray(k); + sumX += point.getDoubleValue(0); + sumY += point.getDoubleValue(1); + count++; + } + } + } + } + + double centerX = sumX / count; + double centerY = sumY / count; + + return new double[]{centerX, centerY}; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/JschRetBufferUtil.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/JschRetBufferUtil.java new file mode 100644 index 000000000..81d363083 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/JschRetBufferUtil.java @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JschRetBufferUtil.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/utils/JschRetBufferUtil.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpException; +import com.jcraft.jsch.SftpProgressMonitor; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.model.ops.JschResult; +import org.opengauss.admin.plugin.domain.model.ops.RetBuffer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * JschRetBufferUtil + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@Slf4j +@Component +@Scope("prototype") +public class JschRetBufferUtil { + private static final int SESSION_TIMEOUT = 10000; + private static final int CHANNEL_TIMEOUT = 50000; + private static ConcurrentHashMap autoResponseContext = new ConcurrentHashMap<>(); + + static { + autoResponseContext.put("yes/no", "yes"); + } + + @Autowired + private WsUtil wsUtil; + + /** + * Acquiring a Session + * + * @param host host + * @param port port + * @param username username + * @param password password + * @return ssh session + * @since 1.0 + */ + public Optional getSession(String host, Integer port, String username, String password) { + log.info("host:{},port:{},username:{}", host, port, username); + return createSession(host, port, username, password); + } + + /** + * execute command + * + * @param command Instructions to execute + * @param session session + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + * @since 1.0 + */ + public JschResult executeCommand(String command, Session session) + throws IOException, InterruptedException { + return executeCommandWithRetBuffer(command, session, null); + } + + /** + * execute command with ret buffer + * + * @param command Instructions to execute + * @param session session + * @param retBuffer retBuffer + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + * @since 1.0 + */ + public JschResult executeCommandWithRetBuffer(String command, Session session, RetBuffer retBuffer) + throws IOException, InterruptedException { + return executeCommandWithRetBufferAndAutoResponse(command, session, retBuffer, null); + } + + + /** + * execute command with ret buffer ,load environment + * + * @param command Instructions to execute + * @param env env + * @param session session + * @param retBuffer retBuffer + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + * @since 1.0 + */ + public JschResult executeCommandWithEnvAndRetBuffer(String command, String env, Session session, RetBuffer retBuffer) + throws IOException, InterruptedException { + if (StrUtil.isNotEmpty(env)) { + command = "source " + env + " && " + command; + } + + return executeCommandWithRetBufferAndAutoResponse(command, session, retBuffer, null); + } + + + /** + * execute command with ret buffer and auto response ,load environment + * + * @param env env + * @param command command + * @param session session + * @param retBuffer retBuffer + * @param autoResponse autoResponse + * @return result + * @throws IOException ioe + * @throws InterruptedException ie + */ + public JschResult executeCommandWithEnvAndRetBufferAndAutoResponse(String env, String command, Session session, + RetBuffer retBuffer, Map autoResponse) throws IOException, InterruptedException { + if (StrUtil.isNotEmpty(env)) { + command = "source " + env + " && " + command; + } + + return executeCommandWithRetBufferAndAutoResponse(command, session, retBuffer, autoResponse); + } + + + /** + * execute command with ret buffer and auto response + * + * @param command Instructions to execute + * @param session session + * @param retBuffer retBuffer + * @param autoResponse autoResponse + * @return SSH Result + * @throws IOException ioe + * @throws InterruptedException ie + */ + public JschResult executeCommandWithRetBufferAndAutoResponse(String command, Session session, RetBuffer retBuffer, + Map autoResponse) throws IOException, InterruptedException { + return executeCommand(command, session, retBuffer, autoResponse, true); + } + + /** + * execute command with ret buffer and auto response + * + * @param command Instructions to execute + * @param session session + * @param retBuffer retBuffer + * @param autoResponse autoResponse + * @param handleErrorBeforeExit handleErrorBeforeExit + * @return SSH Result + * @throws IOException ioe + * @throws InterruptedException ie + */ + public JschResult executeCommand(String command, Session session, RetBuffer retBuffer, + Map autoResponse, boolean handleErrorBeforeExit) throws IOException, InterruptedException { + log.debug("Execute an order:{}", command); + if (Objects.nonNull(retBuffer)) { + retBuffer.sendText(command); + } + + if (Objects.nonNull(autoResponse)) { + HashMap autoClone = new HashMap<>(JschRetBufferUtil.autoResponseContext); + autoClone.putAll(autoResponse); + autoResponse = autoClone; + } else { + autoResponse = JschRetBufferUtil.autoResponseContext; + } + + ChannelExec channel = null; + try { + Channel execChannel = session.openChannel("exec"); + if (execChannel instanceof ChannelExec) { + channel = (ChannelExec) execChannel; + } + channel.setPtyType("dump"); + channel.setPty(!command.contains("nohup")); + } catch (JSchException e) { + log.error("Obtaining the exec channel fails:", e); + throw new OpsException("Obtaining the exec channel fails"); + } + + channel.setCommand(command); + try { + channel.connect(CHANNEL_TIMEOUT); + } catch (JSchException e) { + log.error("Execute instruction [{}] exception:", command, e); + throw new OpsException("Command execution exception"); + } + + JschResult jschResult = buildJschResult(channel, retBuffer, autoResponse); + + if (handleErrorBeforeExit && jschResult.getExitCode() != 0) { + log.error("execute command fail, command:{}, result:{}", command, jschResult); + throw new OpsException("execute command fail with exit code:" + jschResult.getExitCode() + ",msg:" + jschResult.getResult()); + } + + return jschResult; + } + + /** + * File upload + * + * @param session Host SSH Session + * @param retBuffer retBuffer + * @param sourceFilePath Source path + * @param targetPath The target path + */ + public void upload(Session session, RetBuffer retBuffer, String sourceFilePath, String targetPath) { + log.info("Start uploading {} to {}", sourceFilePath, targetPath); + synchronized (session) { + try { + ChannelSftp channel = null; + Channel sftpChannel = session.openChannel("sftp"); + if (sftpChannel instanceof ChannelSftp) { + channel = (ChannelSftp) sftpChannel; + } + channel.connect(); + JschProgressMonitor jschProgressMonitor = new JschProgressMonitor(retBuffer); + channel.put(sourceFilePath.trim(), targetPath.trim(), jschProgressMonitor, ChannelSftp.OVERWRITE); + } catch (JSchException | SftpException e) { + log.error("sftp upload Failure", e); + throw new OpsException("sftp upload Failure: " + e.getMessage()); + } + } + log.info("Upload End"); + } + + /** + * File upload + * + * @param session Host SSH Session + * @param retBuffer retBuffer + * @param sourceStream Source stream + * @param targetPath The target path + */ + public synchronized void upload(Session session, RetBuffer retBuffer, InputStream sourceStream, String targetPath) { + log.info("Start uploading stream to {}", targetPath); + synchronized (session) { + try { + ChannelSftp channel = null; + Channel sftpChannel = session.openChannel("sftp"); + if (sftpChannel instanceof ChannelSftp) { + channel = (ChannelSftp) sftpChannel; + } + channel.connect(); + JschProgressMonitor jschProgressMonitor = new JschProgressMonitor(retBuffer); + channel.put(sourceStream, targetPath.trim(), jschProgressMonitor, ChannelSftp.OVERWRITE); + } catch (JSchException | SftpException e) { + log.error("sftp upload Failure", e); + throw new OpsException("sftp upload Failure: " + e.getMessage()); + } finally { + try { + sourceStream.close(); + } catch (IOException e) { + log.error("close input stream failed: " + e.getMessage()); + } + } + } + log.info("Upload End"); + } + + /** + * ChannelShell Creates a session + * + * @param host host + * @param port port + * @param username username + * @param password password + * @return SSH session + */ + private Optional createSession(String host, Integer port, String username, String password) { + JSch jSch = new JSch(); + Session session; + try { + session = jSch.getSession(username, host, port); + } catch (JSchException e) { + log.error("Connection establishment fail:", e); + throw new OpsException("Connection establishment fail"); + } + + session.setPassword(password); + session.setConfig("StrictHostKeyChecking", "no"); + try { + session.connect(SESSION_TIMEOUT); + } catch (JSchException e) { + log.error(host + "Connection establishment fail", e); + throw new OpsException(host + "Connection establishment fail"); + } + return Optional.of(session); + } + + private JschResult buildJschResult(ChannelExec channelExec, RetBuffer retBuffer, + Map autoResponse) throws IOException, InterruptedException { + JschResult jschResult = new JschResult(); + StringBuilder resultStrBuilder = new StringBuilder(); + // The output of the script execution is an input stream to the program\ + try (InputStream in = channelExec.getInputStream(); OutputStream out = channelExec.getOutputStream()) { + // Read the input stream from the remote host and get the script execution results + byte[] tmp = new byte[1024]; + while (true) { + while (in.available() > 0) { + int i = in.read(tmp, 0, 1024); + if (i < 0) { + break; + } + + String msg = new String(tmp, 0, i, StandardCharsets.UTF_8); + if (Objects.nonNull(retBuffer)) { + retBuffer.sendText(msg); + } + resultStrBuilder.append(msg); + } + + if (channelExec.isClosed()) { + if (in.available() > 0) { + continue; + } + in.close(); + out.close(); + + int exitStatus = channelExec.getExitStatus(); + jschResult.setExitCode(exitStatus); + + break; + } + autoResponse(autoResponse, resultStrBuilder, out); + } + + } + + + channelExec.disconnect(); + jschResult.setResult(resultStrBuilder.toString().trim()); + return jschResult; + } + + + private void autoResponse(Map autoResponse, + StringBuilder resultStrBuilder, OutputStream out) { + if (CollUtil.isNotEmpty(autoResponse)) { + autoResponse.forEach((k, v) -> { + if (resultStrBuilder.toString().trim().endsWith(k.trim())) { + try { + out.write((v.trim() + System.lineSeparator()).getBytes(StandardCharsets.UTF_8)); + out.flush(); + log.info("autoResponse {}{} ", k, v); + resultStrBuilder.append(v.trim() + System.lineSeparator()); + } catch (IOException e) { + log.error("Automatic response exception", e); + } + } + }); + } + } + + private class JschProgressMonitor implements SftpProgressMonitor { + // The total number of bytes currently received + private long count = 0L; + // Final file size + private long max = 0L; + // The progress of + private long percent = -1L; + + private RetBuffer retBuffer; + + public JschProgressMonitor(RetBuffer retBuffer) { + this.retBuffer = retBuffer; + } + + @Override + public void init(int op, String src, String dest, long max) { + this.max = max; + count = 0; + percent = -1; + } + + @Override + public boolean count(long count) { + this.count += count; + if (percent >= this.count * 100 / max) { + return true; + } + percent = this.count * 100 / max; + if (percent % 10 == 0) { + log.info("Completed " + this.count + "(" + percent + "%) out of " + max + "."); + } + retBuffer.sendText(percent + "%"); + return true; + } + + @Override + public void end() { + + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/JschUtil.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/JschUtil.java new file mode 100644 index 000000000..e35005335 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/JschUtil.java @@ -0,0 +1,886 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * JschUtil.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/utils/JschUtil.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.ChannelShell; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpException; +import com.jcraft.jsch.SftpProgressMonitor; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.opengauss.admin.plugin.domain.model.ops.HostFile; +import org.opengauss.admin.plugin.domain.model.ops.JschResult; +import org.opengauss.admin.plugin.domain.model.ops.WsSession; +import org.opengauss.admin.plugin.domain.model.ops.cache.WsConnectorManager; +import org.opengauss.admin.plugin.enums.ops.HostFileTypeEnum; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Vector; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Jsch Tools + * + * @author lhf + * @since 1.0 + * @date 2022/6/13 15:18 + **/ +@Slf4j +@Component +public class JschUtil { + private static final int SESSION_TIMEOUT = 10000; + private static final int CHANNEL_TIMEOUT = 50000; + private static ConcurrentHashMap autoResponseContext = new ConcurrentHashMap<>(); + + static { + autoResponseContext.put("yes/no", "yes"); + } + + @Autowired + private WsUtil wsUtil; + @Autowired + private WsConnectorManager wsConnectorManager; + + /** + * Acquiring a Session + * + * @param host host + * @param port port + * @param username username + * @param password password + * @return ssh session + * + * @since 1.0 + */ + public Optional getSession(String host, Integer port, String username, String password) { + log.info("host:{},port:{},username:{}", host, port, username); + return createSession(host, port, username, password); + } + + /** + * ChannelExec + * + * @param command Instructions to execute + * @param session session + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + * + * @since 1.0 + */ + public JschResult executeCommand(String command, Session session) + throws IOException, InterruptedException { + return executeCommand(command, session, null, null); + } + + + /** + * ChannelExec + * + * @param command Instructions to execute + * @param session session + * @param env env path + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + * + * @since 1.0 + */ + public JschResult executeCommand(String command, Session session, String env) + throws IOException, InterruptedException { + if (StrUtil.isNotEmpty(env)) { + command = "source " + env + " && " + command; + } + + return executeCommand(command, session, null, null); + } + + /** + * ChannelExec + * + * @param command Instructions to execute + * @param session session + * @param wsSession websocket session + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + * + * @since 1.0 + */ + public JschResult executeCommand(String command, Session session, WsSession wsSession) + throws IOException, InterruptedException { + return executeCommand(command, session, wsSession, null); + } + + + /** + * ChannelExec + * + * @param command Instructions to execute + * @param env env + * @param session session + * @param wsSession websocket session + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + * + * @since 1.0 + */ + public JschResult executeCommand(String command, String env, Session session, WsSession wsSession) + throws IOException, InterruptedException { + if (StrUtil.isNotEmpty(env)) { + command = "source " + env + " && " + command; + } + + return executeCommand(command, session, wsSession, null); + } + + /** + * ChannelExec + * + * @param command Instructions to execute + * @param session session + * @param autoResponse response + * @return SSH Result + * @throws IOException IO Exception + * @throws InterruptedException Interrupted Exception + * @since 1.0 + */ + public JschResult executeCommand(String command, Session session, Map autoResponse) + throws IOException, InterruptedException { + return executeCommand(command, session, null, autoResponse); + } + + /** + * executeCommandWithSerialResponse + * + * @param env env + * @param command command + * @param session session + * @param autoResponse autoResponse + * @param retSession retSession + * @return result + * @throws Exception e + */ + public JschResult executeCommandWithSerialResponse(String env, String command, Session session, + Map> autoResponse, WsSession retSession) throws Exception { + if (StrUtil.isNotEmpty(env)) { + command = "source " + env + " && " + command; + } + return executeCommandWithSerialResponse(command, session, autoResponse, retSession); + } + + /** + * executeCommandWithSerialResponse + * + * @param command command + * @param session session + * @param autoResponse autoResponse + * @param retSession retSession + * @return result + * @throws Exception e + */ + public JschResult executeCommandWithSerialResponse(String command, Session session, Map> autoResponse, WsSession retSession) throws Exception { + if (Objects.nonNull(retSession)) { + wsUtil.sendText(retSession, command); + } + + log.debug("Execute an order:{}", command); + if (Objects.nonNull(autoResponse)) { + HashMap> autoClone = new HashMap<>(); + autoClone.putAll(autoResponse); + autoResponse = autoClone; + } else { + autoResponse = new HashMap<>(); + } + + ChannelExec channel = null; + try { + Channel exeChannel = session.openChannel("exec"); + if (exeChannel instanceof ChannelExec) { + channel = (ChannelExec) exeChannel; + } + + channel.setPtyType("dump"); + channel.setPty(true); + } catch (JSchException e) { + log.error("Obtaining the exec channel fails:", e); + throw new OpsException("Obtaining the exec channel fails"); + } + + channel.setCommand(command); + try { + channel.connect(CHANNEL_TIMEOUT); + } catch (JSchException e) { + log.error("Execute instruction [{}] exception:", command, e); + throw new OpsException("Command execution exception"); + } + + return buildJschResultWithSerialResponse(channel, retSession, autoResponse); + } + + /** + * executeCommand + * + * @param env env + * @param command command + * @param session session + * @param wsSession wsSession + * @param autoResponse autoResponse + * @return result + * @throws IOException ioe + * @throws InterruptedException ie + */ + public JschResult executeCommand(String env, String command, Session session, + WsSession wsSession, Map autoResponse) throws IOException, InterruptedException { + if (StrUtil.isNotEmpty(env)) { + command = "source " + env + " && " + command; + } + + return executeCommand(command, session, wsSession, autoResponse); + } + + public JschResult executeCommand(String env, String command, Session session, + WsSession wsSession, boolean handleErrorBeforeExit) throws IOException, InterruptedException { + if (StrUtil.isNotEmpty(env)) { + command = "source " + env + " && " + command; + } + + return executeCommand(command, session, wsSession, null, handleErrorBeforeExit); + } + + /** + * ChannelExec + * + * @param command Instructions to execute + * @param session session + * @param wsSession websocket session + * @param autoResponse autoResponse + * @return SSH Result + * + * @throws IOException ioe + * @throws InterruptedException ie + */ + public JschResult executeCommand(String command, Session session, WsSession wsSession, + Map autoResponse) throws IOException, InterruptedException { + return executeCommand(command, session, wsSession, autoResponse, true); + } + + /** + * ChannelExec + * + * @param command Instructions to execute + * @param session session + * @param wsSession websocket session + * @param autoResponse autoResponse + * @param handleErrorBeforeExit handleErrorBeforeExit + * @return SSH Result + * + * @throws IOException ioe + * @throws InterruptedException ie + */ + public JschResult executeCommand(String command, Session session, WsSession wsSession, + Map autoResponse, boolean handleErrorBeforeExit) throws IOException, InterruptedException { + log.debug("Execute an order:{}", command); + wsUtil.sendText(wsSession, command); + + if (Objects.nonNull(autoResponse)) { + HashMap autoClone = new HashMap<>(JschUtil.autoResponseContext); + autoClone.putAll(autoResponse); + autoResponse = autoClone; + } else { + autoResponse = JschUtil.autoResponseContext; + } + + ChannelExec channel = null; + try { + Channel execChannel = session.openChannel("exec"); + if (execChannel instanceof ChannelExec) { + channel = (ChannelExec) execChannel; + } + channel.setPtyType("dump"); + channel.setPty(!command.contains("nohup")); + } catch (JSchException e) { + log.error("Obtaining the exec channel fails:", e); + throw new OpsException("Obtaining the exec channel fails"); + } + + channel.setCommand(command); + try { + channel.connect(CHANNEL_TIMEOUT); + } catch (JSchException e) { + log.error("Execute instruction [{}] exception:", command, e); + throw new OpsException("Command execution exception"); + } + + JschResult jschResult = buildJschResult(channel, wsSession, autoResponse); + + if (handleErrorBeforeExit && jschResult.getExitCode() != 0) { + log.error("execute command fail, command:{}, result:{}", command, jschResult); + throw new OpsException("execute command fail with exit code:" + jschResult.getExitCode() + ",msg:"+ jschResult.getResult()); + } + + return jschResult; + } + + /** + * open ChannelShell + * + * @param session session + * @return SSH Result + */ + public ChannelShell openChannelShell(Session session) { + ChannelShell channel = null; + try { + Channel channelShell = session.openChannel("shell"); + if (channelShell instanceof ChannelShell) { + channel = (ChannelShell) channelShell; + } + channel.setPtySize(200, 40, 200 * 8, 40 * 8); + channel.connect(CHANNEL_TIMEOUT); + } catch (JSchException e) { + log.error("Channel establishment Exception", e); + throw new OpsException("Channel establishment Exception"); + } + return channel; + } + + /** + * Send instructions to ChannelShell + * + * @param channelShell channelShell + * @param command command + */ + public void sendToChannelShell(ChannelShell channelShell, String command) { + try { + if (channelShell != null) { + OutputStream outputStream = channelShell.getOutputStream(); + outputStream.write(command.getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + } + } catch (IOException e) { + log.error("Message Sending Exception", e); + throw new OpsException("Message Sending Exception"); + } + } + + /** + * The SSH Channel message is sent to the Websocket session + * + * @param channel SSH Channel + * @param wsSession WebSocket session Object + */ + public void channelToWsSession(Channel channel, WsSession wsSession) { + try (InputStream inputStream = channel.getInputStream()) { + // Cyclic reading + byte[] buffer = new byte[1024]; + int len; + // If there is no data coming, the thread will remain blocked at this point waiting for data. + while ((len = inputStream.read(buffer)) != -1) { + byte[] bytes = Arrays.copyOfRange(buffer, 0, len); + wsConnectorManager.getSession(wsSession.getSessionId()).ifPresent(newSession -> { + try { + newSession.getSession().getBasicRemote().sendText(new String(bytes, StandardCharsets.UTF_8)); + } catch (IOException e) { + log.error("Failed to send message [{}] to websocket:", + new String(bytes, StandardCharsets.UTF_8), e); + } + }); + } + } catch (IOException e) { + log.error("Description Failed to send a message to the websocket:", e); + } finally { + wsSession.close(); + channel.disconnect(); + } + } + + /** + * File upload + * + * @param session Host SSH Session + * @param wsSession websocket session + * @param sourceFilePath Source path + * @param targetPath The target path + */ + public void upload(Session session, WsSession wsSession, String sourceFilePath, String targetPath) { + log.info("Start uploading {} to {}", sourceFilePath, targetPath); + synchronized (session) { + try { + Path sourcePath = resolveSourcePath(sourceFilePath); + if (!Files.exists(sourcePath)) { + log.error("Source file not found: {}", sourcePath); + wsUtil.sendText(wsSession, "Source file not found: " + sourcePath); + return; + } + ChannelSftp channel = null; + Channel sftpChannel = session.openChannel("sftp"); + if (sftpChannel instanceof ChannelSftp) { + channel = (ChannelSftp) sftpChannel; + } + channel.connect(); + JschProgressMonitor jschProgressMonitor = new JschProgressMonitor(wsSession); + channel.put(sourcePath.toString(), targetPath.trim(), jschProgressMonitor, ChannelSftp.OVERWRITE); + } catch (JSchException | SftpException e) { + log.error("sftp upload Failure", e); + throw new OpsException("sftp upload Failure: " + e.getMessage()); + } + } + log.info("Upload End"); + } + + private Path resolveSourcePath(String sourceFilePath) { + Path path = Paths.get(sourceFilePath); + if (!path.isAbsolute()) { + Path baseDir = Paths.get(System.getProperty("user.dir")); + return baseDir.resolve(path).normalize(); + } + return path.normalize(); + } + + /** + * File upload + * + * @param session Host SSH Session + * @param wsSession websocket session + * @param sourceStream Source stream + * @param targetPath The target path + */ + public synchronized void upload(Session session, WsSession wsSession, InputStream sourceStream, String targetPath) { + log.info("Start uploading stream to {}", targetPath); + synchronized (session) { + try { + ChannelSftp channel = null; + Channel sftpChannel = session.openChannel("sftp"); + if (sftpChannel instanceof ChannelSftp) { + channel = (ChannelSftp) sftpChannel; + } + channel.connect(); + JschProgressMonitor jschProgressMonitor = new JschProgressMonitor(wsSession); + channel.put(sourceStream, targetPath.trim(), jschProgressMonitor, ChannelSftp.OVERWRITE); + } catch (JSchException | SftpException e) { + log.error("sftp upload Failure", e); + throw new OpsException("sftp upload Failure: " + e.getMessage()); + } finally { + try { + sourceStream.close(); + } catch (IOException e) { + log.error("close input stream failed: " + e.getMessage()); + } + } + } + log.info("Upload End"); + } + + /** + * ChannelShell Creates a session + * + * @param host host + * @param port port + * @param username username + * @param password password + * @return SSH session + */ + private Optional createSession(String host, Integer port, String username, String password) { + JSch jSch = new JSch(); + Session session; + try { + session = jSch.getSession(username, host, port); + } catch (JSchException e) { + log.error("Connection establishment fail:", e); + throw new OpsException("Connection establishment fail"); + } + + session.setPassword(password); + session.setConfig("StrictHostKeyChecking", "no"); + try { + session.connect(SESSION_TIMEOUT); + } catch (JSchException e) { + log.error(host + "Connection establishment fail", e); + throw new OpsException(host + "Connection establishment fail"); + } + return Optional.of(session); + } + + private JschResult buildJschResult(ChannelExec channelExec, WsSession wsSession, + Map autoResponse) throws IOException, InterruptedException { + JschResult jschResult = new JschResult(); + StringBuilder resultStrBuilder = new StringBuilder(); + // The output of the script execution is an input stream to the program + InputStream in = channelExec.getInputStream(); + OutputStream out = channelExec.getOutputStream(); + // Read the input stream from the remote host and get the script execution results + byte[] tmp = new byte[1024]; + while (true) { + while (in.available() > 0) { + int i = in.read(tmp, 0, 1024); + if (i < 0) { + break; + } + + String msg = new String(tmp, 0, i, StandardCharsets.UTF_8); + + msgToWs(msg, wsSession); + + resultStrBuilder.append(msg); + } + + if (channelExec.isClosed()) { + if (in.available() > 0) { + continue; + } + in.close(); + out.close(); + + int exitStatus = channelExec.getExitStatus(); + jschResult.setExitCode(exitStatus); + + break; + } + + autoResponse(autoResponse, resultStrBuilder, out); + } + + channelExec.disconnect(); + jschResult.setResult(resultStrBuilder.toString().trim()); + return jschResult; + } + + private void msgToWs(String msg, WsSession wsSession) throws IOException { + if (Objects.nonNull(wsSession) && Objects.nonNull(wsSession.getSession()) + && wsSession.getSession().isOpen()) { + wsSession.getSession().getBasicRemote().sendText(msg); + } else { + if (Objects.nonNull(wsSession) + && StrUtil.isNotEmpty(wsSession.getSessionId())) { + wsConnectorManager.getSession(wsSession.getSessionId()).ifPresent(newSession -> { + try { + newSession.getSession().getBasicRemote().sendText(msg); + } catch (IOException e) { + log.error("Failed to send message [{}] to websocket:", msg, e); + } + }); + } + } + } + + private void autoResponse(Map autoResponse, + StringBuilder resultStrBuilder, OutputStream out) { + if (CollUtil.isNotEmpty(autoResponse)) { + autoResponse.forEach((k, v) -> { + if (resultStrBuilder.toString().trim().endsWith(k.trim())) { + try { + out.write((v.trim() + System.getProperty("line.separator")).getBytes(StandardCharsets.UTF_8)); + out.flush(); + resultStrBuilder.append(v.trim() + System.getProperty("line.separator")); + } catch (IOException e) { + log.error("Automatic response exception", e); + } + } + }); + } + } + + private JschResult buildJschResultWithSerialResponse(ChannelExec channelExec, + WsSession wsSession, Map> autoResponse) throws Exception { + JschResult jschResult = new JschResult(); + StringBuilder resultStrBuilder = new StringBuilder(); + InputStream in = channelExec.getInputStream(); + OutputStream out = channelExec.getOutputStream(); + byte[] tmp = new byte[1024]; + while (true) { + while (in.available() > 0) { + int i = in.read(tmp, 0, 1024); + if (i < 0) { + break; + } + String msg = new String(tmp, 0, i, StandardCharsets.UTF_8); + msgToWs(msg, wsSession); + resultStrBuilder.append(msg); + } + if (channelExec.isClosed()) { + if (in.available() > 0) { + continue; + } + in.close(); + out.close(); + + int exitStatus = channelExec.getExitStatus(); + jschResult.setExitCode(exitStatus); + break; + } + if (CollUtil.isNotEmpty(autoResponse)) { + autoResponse.forEach((k, v) -> { + if (resultStrBuilder.toString().trim().endsWith(k.trim())) { + try { + if (CollUtil.isNotEmpty(v)) { + out.write((v.get(0) + System.getProperty("line.separator")).getBytes(StandardCharsets.UTF_8)); + v.remove(0); + out.flush(); + } + } catch (IOException e) { + log.error("Automatic response exception", e); + } + } + }); + } + } + channelExec.disconnect(); + jschResult.setResult(resultStrBuilder.toString().trim()); + return jschResult; + } + + /** + * list remote dir + * + * @param session session + * @param remoteDir remoteDir + * @return host files + * + * @since 1.0 + */ + public List ls(Session session, String remoteDir) { + List res = new ArrayList<>(); + + try { + Channel channel = session.openChannel("sftp"); + channel.connect(); + ChannelSftp channelSftp = null; + if (channel instanceof ChannelSftp) { + channelSftp = (ChannelSftp) channel; + } + + Vector vector = channelSftp.ls(remoteDir); + Iterator iterator = vector.iterator(); + while (iterator.hasNext()) { + ChannelSftp.LsEntry file = null; + Object next = iterator.next(); + if (next instanceof ChannelSftp.LsEntry) { + file = (ChannelSftp.LsEntry) next; + } + res.add(HostFile.of(file.getFilename(), file.getAttrs().isDir() + ? HostFileTypeEnum.DIRECTORY : HostFileTypeEnum.FILE, file.getAttrs().getSize())); + } + } catch (JSchException | SftpException e) { + log.error("File enumeration failure", e); + } + + res.sort(Comparator.comparing(HostFile::getType).reversed().thenComparing(HostFile::getName)); + + HostFile currentDir = null; + HostFile parentDir = null; + for (HostFile file : res) { + if (".".equalsIgnoreCase(file.getName())) { + currentDir = file; + } + + if ("..".equals(file.getName())) { + parentDir = file; + } + } + + res.remove(currentDir); + + if (Objects.nonNull(parentDir)) { + res.remove(parentDir); + res.add(0, parentDir); + } + + return res; + } + + public void download(Session session, String path, String filename, HttpServletResponse response) { + ChannelSftp sftp = null; + try { + Channel channel = session.openChannel("sftp"); + channel.connect(); + sftp = (ChannelSftp) channel; + } catch (JSchException e) { + throw new OpsException("Abnormal connection"); + } + + response.reset(); + response.setContentType("application/octet-stream"); + response.setCharacterEncoding("utf-8"); + response.setHeader("Content-Disposition", "attachment;filename=" + filename); + + OutputStream os = null; + try (InputStream inputStream = sftp.get(path + "/" + filename); + BufferedInputStream bis = new BufferedInputStream(inputStream)) { + byte[] buff = new byte[1024]; + os = response.getOutputStream(); + int i = 0; + boolean empty = true; + while ((i = bis.read(buff)) != -1) { + empty = false; + os.write(buff, 0, i); + } + + if (empty) { + os.write(0); + } + + os.flush(); + } catch (SftpException | IOException e) { + log.error("{}", e); + } finally { + if (Objects.nonNull(os)) { + try { + os.close(); + } catch (IOException ignore) { + + } + } + } + } + + /** + * @param rootSession root user session + * @param dependencies the list of dependencies + * @return java.util.ArrayList the list of missing dependencies + */ + public ArrayList checkDependencies(Session rootSession, ArrayList dependencies) { + if (dependencies == null || dependencies.isEmpty()) { + log.warn("The list of dependencies to check is null or empty."); + return dependencies; + } + + ArrayList missingDependencies = new ArrayList<>(); + + String command = String.format("yum list installed | egrep '%s'", + String.join("|", dependencies)); + + JschResult jschResult = null; + try { + jschResult = executeCommand(command, rootSession); + } catch (OpsException e) { + log.info("None of the checked dependencies exists."); + return dependencies; + } catch (IOException | InterruptedException e) { + log.error("The check dependencies command fails to be executed. Details: ", e); + return dependencies; + } + + String commandOutput = jschResult.getResult(); + for (String dependency : dependencies) { + if (!commandOutput.contains(dependency)) { + missingDependencies.add(dependency); + } + } + + return missingDependencies; + } + + /** + * @param rootSession root user session + * @param dependencies the list of dependencies + */ + public void installDependencies(Session rootSession, ArrayList dependencies) { + if (dependencies == null || dependencies.isEmpty()) { + log.warn("The list of dependencies to install is null or empty."); + return; + } + + String command = "yum install -y " + String.join(" ", dependencies); + + try { + executeCommand(command, rootSession); + } catch (OpsException e) { + log.error("Some dependencies are not installed successfully. Details: " + e); + } catch (IOException | InterruptedException e) { + log.error("The install dependencies command fails to be executed. Details: " + e); + } + } + + private class JschProgressMonitor implements SftpProgressMonitor { + // The total number of bytes currently received + private long count = 0L; + // Final file size + private long max = 0L; + // The progress of + private long percent = -1L; + + private WsSession wsSession; + + public JschProgressMonitor(WsSession wsSession) { + this.wsSession = wsSession; + } + + @Override + public void init(int op, String src, String dest, long max) { + this.max = max; + count = 0; + percent = -1; + } + + @Override + public boolean count(long count) { + this.count += count; + if (percent >= this.count * 100 / max) { + return true; + } + percent = this.count * 100 / max; + + if (percent % 10 == 0) { + log.info("Completed " + this.count + "(" + percent + + "%) out of " + max + "."); + } + wsUtil.sendText(wsSession, percent + "%"); + + return true; + } + + @Override + public void end() { + + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/OpsAssert.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/OpsAssert.java new file mode 100644 index 000000000..afe1c6b69 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/OpsAssert.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024-2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsAssert.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/OpsAssert.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.utils; + +import org.opengauss.admin.common.exception.ops.OpsException; + +import java.util.Objects; + +/** + * OpsAssert + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +public class OpsAssert { + /** + * Assert a boolean expression, throwing an {@code OpsException} + * if the expression evaluates to {@code false}. + *
Assert.isTrue(i > 0, "The value must be greater than zero");
+ * + * @param expression a boolean expression + * @param message the exception message to use if the assertion fails + * @throws OpsException if {@code expression} is {@code false} + */ + public static void isTrue(boolean expression, String message) { + if (!expression) { + throw new OpsException(message); + } + } + + /** + * Assert a object non-null, throwing an {@code OpsException} + * if object is null ,then throwing an {@code OpsException} + * + * @param object a Object + * @param message the exception message to use if the assertion fails + * @throws OpsException if {@code expression} is {@code false} + */ + public static void nonNull(Object object, String message) { + if (Objects.isNull(object)) { + throw new OpsException(message); + } + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/OpsJschExecPlugin.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/OpsJschExecPlugin.java new file mode 100644 index 000000000..4a7dcb88e --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/OpsJschExecPlugin.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsJschExecPlugin.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/OpsJschExecPlugin.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.utils; + +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.common.core.vo.HostBean; +import org.opengauss.admin.common.exception.ops.OpsException; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.Properties; + +/** + * cluster task operations + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@Slf4j +@Component +@Scope("prototype") +public class OpsJschExecPlugin { + private static final int SESSION_TIMEOUT = 10000; + + /** + * exec command and session will be created and closed + * + * @param host host + * @param command command + * @return result + */ + public String execCommand(HostBean host, String command) { + Session session = null; + BufferedReader reader = null; + ChannelExec channelExec = null; + StringBuffer result = new StringBuffer(); + try { + JSch jsch = new JSch(); + // 创建与远程服务器的会话 + session = jsch.getSession(host.getUsername(), host.getHost(), host.getPort()); + // 设置会话的密码 + session.setPassword(host.getPassword()); + // 设置一些配置,例如不检查主机密钥 + Properties config = new Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config); + // 连接会话 + session.connect(SESSION_TIMEOUT); + // 打开执行命令的通道 + Channel exec = session.openChannel("exec"); + if (exec instanceof ChannelExec) { + channelExec = (ChannelExec) exec; + channelExec.setCommand(command); + channelExec.setPtyType("dump"); + channelExec.setPty(true); + // 连接通道 + channelExec.connect(); + // 创建读取通道输入流的缓冲读取器 + reader = new BufferedReader( + new InputStreamReader(channelExec.getInputStream(), StandardCharsets.UTF_8)); + } else { + throw new OpsException("Unable to open exec channel."); + } + String line; + // 逐行读取并输出结果 + while ((line = reader.readLine()) != null) { + result.append(line.trim()).append(System.lineSeparator()); + } + result.delete(result.length() - 2, result.length()); + } catch (JSchException | IOException e) { + throw new OpsException(e.getMessage()); + } finally { + // 关闭读取器 + if (Objects.nonNull(reader)) { + try { + reader.close(); + } catch (IOException e) { + log.error("close reader error"); + } + } + // 断开通道 + if (Objects.nonNull(channelExec)) { + channelExec.disconnect(); + } + // 断开会话 + if (Objects.nonNull(session)) { + session.disconnect(); + } + } + return result.toString(); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/PathUtils.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/PathUtils.java new file mode 100644 index 000000000..1501139fc --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/PathUtils.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * PathUtils.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/PathUtils.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.utils; + +import org.springframework.stereotype.Component; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; + +/** + * PathUtils + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Component +public class PathUtils { + private final boolean isLocalDevelopmentEnv; + private final String rootDir; + + /** + * constructor , init isLocalDevelopmentEnv + */ + public PathUtils() { + rootDir = getUserRootDir(); + isLocalDevelopmentEnv = checkLocalDevelopmentEnv(); + } + + private String getUserRootDir() { + final String userDir = System.getProperty("user.dir"); + File[] roots = File.listRoots(); + return Arrays.stream(roots).map(File::getAbsolutePath).filter(userDir::contains).findFirst().orElse(""); + } + + /** + * @param sysUploadPath + * @return + */ + public String getPath(String sysUploadPath) { + return isLocalDevelopmentEnv ? rootDir + sysUploadPath : sysUploadPath; + } + + /** + * Check if the local development environment is Windows. + * + * @return boolean + */ + private boolean checkLocalDevelopmentEnv() { + String osName = System.getProperty("os.name").toLowerCase(); + return osName.contains("win"); + } + + /** + * Check if the three directories do not conflict. + * + * @param path1 path1 + * @param path2 path2 + * @param path3 path2 + * @return boolean + */ + public boolean checkDirNoConflict(String path1, String path2, String path3) { + Path pathOne = Paths.get(path1); + Path pathTwo = Paths.get(path2); + Path pathThree = Paths.get(path3); + boolean isPath12Conflict = contains(pathOne, pathTwo, isLocalDevelopmentEnv); + boolean isPath13Conflict = contains(pathOne, pathThree, isLocalDevelopmentEnv); + boolean isPath21Conflict = contains(pathTwo, pathOne, isLocalDevelopmentEnv); + boolean isPath23Conflict = contains(pathTwo, pathThree, isLocalDevelopmentEnv); + boolean isPath31Conflict = contains(pathThree, pathOne, isLocalDevelopmentEnv); + boolean isPath32Conflict = contains(pathThree, pathOne, isLocalDevelopmentEnv); + return isPath12Conflict || isPath13Conflict || isPath21Conflict || isPath23Conflict || isPath31Conflict || isPath32Conflict; + } + + private boolean contains(Path parent, Path child, boolean isLocalDevelopmentEnv) { + if (isLocalDevelopmentEnv) { + return parent.startsWith(child); + } + if (parent.equals(child)) { + return true; + } + if (parent.isAbsolute() != child.isAbsolute()) { + return false; + } + Path relative = parent.relativize(child); + return relative.equals(Paths.get(".")); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/SpringUtils.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/SpringUtils.java new file mode 100644 index 000000000..ade8f72ff --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/SpringUtils.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SpringUtils.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/SpringUtils.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.plugin.utils; + +import org.opengauss.admin.common.utils.StringUtils; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * Spring Tool + * + * @author xielibo + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware { + /** + * Spring ApplicationContext + */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringUtils.applicationContext = applicationContext; + } + + /** + * Get Bean + * + * @param name + * @throws BeansException + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException { + return (T) beanFactory.getBean(name); + } + + /** + * Get Bean + * + * @param clz + * @return + * @throws BeansException + */ + public static T getBean(Class clz) throws BeansException { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * Check containsBean + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) { + return beanFactory.containsBean(name); + } + + /** + * Determines whether the bean definition registered with the given name is a singleton or a prototype. + * If no bean definition corresponding to the given name is found, an exception (NoSuchBeanDefinitionException) will be thrown + * @param name + * @return boolean + * @throws NoSuchBeanDefinitionException + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return beanFactory.isSingleton(name); + } + + /** + * Get Type + * @param name + * @throws NoSuchBeanDefinitionException + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getType(name); + } + + /** + * If the given bean name has aliases in the bean definition, return those aliases + * + * @param name + * @return + * @throws NoSuchBeanDefinitionException + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getAliases(name); + } + + /** + * Get the aop proxy object + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) { + return (T) AopContext.currentProxy(); + } + + /** + * Get the current environment configuration, if no configuration returns null + * + */ + public static String[] getActiveProfiles() { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * Get the current environment configuration, when there are multiple environment configurations, only get the first one + * + */ + public static String getActiveProfile() { + final String[] activeProfiles = getActiveProfiles(); + return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/WsUtil.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/WsUtil.java new file mode 100644 index 000000000..4e0fad706 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/utils/WsUtil.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * WsUtil.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/utils/WsUtil.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.utils; + +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; +import org.opengauss.admin.plugin.domain.model.ops.WsSession; +import org.opengauss.admin.plugin.domain.model.ops.cache.WsConnectorManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import jakarta.websocket.Session; +import java.io.IOException; +import java.util.Objects; + +/** + * @author lhf + * @date 2022/8/15 09:32 + **/ +@Slf4j +@Component +public class WsUtil { + @Autowired + private WsConnectorManager wsConnectorManager; + + public void sendText(WsSession wsSession, String message) { + if (StrUtil.isEmpty(message)) { + return; + } + + if (Objects.nonNull(wsSession)) { + wsConnectorManager.getSession(wsSession.getSessionId()).ifPresent(wss -> { + Session session = wss.getSession(); + if (session.isOpen()) { + try { + session.getBasicRemote().sendText(message); + } catch (IOException e) { + log.error("Failed to send a message:{}", e.getMessage()); + } + } else { + log.error("Connection closed"); + } + }); + } + } + + public void close(WsSession wsSession) { + if (Objects.nonNull(wsSession)) { + wsConnectorManager.removeByVal(wsSession); + Session session = wsSession.getSession(); + + if (Objects.nonNull(session)) { + try { + session.close(); + } catch (IOException e) { + log.error("close websocket session error : {}", e.getMessage()); + } + } + } + } + + public void setErrorFlag(WsSession wsSession) { + sendText(wsSession, "FINAL_EXECUTE_EXIT_CODE:-1"); + } + + public void setSuccessFlag(WsSession wsSession) { + sendText(wsSession, "FINAL_EXECUTE_EXIT_CODE:0"); + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/BarChartParamsBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/BarChartParamsBody.java new file mode 100644 index 000000000..75a7efc1d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/BarChartParamsBody.java @@ -0,0 +1,83 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling; + +import java.util.List; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class BarChartParamsBody { + + private String title; + private String chartType; + private String dataFlowId; + private String operatorId; + private List indicator; + private List dimension; + private ShowType showType; + public void setTitle(String title) { + this.title = title; + } + public String getTitle() { + return title; + } + + public void setChartType(String chartType) { + this.chartType = chartType; + } + public String getChartType() { + return chartType; + } + + public void setDataFlowId(String dataFlowId) { + this.dataFlowId = dataFlowId; + } + public String getDataFlowId() { + return dataFlowId; + } + + public void setOperatorId(String operatorId) { + this.operatorId = operatorId; + } + public String getOperatorId() { + return operatorId; + } + + public void setIndicator(List indicator) { + this.indicator = indicator; + } + public List getIndicator() { + return indicator; + } + + public void setDimension(List dimension) { + this.dimension = dimension; + } + public List getDimension() { + return dimension; + } + + public void setShowType(ShowType showType) { + this.showType = showType; + } + public ShowType getShowType() { + return showType; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/Categories.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/Categories.java new file mode 100644 index 000000000..50f5e0ddb --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/Categories.java @@ -0,0 +1,57 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Categories { + + private String name; + private int type; + private List value; + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + + public void setType(int type) { + this.type = type; + } + public int getType() { + return type; + } + + public void setValue(List value) { + this.value = value; + } + public List getValue() { + return value; + } + + public CategoryBody toBody(String field) + { + CategoryBody body = new CategoryBody(field,this.name, this.type); + body.setValueList(this.getValue()); + return body; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/CategoryBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/CategoryBody.java new file mode 100644 index 000000000..137fb3eae --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/CategoryBody.java @@ -0,0 +1,64 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class CategoryBody { + + private String field; + private String categoryName; + private Integer type; + private List valueList; + + public CategoryBody(String field, String categoryName, Integer type) { + this.field = field; + this.categoryName = categoryName; + this.type = type; + } + + public void setField(String field) { + this.field = field; + } + public String getField() { + return field; + } + + public void setCategoryName(String field) { + this.categoryName = field; + } + public String getCategoryName() { + return categoryName; + } + public void setType(Integer type) { + this.type = type; + } + public Integer getType() { + return type == 0 ? 1 : type; + } + + public void setValueList(List valueList) { + this.valueList = valueList; + } + public List getValueList() { + return this.valueList; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/Dimension.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/Dimension.java new file mode 100644 index 000000000..83c7492c3 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/Dimension.java @@ -0,0 +1,76 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Data +public class Dimension { + + private String field; + private Boolean customFlag = false; + private int num; + private List categoryBodyList = new ArrayList<>(); + private List categoryName = new ArrayList<>(); + + public void setField(String field) { + this.field = field; + } + public String getField() { + return field; + } + + public void setNum(int num) { + this.num = num; + } + public int getNum() { + return this.num == 0 ? 100 : this.num; + } + + public void addCategoryBody(CategoryBody categoryBody) + { + if (!categoryName.contains(categoryBody.getCategoryName()) && categoryBodyList.size() < this.getNum()) { + categoryBodyList.add(categoryBody); + categoryName.add(categoryBody.getCategoryName()); + } + } + + public List indexListByValue(String value) + { + if (value == null) { + return Collections.emptyList(); + } + + List indexList = new ArrayList<>(); + categoryBodyList.forEach(categoryBody -> { + if (categoryBody.getType() == 1 && categoryBody.getValueList().contains(value)) { + indexList.add(categoryBodyList.indexOf(categoryBody)); + } + if (categoryBody.getType() == 2 && !categoryBody.getValueList().contains(value)) { + indexList.add(categoryBodyList.indexOf(categoryBody)); + } + }); + return indexList; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/GraphChartParamsBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/GraphChartParamsBody.java new file mode 100644 index 000000000..0e19eedb4 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/GraphChartParamsBody.java @@ -0,0 +1,92 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling; + +import org.opengauss.admin.plugin.vo.modeling.component.Node; +import org.opengauss.admin.plugin.vo.modeling.component.Relation; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class GraphChartParamsBody { + + private String title; + private String chartType; + private boolean loading; + private String dataFlowId; + private String operatorId; + private Indicator indicator; + private Node node; + private Relation relation; + + public void setTitle(String title) { + this.title = title; + } + public String getTitle() { + return title; + } + + public void setChartType(String chartType) { + this.chartType = chartType; + } + public String getChartType() { + return chartType; + } + + public void setLoading(boolean loading) { + this.loading = loading; + } + public boolean getLoading() { + return loading; + } + + public void setDataFlowId(String dataFlowId) { + this.dataFlowId = dataFlowId; + } + public String getDataFlowId() { + return dataFlowId; + } + + public void setOperatorId(String operatorId) { + this.operatorId = operatorId; + } + public String getOperatorId() { + return operatorId; + } + + public void setIndicator(Indicator indicator) { + this.indicator = indicator; + } + public Indicator getIndicator() { + return indicator; + } + + public void setNode(Node node) { + this.node = node; + } + public Node getNode() { + return node; + } + + public void setRelation(Relation relation) { + this.relation = relation; + } + public Relation getRelation() { + return relation; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/HeatmapChartParamsBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/HeatmapChartParamsBody.java new file mode 100644 index 000000000..4cfe96543 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/HeatmapChartParamsBody.java @@ -0,0 +1,90 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class HeatmapChartParamsBody { + + private String title; + private String chartType; + private String dataFlowId; + private String operatorId; + private Indicator indicator; + private Dimension x; + private Dimension y; + private List range; + + public void setTitle(String title) { + this.title = title; + } + public String getTitle() { + return title; + } + + public void setChartType(String chartType) { + this.chartType = chartType; + } + public String getChartType() { + return chartType; + } + + public void setDataFlowId(String dataFlowId) { + this.dataFlowId = dataFlowId; + } + public String getDataFlowId() { + return dataFlowId; + } + + public void setOperatorId(String operatorId) { + this.operatorId = operatorId; + } + public String getOperatorId() { + return operatorId; + } + + public void setIndicator(Indicator indicator) { + this.indicator = indicator; + } + public Indicator getIndicator() { + return indicator; + } + + public void setX(Dimension xDimension) { + this.x = xDimension; + } + public Dimension getX() { + return x; + } + + public void setY(Dimension yDimension) { + this.y = yDimension; + } + public Dimension getY() { + return y; + } + + public void setRange(List range) { + this.range = range; + } + public List getRange() { + return range; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/Indicator.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/Indicator.java new file mode 100644 index 000000000..5a5c3ced5 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/Indicator.java @@ -0,0 +1,48 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Indicator { + + private String field; + private String type; + private String unit; + public void setField(String field) { + this.field = field; + } + public String getField() { + return field; + } + + public void setType(String type) { + this.type = type; + } + public String getType() { + return type; + } + + public void setUnit(String unit) { + this.unit = unit; + } + public String getUnit() { + return unit; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/LineChartParamsBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/LineChartParamsBody.java new file mode 100644 index 000000000..a5764c2a9 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/LineChartParamsBody.java @@ -0,0 +1,83 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling; + +import java.util.List; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class LineChartParamsBody { + + private String title; + private String chartType; + private String dataFlowId; + private String operatorId; + private List indicator; + private List dimension; + private ShowType showType; + public void setTitle(String title) { + this.title = title; + } + public String getTitle() { + return title; + } + + public void setChartType(String chartType) { + this.chartType = chartType; + } + public String getChartType() { + return chartType; + } + + public void setDataFlowId(String dataFlowId) { + this.dataFlowId = dataFlowId; + } + public String getDataFlowId() { + return dataFlowId; + } + + public void setOperatorId(String operatorId) { + this.operatorId = operatorId; + } + public String getOperatorId() { + return operatorId; + } + + public void setIndicator(List indicator) { + this.indicator = indicator; + } + public List getIndicator() { + return indicator; + } + + public void setDimension(List dimension) { + this.dimension = dimension; + } + public List getDimension() { + return dimension; + } + + public void setShowType(ShowType showType) { + this.showType = showType; + } + public ShowType getShowType() { + return showType; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/MixChartParamsBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/MixChartParamsBody.java new file mode 100644 index 000000000..34852c78d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/MixChartParamsBody.java @@ -0,0 +1,74 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling; + +import java.util.List; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class MixChartParamsBody { + + private String title; + private String chartType; + private String dataFlowId; + private String operatorId; + private List subCharts; + private ShowType showType; + public void setTitle(String title) { + this.title = title; + } + public String getTitle() { + return title; + } + + public void setChartType(String chartType) { + this.chartType = chartType; + } + public String getChartType() { + return chartType; + } + + public void setDataFlowId(String dataFlowId) { + this.dataFlowId = dataFlowId; + } + public String getDataFlowId() { + return dataFlowId; + } + + public void setOperatorId(String operatorId) { + this.operatorId = operatorId; + } + public String getOperatorId() { + return operatorId; + } + + public void setSubCharts(List subCharts) { + this.subCharts = subCharts; + } + public List getSubCharts() { + return subCharts; + } + + public void setShowType(ShowType showType) { + this.showType = showType; + } + public ShowType getShowType() { + return showType; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/PieChartParamsBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/PieChartParamsBody.java new file mode 100644 index 000000000..9f29285c1 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/PieChartParamsBody.java @@ -0,0 +1,81 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class PieChartParamsBody { + + private String title; + private String chartType; + private String dataFlowId; + private String operatorId; + private Indicator indicator; + private Dimension dimension; + private ShowType showType; + + public void setTitle(String title) { + this.title = title; + } + public String getTitle() { + return title; + } + + public void setChartType(String chartType) { + this.chartType = chartType; + } + public String getChartType() { + return chartType; + } + + public void setDataFlowId(String dataFlowId) { + this.dataFlowId = dataFlowId; + } + public String getDataFlowId() { + return dataFlowId; + } + + public void setOperatorId(String operatorId) { + this.operatorId = operatorId; + } + public String getOperatorId() { + return operatorId; + } + + public void setIndicator(Indicator indicator) { + this.indicator = indicator; + } + public Indicator getIndicator() { + return indicator; + } + + public void setDimension(Dimension dimension) { + this.dimension = dimension; + } + public Dimension getDimension() { + return dimension; + } + + public void setShowType(ShowType showType) { + this.showType = showType; + } + public ShowType getShowType() { + return showType; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/ScatterChartParamsBody.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/ScatterChartParamsBody.java new file mode 100644 index 000000000..8fd10e2c8 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/ScatterChartParamsBody.java @@ -0,0 +1,97 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling; + +import lombok.Data; +import org.opengauss.admin.plugin.vo.modeling.component.Location; + +import java.util.List; +import java.util.Objects; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Data +public class ScatterChartParamsBody { + + private String title; + private String chartType; + private String dataFlowId; + private String operatorId; + private Indicator indicator; + private Location location; + private Double zoom; + private List center; + private List colorConfig; + + public void setTitle(String title) { + this.title = title; + } + public String getTitle() { + return title; + } + + public void setChartType(String chartType) { + this.chartType = chartType; + } + public String getChartType() { + return chartType; + } + + public void setDataFlowId(String dataFlowId) { + this.dataFlowId = dataFlowId; + } + public String getDataFlowId() { + return dataFlowId; + } + + public void setOperatorId(String operatorId) { + this.operatorId = operatorId; + } + public String getOperatorId() { + return operatorId; + } + + public void setIndicator(Indicator indicator) { + this.indicator = indicator; + } + public Indicator getIndicator() { + return indicator; + } + + public void setLocation(Location location) { + this.location = location; + } + public Location getLocation() { + return location; + } + + public void setCenter(List center) { + this.center = center; + } + public List getCenter() { + return center.stream().limit(2).anyMatch(Objects::isNull) ? List.of(0d, 0d) : center; + } + + public void setColorConfig(List colorConfig) { + this.colorConfig = colorConfig; + } + public List getColorConfig() { + return colorConfig; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/ShowType.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/ShowType.java new file mode 100644 index 000000000..a3603a76c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/ShowType.java @@ -0,0 +1,81 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class ShowType { + + private int key; + private String dimension; + private String field; + private Boolean percentage; + private String dateField; + private Integer particle; + private Integer compareWay; + + public void setKey(int key) { + this.key = key; + } + public int getKey() { + return key; + } + + public void setDimension(String dimension) { + this.dimension = dimension; + } + public String getDimension() { + return dimension; + } + + public void setField(String field) { + this.field = field; + } + public String getField() { + return field; + } + + public void setPercentage(Boolean percentage) { + this.percentage = percentage; + } + public Boolean getPercentage() { + return percentage; + } + + public void setDateField(String dateField) { + this.dateField = dateField; + } + public String getDateField() { + return dateField; + } + + public void setParticle(Integer particle) { + this.particle = particle; + } + public Integer getParticle() { + return particle; + } + + public void setCompareWay(Integer compareWay) { + this.compareWay = compareWay; + } + public Integer getCompareWay() { + return compareWay; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/SubCharts.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/SubCharts.java new file mode 100644 index 000000000..a71eaf47c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/SubCharts.java @@ -0,0 +1,31 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling; + +import lombok.Data; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +@Data +public class SubCharts { + private String chartType; + private List indicator; + private List dimension; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/A.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/A.java new file mode 100644 index 000000000..0d76f6e0a --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/A.java @@ -0,0 +1,49 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class A { + + private String color; + private int lineHeight; + private String align; + public void setColor(String color) { + this.color = color; + } + public String getColor() { + return color; + } + + public void setLineHeight(int lineHeight) { + this.lineHeight = lineHeight; + } + public int getLineHeight() { + return lineHeight; + } + + public void setAlign(String align) { + this.align = align; + } + public String getAlign() { + return align; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/AxisLabel.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/AxisLabel.java new file mode 100644 index 000000000..ceef37296 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/AxisLabel.java @@ -0,0 +1,37 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class AxisLabel { + + private String formatter; + + public AxisLabel(String formatter) { + this.formatter = formatter; + } + + public void setFormatter(String formatter) { + this.formatter = formatter; + } + public String getFormatter() { + return formatter; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/AxisLine.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/AxisLine.java new file mode 100644 index 000000000..af4cda112 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/AxisLine.java @@ -0,0 +1,41 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class AxisLine { + + private boolean show; + private LineStyle lineStyle; + public void setShow(boolean show) { + this.show = show; + } + public boolean getShow() { + return show; + } + + public void setLineStyle(LineStyle lineStyle) { + this.lineStyle = lineStyle; + } + public LineStyle getLineStyle() { + return lineStyle; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/B.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/B.java new file mode 100644 index 000000000..9f83428a1 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/B.java @@ -0,0 +1,56 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class B { + + private String color; + private int fontSize; + private String fontWeight; + private int lineHeight; + public void setColor(String color) { + this.color = color; + } + public String getColor() { + return color; + } + + public void setFontSize(int fontSize) { + this.fontSize = fontSize; + } + public int getFontSize() { + return fontSize; + } + + public void setFontWeight(String fontWeight) { + this.fontWeight = fontWeight; + } + public String getFontWeight() { + return fontWeight; + } + + public void setLineHeight(int lineHeight) { + this.lineHeight = lineHeight; + } + public int getLineHeight() { + return lineHeight; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Bmap.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Bmap.java new file mode 100644 index 000000000..8a1918761 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Bmap.java @@ -0,0 +1,72 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Bmap { + + private String key; + private List center; + private int zoom; + private boolean roam; + private MapStyle mapStyle; + + public Bmap setKey(String key) { + this.key = key; + return this; + } + public String getKey() { + return key; + } + + public Bmap setCenter(List center) { + this.center = center; + return this; + } + public List getCenter() { + return center; + } + + public Bmap setZoom(int zoom) { + this.zoom = zoom; + return this; + } + public int getZoom() { + return zoom; + } + + public Bmap setRoam(boolean roam) { + this.roam = roam; + return this; + } + public boolean getRoam() { + return roam; + } + + public Bmap setMapStyle(MapStyle mapStyle) { + this.mapStyle = mapStyle; + return this; + } + public MapStyle getMapStyle() { + return mapStyle; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/DataZoomItem.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/DataZoomItem.java new file mode 100644 index 000000000..60d0254c7 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/DataZoomItem.java @@ -0,0 +1,52 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class DataZoomItem { + + private String type; + private Integer start; + private Integer end; + + public DataZoomItem setType(String type) { + this.type = type; + return this; + } + public String getType() { + return type; + } + + public DataZoomItem setStart(Integer start) { + this.start = start; + return this; + } + public Integer getStart() { + return start; + } + + public DataZoomItem setEnd(Integer end) { + this.end = end; + return this; + } + public Integer getEnd() { + return end; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Emphasis.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Emphasis.java new file mode 100644 index 000000000..80e0adf81 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Emphasis.java @@ -0,0 +1,40 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Emphasis { + + private Label label; + private ItemStyle itemStyle; + public Emphasis setLabel(Label label) { + this.label = label; + return this; + } + public Label getLabel() { + return label; + } + public Emphasis setItemStyle(ItemStyle itemStyle) { + this.itemStyle = itemStyle; + return this; + } + public ItemStyle getItemStyle() { + return itemStyle; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Encode.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Encode.java new file mode 100644 index 000000000..4bba671eb --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Encode.java @@ -0,0 +1,33 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Encode { + + private int value; + public Encode setValue(int value) { + this.value = value; + return this; + } + public int getValue() { + return value; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Geo.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Geo.java new file mode 100644 index 000000000..704e61189 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Geo.java @@ -0,0 +1,63 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Geo { + + private String map; + private List center; + private Double zoom; + private boolean roam; + + public Geo setMap(String map) { + this.map = map; + return this; + } + public String getMap() { + return map; + } + + public Geo setCenter(List center) { + this.center = center; + return this; + } + public List getCenter() { + return center; + } + + public Geo setZoom(Double zoom) { + this.zoom = zoom; + return this; + } + public Double getZoom() { + return zoom; + } + + public Geo setRoam(boolean roam) { + this.roam = roam; + return this; + } + public boolean getRoam() { + return roam; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/GraphLink.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/GraphLink.java new file mode 100644 index 000000000..29d4bef06 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/GraphLink.java @@ -0,0 +1,40 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class GraphLink { + + private String source; + private String target; + public void setSource(String source) { + this.source = source; + } + public String getSource() { + return source; + } + + public void setTarget(String target) { + this.target = target; + } + public String getTarget() { + return target; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/GraphNode.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/GraphNode.java new file mode 100644 index 000000000..61a5ceeb3 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/GraphNode.java @@ -0,0 +1,89 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class GraphNode { + + private String id; + private String name; + private float symbolSize; + private float x; + private float y; + private float value; + private int category; + private Label label; + + public void setId(String id) { + this.id = id; + } + public String getId() { + return id; + } + + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + + public void setSymbolSize(float symbolSize) { + this.symbolSize = symbolSize; + } + public float getSymbolSize() { + return symbolSize; + } + + public void setX(float x) { + this.x = x; + } + public float getX() { + return x; + } + + public void setY(float y) { + this.y = y; + } + public float getY() { + return y; + } + + public void setValue(float value) { + this.value = value; + } + public float getValue() { + return value; + } + + public void setCategory(int category) { + this.category = category; + } + public int getCategory() { + return category; + } + + public void setLabel(Label label) { + this.label = label; + } + public Label getLabel() { + return label; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/GraphSeries.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/GraphSeries.java new file mode 100644 index 000000000..881634ab7 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/GraphSeries.java @@ -0,0 +1,108 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +import java.util.List; +import java.util.Map; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class GraphSeries { + + private String name; + private String type; + private String layout; + private List data; + private List links; + private List> categories; + + private boolean roam; + private Label label; + private LineStyle lineStyle; + private Emphasis emphasis; + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + + public void setType(String type) { + this.type = type; + } + public String getType() { + return type; + } + + public void setLayout(String layout) { + this.layout = layout; + } + public String getLayout() { + return layout; + } + + public void setData(List data) { + this.data = data; + } + public List getData() { + return data; + } + + public void setLinks(List links) { + this.links = links; + } + public List getLinks() { + return links; + } + + public void setCategories(List> categories) { + this.categories = categories; + } + public List> getCategories() { + return categories; + } + + public void setRoam(boolean roam) { + this.roam = roam; + } + public boolean getRoam() { + return roam; + } + + public void setLabel(Label label) { + this.label = label; + } + public Label getLabel() { + return label; + } + + public void setLineStyle(LineStyle lineStyle) { + this.lineStyle = lineStyle; + } + public LineStyle getLineStyle() { + return lineStyle; + } + + public void setEmphasis(Emphasis emphasis) { + this.emphasis = emphasis; + } + public Emphasis getEmphasis() { + return emphasis; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Grid.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Grid.java new file mode 100644 index 000000000..2d333c2b6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Grid.java @@ -0,0 +1,61 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Grid { + + private String left; + private String right; + private String bottom; + private boolean containLabel; + public Grid setLeft(String left) { + this.left = left; + return this; + } + public String getLeft() { + return left; + } + + public Grid setRight(String right) { + this.right = right; + return this; + } + public String getRight() { + return right; + } + + public Grid setBottom(String bottom) { + this.bottom = bottom; + return this; + } + public String getBottom() { + return bottom; + } + + public Grid setContainLabel(boolean containLabel) { + this.containLabel = containLabel; + return this; + } + public boolean getContainLabel() { + return containLabel; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/HeatmapSeries.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/HeatmapSeries.java new file mode 100644 index 000000000..3b5fa827c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/HeatmapSeries.java @@ -0,0 +1,67 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class HeatmapSeries { + + private String name; + private String type; + private List> data; + private Label label; + private Emphasis emphasis; + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + + public void setType(String type) { + this.type = type; + } + public String getType() { + return type; + } + + public HeatmapSeries setData(List> data) { + this.data = data; + return this; + } + public List> getData() { + return data; + } + + public void setLabel(Label label) { + this.label = label; + } + public Label getLabel() { + return label; + } + + public void setEmphasis(Emphasis emphasis) { + this.emphasis = emphasis; + } + public Emphasis getEmphasis() { + return emphasis; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Hr.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Hr.java new file mode 100644 index 000000000..458dd3ef1 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Hr.java @@ -0,0 +1,58 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +import java.util.Date; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Hr { + + private String borderColor; + private Date width; + private int borderWidth; + private int height; + public void setBorderColor(String borderColor) { + this.borderColor = borderColor; + } + public String getBorderColor() { + return borderColor; + } + + public void setWidth(Date width) { + this.width = width; + } + public Date getWidth() { + return width; + } + + public void setBorderWidth(int borderWidth) { + this.borderWidth = borderWidth; + } + public int getBorderWidth() { + return borderWidth; + } + + public void setHeight(int height) { + this.height = height; + } + public int getHeight() { + return height; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/InRange.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/InRange.java new file mode 100644 index 000000000..82d4c8d54 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/InRange.java @@ -0,0 +1,34 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class InRange { + + private List color; + public InRange setColor(List color) { + this.color = color; + return this; + } + public List getColor() { + return color; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/ItemStyle.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/ItemStyle.java new file mode 100644 index 000000000..8b4eed386 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/ItemStyle.java @@ -0,0 +1,51 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class ItemStyle { + + private String color; + private int shadowBlur; + private String shadowColor; + + public ItemStyle setColor(String color) { + this.color = color; + return this; + } + public String getColor() { + return color; + } + + public ItemStyle setShadowBlur(int shadowBlur) { + this.shadowBlur = shadowBlur; + return this; + } + public int getShadowBlur() { + return shadowBlur; + } + + public ItemStyle setShadowColor(String shadowColor) { + this.shadowColor = shadowColor; + return this; + } + public String getShadowColor() { + return shadowColor; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Label.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Label.java new file mode 100644 index 000000000..1fb6783e3 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Label.java @@ -0,0 +1,60 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Label { + + private String position; + private int fontSize; + private boolean show; + private String formatter; + + public Label setPosition(String position) { + this.position = position; + return this; + } + public String getPosition() { + return position; + } + + public Label setFontSize(int fontSize) { + this.fontSize = fontSize; + return this; + } + public int getFontSize() { + return fontSize == 0 ? 10 : fontSize; + } + + public Label setShow(boolean show) { + this.show = show; + return this; + } + public boolean getShow() { + return show; + } + + public Label setFormatter(String formatter) { + this.formatter = formatter; + return this; + } + public String getFormatter() { + return formatter; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/LabelLine.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/LabelLine.java new file mode 100644 index 000000000..446b8b4ca --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/LabelLine.java @@ -0,0 +1,40 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class LabelLine { + + private int length; + public LabelLine setLength(int length) { + this.length = length; + return this; + } + public int getLength() { + return length; + } + private boolean show; + public LabelLine setShow(boolean show) { + this.show = show; + return this; + } + public boolean getShow() { + return show; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Legend.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Legend.java new file mode 100644 index 000000000..e06226b61 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Legend.java @@ -0,0 +1,76 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Legend { + + private List data; + private String type; + private String width; + + private int bottom; + private int top; + + public Legend(String type) { + this.type = type; + } + + public Legend setData(List data) { + this.data = data; + return this; + } + public List getData() { + return data; + } + + public String getType() { + return this.type; + } + + public Legend setBottom(int bottom) { + this.bottom = bottom; + return this; + } + + public int getBottom() { + return this.bottom; + } + + public Legend setTop(int top) { + this.top = top; + return this; + } + + public int getTop() { + return this.top; + } + + public Legend setWidth(String width) { + this.width = width; + return this; + } + + public String getWidth() { + return this.width; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/LineStyle.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/LineStyle.java new file mode 100644 index 000000000..47550ca22 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/LineStyle.java @@ -0,0 +1,41 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class LineStyle { + + private String color; + private float curveness; + public LineStyle setColor(String color) { + this.color = color; + return this; + } + public String getColor() { + return color; + } + + public LineStyle setCurveness(float curveness) { + this.curveness = curveness; + return this; + } + public float getCurveness() { + return this.curveness; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Location.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Location.java new file mode 100644 index 000000000..d82c411c0 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Location.java @@ -0,0 +1,64 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Location { + + private String field; + private int type; + private Long commonValue; + private String diyValue; + private int area; + public void setField(String field) { + this.field = field; + } + public String getField() { + return field; + } + + public void setType(int type) { + this.type = type; + } + public int getType() { + return type; + } + + public void setCommonValue(Long commonValue) { + this.commonValue = commonValue; + } + public Long getCommonValue() { + return commonValue; + } + + public void setDiyValue(String diyValue) { + this.diyValue = diyValue; + } + public String getDiyValue() { + return diyValue; + } + + public void setArea(int area) { + this.area = area; + } + public int getArea() { + return area; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/MapData.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/MapData.java new file mode 100644 index 000000000..2f0ecc987 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/MapData.java @@ -0,0 +1,41 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +import java.util.Date; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class MapData { + + private String name; + private Date value; + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + + public void setValue(Date value) { + this.value = value; + } + public Date getValue() { + return value; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/MapSeries.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/MapSeries.java new file mode 100644 index 000000000..2eb54071d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/MapSeries.java @@ -0,0 +1,90 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class MapSeries { + + private String name; + private String type; + private String coordinateSystem; + private List data; + private Encode encode; + private Label label; + private Emphasis emphasis; + private ItemStyle itemStyle; + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + + public void setType(String type) { + this.type = type; + } + public String getType() { + return type; + } + + public void setCoordinateSystem(String coordinateSystem) { + this.coordinateSystem = coordinateSystem; + } + public String getCoordinateSystem() { + return coordinateSystem; + } + + public void setData(List data) { + this.data = data; + } + public List getData() { + return data; + } + + public void setEncode(Encode encode) { + this.encode = encode; + } + public Encode getEncode() { + return encode; + } + + public void setLabel(Label label) { + this.label = label; + } + public Label getLabel() { + return label; + } + + public void setEmphasis(Emphasis emphasis) { + this.emphasis = emphasis; + } + public Emphasis getEmphasis() { + return emphasis; + } + + public void setItemStyle(ItemStyle itemStyle) { + this.itemStyle = itemStyle; + } + public ItemStyle getItemStyle() { + return itemStyle; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/MapStyle.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/MapStyle.java new file mode 100644 index 000000000..1e2cb38a4 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/MapStyle.java @@ -0,0 +1,47 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class MapStyle { + + private List styleJson; + public MapStyle setStyleJson(List styleJson) { + this.styleJson = styleJson; + return this; + } + public List getStyleJson() { + return styleJson; + } + + public MapStyle setStyleJsonDefault() { + this.styleJson = List.of( + new StyleJson().setFeatureType("water").setElementType("all").setStylers(new Stylers().setColor("#044161")), + new StyleJson().setFeatureType("land").setElementType("all").setStylers(new Stylers().setColor("#004981")), + new StyleJson().setFeatureType("boundary").setElementType("geometry").setStylers(new Stylers().setColor("#064f85")), + new StyleJson().setFeatureType("railway").setElementType("all").setStylers(new Stylers().setColor("#004981")), + new StyleJson().setFeatureType("highway").setElementType("geometry").setStylers(new Stylers().setColor("#004981")), + new StyleJson().setFeatureType("highway").setElementType("geometry.fill").setStylers(new Stylers().setColor("##005b96")), + new StyleJson().setFeatureType("water").setElementType("all").setStylers(new Stylers().setColor("#044161")) + ); + return this; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Node.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Node.java new file mode 100644 index 000000000..8d0d2d2df --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Node.java @@ -0,0 +1,65 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Node { + + private String id; + + private String name; + private String x; + private String y; + private String size; + public void setId(String id) { + this.id = id; + } + public String getId() { + return id; + } + + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + + public void setX(String x) { + this.x = x; + } + public String getX() { + return x; + } + + public void setY(String y) { + this.y = y; + } + public String getY() { + return y; + } + + public void setSize(String size) { + this.size = size; + } + public String getSize() { + return size; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Per.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Per.java new file mode 100644 index 000000000..c9e97c349 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Per.java @@ -0,0 +1,57 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +import java.util.Date; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Per { + + private String color; + private String backgroundColor; + private Date padding; + private int borderRadius; + public void setColor(String color) { + this.color = color; + } + public String getColor() { + return color; + } + + public void setBackgroundColor(String backgroundColor) { + this.backgroundColor = backgroundColor; + } + public String getBackgroundColor() { + return backgroundColor; + } + + public void setPadding(Date padding) { + this.padding = padding; + } + public Date getPadding() { + return padding; + } + + public void setBorderRadius(int borderRadius) { + this.borderRadius = borderRadius; + } + public int getBorderRadius() { + return borderRadius; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/PieData.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/PieData.java new file mode 100644 index 000000000..15da1a603 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/PieData.java @@ -0,0 +1,44 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class PieData { + + private float value; + private String name; + public void setValue(float value) { + this.value = value; + } + public float getValue() { + return value; + } + + public void addValue(float value) { + this.value += value; + } + + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/PieSeries.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/PieSeries.java new file mode 100644 index 000000000..f35c15e0b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/PieSeries.java @@ -0,0 +1,88 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +import java.util.List; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class PieSeries { + + private String name; + private String type; + private List data; + private String selectedMode; + private List radius; + private Label label; + private LabelLine labelLine; + + public PieSeries setName(String name) { + this.name = name; + return this; + } + public String getName() { + return name; + } + + public PieSeries setType(String type) { + this.type = type; + return this; + } + public String getType() { + return type; + } + + public void setSelectedMode(String selectedMode) { + this.selectedMode = selectedMode; + } + public String getSelectedMode() { + return selectedMode; + } + + public void setRadius(List radius) { + this.radius = radius; + } + public List getRadius() { + return radius; + } + + public void setLabel(Label label) { + this.label = label; + } + public Label getLabel() { + return label; + } + + public void setLabelLine(LabelLine labelLine) { + this.labelLine = labelLine; + } + public LabelLine getLabelLine() { + return labelLine; + } + + public PieSeries setData(List data) { + this.data = data; + return this; + } + + public List getData() { + return data; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Relation.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Relation.java new file mode 100644 index 000000000..a139fd590 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Relation.java @@ -0,0 +1,48 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Relation { + + private String out; + private String in; + private String value; + public void setOut(String out) { + this.out = out; + } + public String getOut() { + return out; + } + + public void setIn(String in) { + this.in = in; + } + public String getIn() { + return in; + } + + public void setValue(String value) { + this.value = value; + } + public String getValue() { + return value; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Rich.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Rich.java new file mode 100644 index 000000000..bbad0bb4d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Rich.java @@ -0,0 +1,56 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Rich { + + private A a; + private Hr hr; + private B b; + private Per per; + public void setA(A a) { + this.a = a; + } + public A getA() { + return a; + } + + public void setHr(Hr hr) { + this.hr = hr; + } + public Hr getHr() { + return hr; + } + + public void setB(B b) { + this.b = b; + } + public B getB() { + return b; + } + + public void setPer(Per per) { + this.per = per; + } + public Per getPer() { + return per; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/ScatterSeries.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/ScatterSeries.java new file mode 100644 index 000000000..1293526c3 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/ScatterSeries.java @@ -0,0 +1,134 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +import java.util.List; +import java.util.Map; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ + +public class ScatterSeries { + + private String name; + private String map; + private String type; + private boolean roam; + + private String coordinateSystem; + private List> data; + private String symbolSize; + private Encode encode; + private Label label; + private Emphasis emphasis; + private ItemStyle itemStyle; + private int geoIndex; + private String selectedMode; + + public void setRoam(boolean roam) { + this.roam = roam; + } + public boolean getRoam() { + return roam; + } + + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + + public void setMap(String map) { + this.map = map; + } + public String getMap() { + return map; + } + + public void setType(String type) { + this.type = type; + } + public String getType() { + return type; + } + + public void setCoordinateSystem(String coordinateSystem) { + this.coordinateSystem = coordinateSystem; + } + public String getCoordinateSystem() { + return coordinateSystem; + } + + public void setData(List> data) { + this.data = data; + } + public List> getData() { + return data; + } + + public void setSymbolSize(String symbolSize) { + this.symbolSize = symbolSize; + } + public String getSymbolSize() { + return symbolSize; + } + + public void setEncode(Encode encode) { + this.encode = encode; + } + public Encode getEncode() { + return encode; + } + + public void setLabel(Label label) { + this.label = label; + } + public Label getLabel() { + return label; + } + + public void setEmphasis(Emphasis emphasis) { + this.emphasis = emphasis; + } + public Emphasis getEmphasis() { + return emphasis; + } + + public void setItemStyle(ItemStyle itemStyle) { + this.itemStyle = itemStyle; + } + public ItemStyle getItemStyle() { + return itemStyle; + } + + public void setGeoIndex(int geoIndex) { + this.geoIndex = geoIndex; + } + public int getGeoIndex() { + return geoIndex; + } + + public void setSelectedMode(String selectedMode) { + this.selectedMode = selectedMode; + } + public String getSelectedMode() { + return selectedMode; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Series.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Series.java new file mode 100644 index 000000000..63b1558fa --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Series.java @@ -0,0 +1,81 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +import java.util.List; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Series { + + private String name; + private String type; + private String stack; + private List data; + private int yAxisIndex; + private Label label; + + public Series setName(String name) { + this.name = name; + return this; + } + public String getName() { + return name; + } + + public Series setType(String type) { + this.type = type; + return this; + } + public String getType() { + return type; + } + + public Series setStack(String stack) { + this.stack = stack; + return this; + } + public String getStack() { + return stack; + } + + public Series setData(List data) { + this.data = data; + return this; + } + public List getData() { + return data; + } + + public Series setYAxisIndex(int yAxisIndex) { + this.yAxisIndex = yAxisIndex; + return this; + } + public int getYAxisIndex() { + return yAxisIndex; + } + + public Series setLabel(Label label) { + this.label = label; + return this; + } + public Label getLabel() { + return label; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/SplitArea.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/SplitArea.java new file mode 100644 index 000000000..c6588b280 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/SplitArea.java @@ -0,0 +1,33 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class SplitArea { + + private boolean show; + public SplitArea setShow(boolean show) { + this.show = show; + return this; + } + public boolean getShow() { + return show; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/StyleJson.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/StyleJson.java new file mode 100644 index 000000000..e2a221c65 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/StyleJson.java @@ -0,0 +1,51 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class StyleJson { + + private String featureType; + private String elementType; + private Stylers stylers; + public StyleJson setFeatureType(String featureType) { + this.featureType = featureType; + return this; + } + public String getFeatureType() { + return featureType; + } + + public StyleJson setElementType(String elementType) { + this.elementType = elementType; + return this; + } + public String getElementType() { + return elementType; + } + + public StyleJson setStylers(Stylers stylers) { + this.stylers = stylers; + return this; + } + public Stylers getStylers() { + return stylers; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Stylers.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Stylers.java new file mode 100644 index 000000000..522c5a940 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Stylers.java @@ -0,0 +1,33 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Stylers { + + private String color; + public Stylers setColor(String color) { + this.color = color; + return this; + } + public String getColor() { + return color; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Title.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Title.java new file mode 100644 index 000000000..6e9dfe88a --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Title.java @@ -0,0 +1,34 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Title { + + private String text; + public Title setText(String text) { + this.text = text; + return this; + } + public String getText() { + return text; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Tooltip.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Tooltip.java new file mode 100644 index 000000000..f0c529265 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/Tooltip.java @@ -0,0 +1,43 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class Tooltip { + + private String trigger; + private String formatter; + + public Tooltip setTrigger(String trigger) { + this.trigger = trigger; + return this; + } + public String getTrigger() { + return trigger; + } + + public Tooltip setFormatter(String formatter) { + this.formatter = formatter; + return this; + } + public String getFormatter() { + return formatter; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/VisualMap.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/VisualMap.java new file mode 100644 index 000000000..18902886d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/VisualMap.java @@ -0,0 +1,97 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +import java.util.List; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class VisualMap { + + private int min; + private int max; + private List text; + private boolean calculable; + private String orient; + private String left; + private String bottom; + private InRange inRange; + + public VisualMap setMin(int min) { + this.min = min; + return this; + } + public int getMin() { + return min; + } + + public VisualMap setMax(int max) { + this.max = max; + return this; + } + public int getMax() { + return max; + } + + public VisualMap setCalculable(boolean calculable) { + this.calculable = calculable; + return this; + } + public boolean getCalculable() { + return calculable; + } + + public VisualMap setOrient(String orient) { + this.orient = orient; + return this; + } + public String getOrient() { + return orient; + } + + public VisualMap setLeft(String left) { + this.left = left; + return this; + } + public String getLeft() { + return left; + } + + public VisualMap setBottom(String bottom) { + this.bottom = bottom; + return this; + } + public String getBottom() { + return bottom; + } + public VisualMap setInRange(InRange inRange) { + this.inRange = inRange; + return this; + } + public InRange getInRange() { + return inRange; + } + public VisualMap setText(List text) { + this.text = text; + return this; + } + public List getText() { + return text; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/XAxis.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/XAxis.java new file mode 100644 index 000000000..51bd8ff22 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/XAxis.java @@ -0,0 +1,64 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; + +import java.util.List; + +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class XAxis { + + private String type; + private boolean boundaryGap; + private SplitArea splitArea; + private List data; + + public XAxis setType(String type) { + this.type = type; + return this; + } + public String getType() { + return type; + } + + public XAxis setBoundaryGap(boolean boundaryGap) { + this.boundaryGap = boundaryGap; + return this; + } + public boolean getBoundaryGap() { + return boundaryGap; + } + + public XAxis setSplitArea(SplitArea splitArea) { + this.splitArea = splitArea; + return this; + } + public SplitArea getSplitArea() { + return splitArea; + } + + public XAxis setData(List data) { + this.data = data; + return this; + } + public List getData() { + return data; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/YAxis.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/YAxis.java new file mode 100644 index 000000000..30eeac26f --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/modeling/component/YAxis.java @@ -0,0 +1,88 @@ +/** + Copyright (c) 2020 Huawei Technologies Co.,Ltd. + Copyright (c) 2021 openGauss Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless 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. + */ +package org.opengauss.admin.plugin.vo.modeling.component; +/** + * @author LZW + * @date 2022/10/29 22:38 + **/ +public class YAxis { + + private String type; + private String name; + private String position; + private boolean alignTicks; + private AxisLine axisLine; + private AxisLabel axisLabel; + private float min; + + public YAxis setType(String type) { + this.type = type; + return this; + } + public String getType() { + return type; + } + + public YAxis setName(String name) { + this.name = name; + return this; + } + public String getName() { + return name; + } + + public YAxis setPosition(String position) { + this.position = position; + return this; + } + public String getPosition() { + return position; + } + + public YAxis setAlignTicks(boolean alignTicks) { + this.alignTicks = alignTicks; + return this; + } + public boolean getAlignTicks() { + return alignTicks; + } + + public YAxis setAxisLine(AxisLine axisLine) { + this.axisLine = axisLine; + return this; + } + public AxisLine getAxisLine() { + return axisLine; + } + + public YAxis setAxisLabel(AxisLabel axisLabel) { + this.axisLabel = axisLabel; + return this; + } + public AxisLabel getAxisLabel() { + return axisLabel; + } + + public YAxis setMin(float min) { + this.min = min; + return this; + } + public float getMin() { + return min; + } + +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/AuditLogVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/AuditLogVO.java new file mode 100644 index 000000000..7cc4c3f54 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/AuditLogVO.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * AuditLogVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/AuditLogVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/12 17:39 + **/ +@Data +public class AuditLogVO { + private String time; + private String type; + private String result; + private String userid; + private String username; + private String database; + private String client_conninfo; + private String object_name; + private String detail_info; + private String node_name; + private String thread_id; + private String local_port; + private String remote_port; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/BackupInputDto.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/BackupInputDto.java new file mode 100644 index 000000000..01b6362b0 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/BackupInputDto.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BackupInputDto.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/BackupInputDto.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; + +/** + * @author lhf + * @date 2022/11/2 16:48 + **/ +@Data +public class BackupInputDto { + @NotBlank(message = "Cluster ID cannot be empty") + private String clusterId; + @NotBlank(message = "Backup directory cannot be empty") + private String backupPath; + + private String remark; + + private String businessId; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/BackupVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/BackupVO.java new file mode 100644 index 000000000..5303949c6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/BackupVO.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * BackupVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/BackupVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import org.opengauss.admin.plugin.domain.entity.ops.OpsBackupEntity; +import lombok.Data; + +/** + * @author lhf + * @date 2022/11/13 09:44 + **/ +@Data +public class BackupVO extends OpsBackupEntity { + private String publicIp; + private String privateIp; + private String hostname; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckClusterVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckClusterVO.java new file mode 100644 index 000000000..c8750d1c6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckClusterVO.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckClusterVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckClusterVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 18:35 + **/ +@Data +public class CheckClusterVO { + private CheckItemVO checkClusterState; + private CheckItemVO checkDBParams; + private CheckItemVO checkDebugSwitch; + private CheckItemVO checkDirPermissions; + private CheckItemVO checkEnvProfile; + private CheckItemVO checkReadonlyMode; + private CheckItemVO checkDilateSysTab; + private CheckItemVO checkProStartTime; + private CheckItemVO checkMpprcFile; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckDbVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckDbVO.java new file mode 100644 index 000000000..8617b58de --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckDbVO.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckDbVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckDbVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 18:36 + **/ +@Data +public class CheckDbVO { + private CheckItemVO checkCurConnCount; + private CheckItemVO checkCursorNum; + private CheckItemVO checkPgxcgroup; + private CheckItemVO checkTableSpace; + private CheckItemVO checkSysadminUser; + private CheckItemVO checkHashIndex; + private CheckItemVO checkPgxcRedistb; + private CheckItemVO checkNodeGroupName; + private CheckItemVO checkTDDate; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckDeviceVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckDeviceVO.java new file mode 100644 index 000000000..5065db1ec --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckDeviceVO.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckDeviceVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckDeviceVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 18:36 + **/ +@Data +public class CheckDeviceVO { + private CheckItemVO checkBlockdev; + private CheckItemVO checkDiskFormat; + private CheckItemVO checkSpaceUsage; + private CheckItemVO checkInodeUsage; + private CheckItemVO checkSwapMemory; + private CheckItemVO checkLogicalBlock; + private CheckItemVO checkIOrequestqueue; + private CheckItemVO checkMaxAsyIOrequests; + private CheckItemVO checkIOConfigure; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckItemVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckItemVO.java new file mode 100644 index 000000000..39e5554f6 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckItemVO.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckItemVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckItemVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 18:38 + **/ +@Data +public class CheckItemVO { + private String name; + private String status; + private String msg; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckNetworkVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckNetworkVO.java new file mode 100644 index 000000000..b925eb363 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckNetworkVO.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckNetworkVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckNetworkVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 18:35 + **/ +@Data +public class CheckNetworkVO { + private CheckItemVO checkMTU; + private CheckItemVO checkPing; + private CheckItemVO checkRXTX; + private CheckItemVO checkNetWorkDrop; + private CheckItemVO checkMultiQueue; + private CheckItemVO checkRouting; + private CheckItemVO checkNICModel; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckOSVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckOSVO.java new file mode 100644 index 000000000..02dc2215b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckOSVO.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckOSVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckOSVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 18:35 + **/ +@Data +public class CheckOSVO { + private CheckItemVO checkEncoding; + private CheckItemVO checkFirewall; + private CheckItemVO checkKernelVer; + private CheckItemVO checkMaxHandle; + private CheckItemVO checkNTPD; + private CheckItemVO checkOSVer; + private CheckItemVO checkSysParams; + private CheckItemVO checkTHP; + private CheckItemVO checkTimeZone; + private CheckItemVO checkCPU; + private CheckItemVO checkSshdService; + private CheckItemVO checkSshdConfig; + private CheckItemVO checkCrondService; + private CheckItemVO checkStack; + private CheckItemVO checkSysPortRange; + private CheckItemVO checkMemInfo; + private CheckItemVO checkHyperThread; + private CheckItemVO checkMaxProcMemory; + private CheckItemVO checkBootItems; + private CheckItemVO checkKeyProAdj; + private CheckItemVO checkFilehandle; + private CheckItemVO checkDropCache; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckOtherVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckOtherVO.java new file mode 100644 index 000000000..2c1364802 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckOtherVO.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckOtherVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckOtherVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 18:36 + **/ +@Data +public class CheckOtherVO { + private CheckItemVO using; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckVO.java new file mode 100644 index 000000000..1e785b91b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckVO.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * CheckVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/CheckVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.util.*; + +/** + * @author lhf + * @date 2022/10/21 18:34 + **/ +@Slf4j +@Data +public class CheckVO { + /** + * cluster + */ + private CheckClusterVO cluster; + /** + * database + */ + private CheckDbVO db; + /** + * os + */ + private CheckOSVO os; + /** + * device + */ + private CheckDeviceVO device; + /** + * network + */ + private CheckNetworkVO network; + + public Map> summary() { + Map> summary = new HashMap<>(); + Field[] clusterField = cluster.getClass().getDeclaredFields(); + for (Field declaredField : clusterField) { + declaredField.setAccessible(true); + try { + CheckItemVO checkItemVO = (CheckItemVO) declaredField.get(cluster); + if (Objects.isNull(checkItemVO)) { + continue; + } + String status = checkItemVO.getStatus(); + List checkItemVOS = summary.get(status); + if (Objects.isNull(checkItemVOS)) { + checkItemVOS = new ArrayList<>(); + summary.put(status, checkItemVOS); + } + checkItemVOS.add(checkItemVO); + } catch (Exception e) { + log.error("get fail", e); + } + } + Field[] dbField = db.getClass().getDeclaredFields(); + for (Field declaredField : dbField) { + declaredField.setAccessible(true); + try { + CheckItemVO checkItemVO = (CheckItemVO) declaredField.get(db); + if (Objects.isNull(checkItemVO)) { + continue; + } + String status = checkItemVO.getStatus(); + List checkItemVOS = summary.get(status); + if (Objects.isNull(checkItemVOS)) { + checkItemVOS = new ArrayList<>(); + summary.put(status, checkItemVOS); + } + checkItemVOS.add(checkItemVO); + } catch (Exception e) { + log.error("get fail", e); + } + } + Field[] osField = os.getClass().getDeclaredFields(); + for (Field declaredField : osField) { + declaredField.setAccessible(true); + try { + CheckItemVO checkItemVO = (CheckItemVO) declaredField.get(os); + if (Objects.isNull(checkItemVO)) { + continue; + } + String status = checkItemVO.getStatus(); + List checkItemVOS = summary.get(status); + if (Objects.isNull(checkItemVOS)) { + checkItemVOS = new ArrayList<>(); + summary.put(status, checkItemVOS); + } + checkItemVOS.add(checkItemVO); + } catch (Exception e) { + log.error("get fail", e); + } + } + Field[] deviceField = device.getClass().getDeclaredFields(); + for (Field declaredField : deviceField) { + declaredField.setAccessible(true); + try { + CheckItemVO checkItemVO = (CheckItemVO) declaredField.get(device); + if (Objects.isNull(checkItemVO)) { + continue; + } + String status = checkItemVO.getStatus(); + List checkItemVOS = summary.get(status); + if (Objects.isNull(checkItemVOS)) { + checkItemVOS = new ArrayList<>(); + summary.put(status, checkItemVOS); + } + checkItemVOS.add(checkItemVO); + } catch (Exception e) { + log.error("get fail", e); + } + } + Field[] networkField = network.getClass().getDeclaredFields(); + for (Field declaredField : networkField) { + declaredField.setAccessible(true); + try { + CheckItemVO checkItemVO = (CheckItemVO) declaredField.get(network); + if (Objects.isNull(checkItemVO)) { + continue; + } + String status = checkItemVO.getStatus(); + List checkItemVOS = summary.get(status); + if (Objects.isNull(checkItemVOS)) { + checkItemVOS = new ArrayList<>(); + summary.put(status, checkItemVOS); + } + checkItemVOS.add(checkItemVO); + } catch (Exception e) { + log.error("get fail", e); + } + } + return summary; + } +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/ClusterEnvCheck.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/ClusterEnvCheck.java new file mode 100644 index 000000000..26c632cac --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/ClusterEnvCheck.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterNodeEnvCheck.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/ClusterNodeEnvCheck.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; +import org.opengauss.admin.common.enums.ops.ClusterEnvCheckResultEnum; + +import java.util.List; + +/** + * cluster env check + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Data +public class ClusterEnvCheck { + private String clusterId; + private ClusterEnvCheckResultEnum result; + private List envCheckDetails; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/ClusterNodeEnvCheck.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/ClusterNodeEnvCheck.java new file mode 100644 index 000000000..8c536ba62 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/ClusterNodeEnvCheck.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterNodeEnvCheck.java + * + * IDENTIFICATION + * plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/ClusterNodeEnvCheck.java + * + * ------------------------------------------------------------------------- + */ + + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; +import org.opengauss.admin.common.enums.ops.ClusterEnvCheckResultEnum; +import org.opengauss.admin.plugin.domain.model.ops.env.HostEnv; + +/** + * cluster node env check + * + * @author wangchao + * @date 2024/6/22 9:41 + **/ +@Data +public class ClusterNodeEnvCheck { + private String clusterNodeId; + private ClusterEnvCheckResultEnum result; + private HostEnv envCheckDetails; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/ClusterPortVo.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/ClusterPortVo.java new file mode 100644 index 000000000..0f133b259 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/ClusterPortVo.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024-2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * ClusterPortVo.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/ClusterPortVo.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; +import org.opengauss.admin.common.enums.ops.OpsHostPortUsedStatusEnum; + +/** + * ClusterPortVo + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@Data +public class ClusterPortVo { + private String clusterNodeId; + private String databasePort; + private String cmPort; + private OpsHostPortUsedStatusEnum databasePortStatus; + private OpsHostPortUsedStatusEnum cmPortStatus; + + public ClusterPortVo(String clusterNodeId, String databasePort, String cmPort) { + this.clusterNodeId = clusterNodeId; + this.databasePort = databasePort; + this.cmPort = cmPort; + } +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/DwrSnapshotVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/DwrSnapshotVO.java new file mode 100644 index 000000000..f76dc557c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/DwrSnapshotVO.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DwrSnapshotVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/DwrSnapshotVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * @author lhf + * @date 2022/10/24 13:55 + **/ +@Data +public class DwrSnapshotVO { + private String snapshot_id; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date start_ts; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date end_ts; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/GucSettingDto.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/GucSettingDto.java new file mode 100644 index 000000000..af18d3a1d --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/GucSettingDto.java @@ -0,0 +1,14 @@ +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +import java.util.List; + +@Data +public class GucSettingDto { + private String clusterId; + private String hostId; + private String dataPath; + private Boolean isApplyToAllNode; + private List settings; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/GucSettingVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/GucSettingVO.java new file mode 100644 index 000000000..440133d68 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/GucSettingVO.java @@ -0,0 +1,20 @@ +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +@Data +public class GucSettingVO { + private String name; + private String value; + private String unit; + private String shortDesc; + private String extraDesc; + private String varType; + private String enumVals; + private String maxVal; + private String minVal; + private String defaultVal; + private String context; + // if this setting is changed by frontend, set to true + private Boolean hasChanged; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/OpsNodeLogVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/OpsNodeLogVO.java new file mode 100644 index 000000000..527d9df1c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/OpsNodeLogVO.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * OpsNodeLogVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/OpsNodeLogVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +/** + * https://opengauss.org/zh/docs/3.0.0/docs/Administratorguide/%E6%97%A5%E5%BF%97%E5%8F%82%E8%80%83.html + * + * @author lhf + * @date 2022/10/12 17:08 + **/ +@Data +public class OpsNodeLogVO { + /** + * System Log path + */ + private String systemLogPath; + /** + * Path of operation logs. If the environment variable $GAUSSLOG does not exist or is empty, tool logs are not recorded in corresponding tool log files and are only printed on the screen + */ + private String opLogPath; + /** + * Black Box Log Path + */ + private String dumpLogPath; + /** + * Performance log path + */ + private String performanceLogPath; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/RecoverInputDto.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/RecoverInputDto.java new file mode 100644 index 000000000..856054b66 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/RecoverInputDto.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * RecoverInputDto.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/RecoverInputDto.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/11/5 10:39 + **/ +@Data +public class RecoverInputDto { + private String businessId; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/SessionVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/SessionVO.java new file mode 100644 index 000000000..0b530f064 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/SessionVO.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SessionVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/SessionVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/21 15:00 + **/ +@Data +public class SessionVO { + private String datid; + private String datname; + private String pid; + private String sessionid; + private String usesysid; + private String usename; + private String application_name; + private String client_addr; + private String client_hostname; + private String client_port; + private String backend_start; + private String xact_start; + private String query_start; + private String state_change; + private String waiting; + private String enqueue; + private String state; + private String resource_pool; + private String query_id; + private String query; + private String connection_info; + private String unique_sql_id; + private String trace_id; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/SlowSqlVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/SlowSqlVO.java new file mode 100644 index 000000000..ceaf5c463 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/SlowSqlVO.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SlowSqlVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/SlowSqlVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2022/10/12 18:11 + **/ +@Data +public class SlowSqlVO { + private String node_name; + private String db_name; + private String start_time; + private String finish_time; + private String slow_sql_threshold; + private String query; + private String query_plan; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/TaskStatusVo.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/TaskStatusVo.java new file mode 100644 index 000000000..bebe86cd2 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/TaskStatusVo.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024-2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * TaskStatusVo.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/TaskStatusVo.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.opengauss.admin.common.enums.ops.OpsClusterTaskStatusEnum; + +/** + * TaskStatusVo + * + * @author wangchao + * @since 2024/6/22 9:41 + **/ +@Data +@AllArgsConstructor +public class TaskStatusVo { + private String clusterId; + private OpsClusterTaskStatusEnum status; + private String remark; +} diff --git a/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/UpgradeOsCheckVO.java b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/UpgradeOsCheckVO.java new file mode 100644 index 000000000..df79fd9da --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/UpgradeOsCheckVO.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * SlowSqlVO.java + * + * IDENTIFICATION + * base-ops/src/main/java/org/opengauss/admin/plugin/vo/ops/SlowSqlVO.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.vo.ops; + +import lombok.Data; + +/** + * @author lhf + * @date 2023/11/4 19:57 + **/ +@Data +public class UpgradeOsCheckVO { + private String osInfo; + private Integer diskUsed; +} diff --git a/ER-Model/plugins/base-ops/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ER-Model/plugins/base-ops/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..a79d540f9 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/resources/application.yml b/ER-Model/plugins/base-ops/src/main/resources/application.yml new file mode 100644 index 000000000..a2291bfe8 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/application.yml @@ -0,0 +1,45 @@ +install: + package: + upgrade: + old-history-version: 3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1 + history-version: 6.0.0;7.0.0-RC1 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + druid: + test-while-idle: false + test-on-borrow: true + validation-query: "select 1" + validation-query-timeout: 30000 + web: + resources: + static-locations: classpath:resources +system: + defaultStoragePath: files/plugins/base_ops_default_path +# log config +logging: + level: + org.opengauss.admin.plugin: INFO + file: + name: logs/plugins_base_ops.log +mybatis-plus: + # Search for the specified package alias + typeAliasesPackage: org.opengauss.admin.plugin.domain + + mapperLocations: classpath*:mapper/**/*.xml + # Load the global configuration file + configuration: + mapUnderscoreToCamelCase: true + log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl + +installPackage: + urlPrefix: https://opengauss.obs.cn-south-1.myhuaweicloud.com + +olk: + # read log timeout, seconds + readLogTimeout: 30 + # Loop count to find start flag in log file + startLoopCount: 4 + # wait time before each loop, seconds + startLoopWaitTime: 5 + diff --git a/ER-Model/plugins/base-ops/src/main/resources/intarkdb-schema.sql b/ER-Model/plugins/base-ops/src/main/resources/intarkdb-schema.sql new file mode 100644 index 000000000..a1c778208 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/intarkdb-schema.sql @@ -0,0 +1,393 @@ +-- ---------------------------- +-- Table structure for modeling_data_flow +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "modeling_data_flow" ( + "id" int8 NOT NULL PRIMARY KEY AUTOINCREMENT, + "name" varchar(512), + "u_id" varchar(128), + "tags" varchar(512), + "remark" text, + "operator_content" text, + "type" varchar(128), + "create_by" varchar(64), + "create_time" timestamp, + "update_by" varchar(64), + "update_time" timestamp, + "db_name" varchar(255), + "cluster_node_id" varchar(128), + "schema" varchar(255), + "cluster_id" varchar(128), + "query_count" int4, + "data_base" varchar(128), + "creator" int4, + "manager" varchar(1024), + "developer" varchar(1024), + "visitor" varchar(1024), + "input_fileds" varchar(512), + "output_fields" varchar(512), + "operator_content_auto_save" text + ) +; +COMMENT ON COLUMN "modeling_data_flow"."name" IS '数据流名称'; +COMMENT ON COLUMN "modeling_data_flow"."u_id" IS '数据流全局唯一ID'; +COMMENT ON COLUMN "modeling_data_flow"."tags" IS '标签'; +COMMENT ON COLUMN "modeling_data_flow"."remark" IS '备注'; +COMMENT ON COLUMN "modeling_data_flow"."operator_content" IS '算子完整内容json'; +COMMENT ON COLUMN "modeling_data_flow"."type" IS '查询类型'; +COMMENT ON COLUMN "modeling_data_flow"."create_by" IS '创建者'; +COMMENT ON COLUMN "modeling_data_flow"."create_time" IS '创建时间'; +COMMENT ON COLUMN "modeling_data_flow"."update_by" IS '更新者'; +COMMENT ON COLUMN "modeling_data_flow"."update_time" IS '更新时间'; +COMMENT ON COLUMN "modeling_data_flow"."data_base" IS '默认访问数据库'; +COMMENT ON COLUMN "modeling_data_flow"."creator" IS '创建人'; +COMMENT ON COLUMN "modeling_data_flow"."manager" IS '管理权限'; +COMMENT ON COLUMN "modeling_data_flow"."developer" IS '开发权限'; +COMMENT ON COLUMN "modeling_data_flow"."visitor" IS '访客权限'; +COMMENT ON COLUMN "modeling_data_flow"."input_fileds" IS '输入字段'; +COMMENT ON COLUMN "modeling_data_flow"."output_fields" IS '输出字段'; +COMMENT ON COLUMN "modeling_data_flow"."operator_content_auto_save" IS '算子完整内容json定时自动保存'; +COMMENT ON TABLE "modeling_data_flow" IS '数据流表'; + +-- ---------------------------- +-- Records of modeling_data_flow +-- ---------------------------- + +-- ---------------------------- +-- Table structure for modeling_data_flow_operator +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "modeling_data_flow_operator" ( + "id" int4 NOT NULL PRIMARY KEY, + "name" varchar(255), + "package_path" varchar(255), + "type" int2, + "group_id" int2, + "sort_id" int2, + "create_by" varchar(64), + "create_time" timestamp, + "update_by" varchar(64), + "update_time" timestamp, + "remark" varchar(255) + ) +; +COMMENT ON COLUMN "modeling_data_flow_operator"."name" IS '算子名称'; +COMMENT ON COLUMN "modeling_data_flow_operator"."package_path" IS '算子包路径'; +COMMENT ON COLUMN "modeling_data_flow_operator"."type" IS '可用数据流类型'; +COMMENT ON COLUMN "modeling_data_flow_operator"."group_id" IS '所属分组'; +COMMENT ON COLUMN "modeling_data_flow_operator"."sort_id" IS '菜单排序'; +COMMENT ON COLUMN "modeling_data_flow_operator"."create_by" IS '创建者'; +COMMENT ON COLUMN "modeling_data_flow_operator"."create_time" IS '创建时间'; +COMMENT ON COLUMN "modeling_data_flow_operator"."update_by" IS '更新者'; +COMMENT ON COLUMN "modeling_data_flow_operator"."update_time" IS '更新时间'; +COMMENT ON TABLE "modeling_data_flow_operator" IS '数据库操作算子表'; + +-- ---------------------------- +-- Records of modeling_data_flow_operator +-- ---------------------------- +INSERT INTO "modeling_data_flow_operator" VALUES (202, '条件算子', 'condition', 2, 2, 2, NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_operator" VALUES (102, '条件算子', 'condition', 1, 2, 2, NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_operator" VALUES (108, '映射算子', 'mapping', 1, 2, 8, NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_operator" VALUES (101, '查询算子', 'query', 1, 1, 1, NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_operator" VALUES (103, '排序算子', 'sort', 1, 2, 3, NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_operator" VALUES (104, '限条算子', 'limit', 1, 2, 4, NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_operator" VALUES (105, '分组算子', 'group', 1, 2, 5, NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_operator" VALUES (106, '聚合算子', 'polymerization', 1, 2, 6, NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_operator" VALUES (107, '桥接算子', 'join', 1, 2, 7, NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_operator" VALUES (201, '更新算子', 'update', 2, 1, 1, NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_operator" VALUES (1001, '数据源算子', 'dataSource', 10, 0, 1, NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_operator" VALUES (301, '插入算子', 'insert', 3, 1, 1, NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_operator" VALUES (401, '删除算子', 'delete', 4, 1, 1, NULL, NULL, NULL, NULL, NULL); + +-- ---------------------------- +-- Table structure for modeling_data_flow_operator_group +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "modeling_data_flow_operator_group" ( + "id" int4 NOT NULL PRIMARY KEY, + "name" varchar(255), + "sort_id" int4, + "create_by" varchar(64), + "create_time" timestamp, + "update_by" varchar(64), + "update_time" timestamp, + "remark" varchar(255) + ) +; +COMMENT ON COLUMN "modeling_data_flow_operator_group"."name" IS '分组名称'; +COMMENT ON COLUMN "modeling_data_flow_operator_group"."sort_id" IS '菜单排序'; +COMMENT ON COLUMN "modeling_data_flow_operator_group"."create_by" IS '创建者'; +COMMENT ON COLUMN "modeling_data_flow_operator_group"."create_time" IS '创建时间'; +COMMENT ON COLUMN "modeling_data_flow_operator_group"."update_by" IS '更新者'; +COMMENT ON COLUMN "modeling_data_flow_operator_group"."update_time" IS '更新时间'; +COMMENT ON COLUMN "modeling_data_flow_operator_group"."remark" IS '描述'; +COMMENT ON TABLE "modeling_data_flow_operator_group" IS '数据库算子分类'; + +-- ---------------------------- +-- Records of modeling_data_flow_operator_group +-- ---------------------------- +INSERT INTO "modeling_data_flow_operator_group" VALUES (1, '基础算子', 1, NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_operator_group" VALUES (2, '组合算子', 2, NULL, NULL, NULL, NULL, NULL); + +-- ---------------------------- +-- Table structure for modeling_data_flow_type +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "modeling_data_flow_type" ( + "id" int4 NOT NULL PRIMARY KEY, + "name" varchar(255), + "create_by" varchar(64), + "create_time" timestamp, + "update_by" varchar(64), + "update_time" timestamp, + "remark" varchar(255) + ) +; +COMMENT ON COLUMN "modeling_data_flow_type"."name" IS '类型名称'; +COMMENT ON COLUMN "modeling_data_flow_type"."create_by" IS '创建者'; +COMMENT ON COLUMN "modeling_data_flow_type"."create_time" IS '创建时间'; +COMMENT ON COLUMN "modeling_data_flow_type"."update_by" IS '更新者'; +COMMENT ON COLUMN "modeling_data_flow_type"."update_time" IS '更新时间'; +COMMENT ON COLUMN "modeling_data_flow_type"."remark" IS '描述'; +COMMENT ON TABLE "modeling_data_flow_type" IS '数据流操作类型'; + +-- ---------------------------- +-- Records of modeling_data_flow_type +-- ---------------------------- +INSERT INTO "modeling_data_flow_type" VALUES (1, '查询类', NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_type" VALUES (2, '更新类', NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_type" VALUES (3, '删除类', NULL, NULL, NULL, NULL, NULL); +INSERT INTO "modeling_data_flow_type" VALUES (4, '插入类', NULL, NULL, NULL, NULL, NULL); + +-- ---------------------------- +-- Table structure for modeling_visualization_custom_dimensions +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "modeling_visualization_custom_dimensions" ( + "id" int8 NOT NULL PRIMARY KEY, + "name" varchar(255), + "field" varchar(128), + "categories_json" text, + "operator_id" varchar(128), + "data_flow_id" int8, + "sort_id" int4, + "create_by" varchar(64), + "create_time" timestamp, + "update_by" varchar(64), + "update_time" timestamp, + "remark" varchar(255) + ) +; +COMMENT ON COLUMN "modeling_visualization_custom_dimensions"."id" IS 'id'; +COMMENT ON COLUMN "modeling_visualization_custom_dimensions"."name" IS '维度名称'; +COMMENT ON COLUMN "modeling_visualization_custom_dimensions"."field" IS '维度对应字段'; +COMMENT ON COLUMN "modeling_visualization_custom_dimensions"."categories_json" IS '类目完整json'; +COMMENT ON COLUMN "modeling_visualization_custom_dimensions"."operator_id" IS '算子唯一id'; +COMMENT ON COLUMN "modeling_visualization_custom_dimensions"."data_flow_id" IS '所属数据流'; +COMMENT ON COLUMN "modeling_visualization_custom_dimensions"."sort_id" IS '目录排序'; +COMMENT ON COLUMN "modeling_visualization_custom_dimensions"."create_by" IS '创建者'; +COMMENT ON COLUMN "modeling_visualization_custom_dimensions"."create_time" IS '创建时间'; +COMMENT ON COLUMN "modeling_visualization_custom_dimensions"."update_by" IS '更新者'; +COMMENT ON COLUMN "modeling_visualization_custom_dimensions"."update_time" IS '更新时间'; +COMMENT ON COLUMN "modeling_visualization_custom_dimensions"."remark" IS '描述'; + +-- ---------------------------- +-- Records of modeling_visualization_custom_dimensions +-- ---------------------------- + +-- ---------------------------- +-- Table structure for modeling_visualization_geo_files +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "modeling_visualization_geo_files" ( + "id" int8 NOT NULL PRIMARY KEY, + "name" varchar(255), + "file_path" varchar(128), + "geo_json" text, + "register_name" varchar(128), + "data_flow_id" int8, + "sort_id" int4, + "create_by" varchar(64), + "create_time" timestamp, + "update_by" varchar(64), + "update_time" timestamp, + "remark" varchar(255) + ) +; +COMMENT ON COLUMN "modeling_visualization_geo_files"."id" IS 'id'; +COMMENT ON COLUMN "modeling_visualization_geo_files"."name" IS '地图中文名称'; +COMMENT ON COLUMN "modeling_visualization_geo_files"."file_path" IS 'http访问路径'; +COMMENT ON COLUMN "modeling_visualization_geo_files"."geo_json" IS 'geo完整json'; +COMMENT ON COLUMN "modeling_visualization_geo_files"."register_name" IS '地图注册名称'; +COMMENT ON COLUMN "modeling_visualization_geo_files"."data_flow_id" IS '所属数据流'; +COMMENT ON COLUMN "modeling_visualization_geo_files"."sort_id" IS '目录排序'; +COMMENT ON COLUMN "modeling_visualization_geo_files"."create_by" IS '创建者'; +COMMENT ON COLUMN "modeling_visualization_geo_files"."create_time" IS '创建时间'; +COMMENT ON COLUMN "modeling_visualization_geo_files"."update_by" IS '更新者'; +COMMENT ON COLUMN "modeling_visualization_geo_files"."update_time" IS '更新时间'; +COMMENT ON COLUMN "modeling_visualization_geo_files"."remark" IS '描述'; + +-- ---------------------------- +-- Records of modeling_visualization_geo_files +-- ---------------------------- + +-- ---------------------------- +-- Table structure for modeling_visualization_params +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "modeling_visualization_params" ( + "id" int8 NOT NULL PRIMARY KEY, + "name" varchar(255), + "type" varchar(128), + "params_json" text, + "operator_id" varchar(128), + "data_flow_id" int8, + "sort_id" int4, + "create_by" varchar(64), + "create_time" timestamp, + "update_by" varchar(64), + "update_time" timestamp, + "remark" varchar(255) + ) +; +COMMENT ON COLUMN "modeling_visualization_params"."id" IS 'id'; +COMMENT ON COLUMN "modeling_visualization_params"."name" IS '图表名称'; +COMMENT ON COLUMN "modeling_visualization_params"."type" IS '图表类型'; +COMMENT ON COLUMN "modeling_visualization_params"."params_json" IS '图表参数完整json'; +COMMENT ON COLUMN "modeling_visualization_params"."operator_id" IS '算子唯一id'; +COMMENT ON COLUMN "modeling_visualization_params"."data_flow_id" IS '所属数据流'; +COMMENT ON COLUMN "modeling_visualization_params"."sort_id" IS '目录排序'; +COMMENT ON COLUMN "modeling_visualization_params"."create_by" IS '创建者'; +COMMENT ON COLUMN "modeling_visualization_params"."create_time" IS '创建时间'; +COMMENT ON COLUMN "modeling_visualization_params"."update_by" IS '更新者'; +COMMENT ON COLUMN "modeling_visualization_params"."update_time" IS '更新时间'; +COMMENT ON COLUMN "modeling_visualization_params"."remark" IS '描述'; + +-- ---------------------------- +-- Records of modeling_visualization_params +-- ---------------------------- + +-- ---------------------------- +-- Table structure for modeling_visualization_reports +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "modeling_visualization_reports" ( + "id" int8 NOT NULL PRIMARY KEY, + "name" varchar(255), + "url" varchar(1024), + "intro" text, + "data_flow_id" int8, + "params_json" text, + "type" int8, + "resource_url" varchar(1024), + "create_by" varchar(64), + "create_time" timestamp, + "update_by" varchar(64), + "update_time" timestamp, + "remark" varchar(255) + ) +; +COMMENT ON COLUMN "modeling_visualization_reports"."create_by" IS '创建者'; +COMMENT ON COLUMN "modeling_visualization_reports"."create_time" IS '创建时间'; +COMMENT ON COLUMN "modeling_visualization_reports"."update_by" IS '更新者'; +COMMENT ON COLUMN "modeling_visualization_reports"."update_time" IS '更新时间'; +COMMENT ON COLUMN "modeling_visualization_reports"."remark" IS '描述'; + +-- ---------------------------- +-- Records of modeling_visualization_reports +-- ---------------------------- + +-- ---------------------------- +-- Table structure for modeling_visualization_snapshots +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "modeling_visualization_snapshots" ( + "id" int8 NOT NULL PRIMARY KEY, + "name" varchar(255), + "img_base64" text, + "chart_data_json" text, + "data_flow_id" int8, + "create_by" varchar(64), + "create_time" timestamp, + "update_by" varchar(64), + "update_time" timestamp, + "remark" varchar(255) + ) +; +COMMENT ON COLUMN "modeling_visualization_snapshots"."create_by" IS '创建者'; +COMMENT ON COLUMN "modeling_visualization_snapshots"."create_time" IS '创建时间'; +COMMENT ON COLUMN "modeling_visualization_snapshots"."update_by" IS '更新者'; +COMMENT ON COLUMN "modeling_visualization_snapshots"."update_time" IS '更新时间'; +COMMENT ON COLUMN "modeling_visualization_snapshots"."remark" IS '描述'; + +CREATE TABLE IF NOT EXISTS "ops_backup" ( + "backup_id" varchar(255) NOT NULL PRIMARY KEY, + "cluster_id" varchar(255), + "host_id" varchar(255), + "cluster_node_id" varchar(255), + "backup_path" text, + "remark" varchar(255), + "create_by" varchar(64), + "create_time" timestamp, + "update_by" varchar(64), + "update_time" timestamp + ); +CREATE TABLE IF NOT EXISTS "ops_wdr" ( + "wdr_id" varchar(255) NOT NULL PRIMARY KEY, + "scope" varchar(255), + "report_at" timestamp, + "report_type" varchar(255), + "report_name" varchar(255), + "report_path" varchar(255), + "cluster_id" varchar(255), + "node_id" varchar(255), + "host_id" varchar(255), + "user_id" varchar(255), + "remark" varchar(255), + "create_by" varchar(64), + "create_time" timestamp, + "update_by" varchar(64), + "update_time" timestamp, + "start_snapshot_id" varchar(255), + "end_snapshot_id" varchar(255) + ) +; + +CREATE TABLE IF NOT EXISTS "ops_package_manager" ( + "package_id" varchar(255) NOT NULL PRIMARY KEY, + "os" varchar(255), + "cpu_arch" varchar(255), + "package_version" varchar(255), + "package_version_num" varchar(255), + "package_url" varchar(1024), + "remark" varchar(255), + "create_by" varchar(64), + "create_time" timestamp, + "update_by" varchar(64), + "update_time" timestamp + ) +; + +CREATE TABLE IF NOT EXISTS "ops_olk" ( + "id" VARCHAR(64) NOT NULL PRIMARY KEY, + "name" VARCHAR(255) NOT NULL, + "olk_tar_id" VARCHAR(255) NOT NULL, + "ss_tar_id" VARCHAR(255) NOT NULL, + "dad_tar_id" VARCHAR(255) NOT NULL, + "dad_port" VARCHAR(10) NOT NULL, + "dad_install_path" TEXT NOT NULL, + "dad_install_host_id" VARCHAR(255) NOT NULL, + "dad_install_username" VARCHAR(255) NOT NULL, + "zk_tar_id" VARCHAR(255) NOT NULL, + "ss_port" VARCHAR(10) NOT NULL, + "olk_port" VARCHAR(10) NOT NULL, + "zk_port" VARCHAR(10) NOT NULL, + "ss_install_path" TEXT NOT NULL, + "ss_upload_path" TEXT NOT NULL, + "olk_install_path" TEXT NOT NULL, + "olk_upload_path" TEXT NOT NULL, + "ss_install_host_id" VARCHAR(255) NOT NULL, + "olk_install_host_id" VARCHAR(255) NOT NULL, + "ss_install_username" VARCHAR(255) NOT NULL, + "olk_install_username" VARCHAR(255) NOT NULL, + "remark" TEXT NULL DEFAULT NULL, + "rule_yaml" TEXT NOT NULL, + "table_name" TEXT NULL DEFAULT NULL, + "columns" TEXT NULL DEFAULT NULL, + "create_by" VARCHAR(64) NULL DEFAULT NULL, + "update_by" VARCHAR(64) NULL DEFAULT NULL, + "update_time" TIMESTAMP NULL DEFAULT NULL, + "create_time" TIMESTAMP NULL DEFAULT NULL, + "dad_install_password" TEXT NULL DEFAULT NULL + ); \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/resources/log4j2.xml b/ER-Model/plugins/base-ops/src/main/resources/log4j2.xml new file mode 100644 index 000000000..a8fc64da4 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/log4j2.xml @@ -0,0 +1,52 @@ + + + + $${sys:logging.file.path:-./logs} + %enc{%msg}{CRLF}%n + %style{%d{yyyy-MM-dd HH:mm:ss}}{blue} [%style{%t}{cyan}] [%style{%c{32}}{yellow}] - [%highlight{%p}] ${SAFE_MSG} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/.gitkeep b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingDataFlowMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingDataFlowMapper.xml new file mode 100644 index 000000000..f475ce237 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingDataFlowMapper.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + id,name,u_id, + data_base,tags,remark, + creator,manager,developer, + visitor,input_fileds,output_fields, + operator_content,operator_content_auto_save,type,create_by, + create_time,update_by,update_time + + diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingDataFlowOperatorGroupMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingDataFlowOperatorGroupMapper.xml new file mode 100644 index 000000000..dac5edc74 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingDataFlowOperatorGroupMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + id,name,sort_id, + create_by,create_time,update_by, + update_time + + diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingDataFlowOperatorMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingDataFlowOperatorMapper.xml new file mode 100644 index 000000000..53f9f563a --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingDataFlowOperatorMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + id,name,package_path, + type,group_id,sort_id, + create_by,create_time,update_by, + update_time + + diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingDataFlowTypeMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingDataFlowTypeMapper.xml new file mode 100644 index 000000000..3d4ea39a2 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingDataFlowTypeMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + id,name,create_by, + create_time,update_by,update_time + + diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationCustomDimensionsMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationCustomDimensionsMapper.xml new file mode 100644 index 000000000..ce6eeadd8 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationCustomDimensionsMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + id,name,field, + categories_json,operator_id,data_flow_id, + sort_id,create_by,create_time, + update_by,update_time + + diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationGeoFilesMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationGeoFilesMapper.xml new file mode 100644 index 000000000..6f9203acd --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationGeoFilesMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + id,name,file_path, + geo_json,register_name,data_flow_id, + sort_id,create_by,create_time, + update_by,update_time + + diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationParamsMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationParamsMapper.xml new file mode 100644 index 000000000..0d8f1064e --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationParamsMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + id,name,type, + params_json,operator_id,data_flow_id, + sort_id,create_by,create_time, + update_by,update_time + + diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationReportsMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationReportsMapper.xml new file mode 100644 index 000000000..bb912e585 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationReportsMapper.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + id,name,url, + intro,data_flow_id,params_json, + type,resource_url,create_by, + create_time,update_by,update_time + + diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationSnapshotsMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationSnapshotsMapper.xml new file mode 100644 index 000000000..116660e7c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/modeling/ModelingVisualizationSnapshotsMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + id,name,img_base64, + intro,data_flow_id,create_by, + create_time,update_by,update_time + + diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/.gitkeep b/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsBackupMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsBackupMapper.xml new file mode 100644 index 000000000..748b63c98 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsBackupMapper.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsBaseHostMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsBaseHostMapper.xml new file mode 100644 index 000000000..95427856c --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsBaseHostMapper.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsClusterNodeMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsClusterNodeMapper.xml new file mode 100644 index 000000000..17358b19e --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsClusterNodeMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsHostMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsHostMapper.xml new file mode 100644 index 000000000..0b02a97d1 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsHostMapper.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsImportSshMapper.xml b/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsImportSshMapper.xml new file mode 100644 index 000000000..f82346d52 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/mapper/ops/OpsImportSshMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/src/main/resources/openGauss-schema.sql b/ER-Model/plugins/base-ops/src/main/resources/openGauss-schema.sql new file mode 100644 index 000000000..520d64939 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/main/resources/openGauss-schema.sql @@ -0,0 +1,483 @@ + +CREATE OR REPLACE FUNCTION check_sequences() RETURNS integer AS ' + BEGIN +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_modeling_data_flow_id'' ) +THEN +CREATE SEQUENCE "public"."sq_modeling_data_flow_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_modeling_custom_dimension_id'' ) +THEN +CREATE SEQUENCE "public"."sq_modeling_custom_dimension_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1000 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_modeling_visualization_params_id'' ) +THEN +CREATE SEQUENCE "public"."sq_modeling_visualization_params_id" + INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1000 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_modeling_visualization_report_id'' ) +THEN +CREATE SEQUENCE "public"."sq_modeling_visualization_report_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1000 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_modeling_visualization_geo_id'' ) +THEN +CREATE SEQUENCE "public"."sq_modeling_visualization_geo_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1000 +CACHE 1; +END IF; + +IF NOT EXISTS (SELECT 1 FROM information_schema.sequences WHERE sequence_schema=''public'' AND sequence_name=''sq_modeling_visualization_snapshot_id'' ) +THEN +CREATE SEQUENCE "public"."sq_modeling_visualization_snapshot_id" +INCREMENT 1 +MINVALUE 1 +MAXVALUE 9223372036854775807 +START 1000 +CACHE 1; +END IF; + +RETURN 0; + END;' + LANGUAGE plpgsql; + +SELECT check_sequences(); + +DROP FUNCTION check_sequences; + +-- ---------------------------- +-- Table structure for modeling_data_flow +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."modeling_data_flow" ( + "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_modeling_data_flow_id'::regclass), + "name" varchar(512) COLLATE "pg_catalog"."default", + "u_id" varchar(128) COLLATE "pg_catalog"."default", + "tags" varchar(512) COLLATE "pg_catalog"."default", + "remark" text COLLATE "pg_catalog"."default", + "operator_content" text COLLATE "pg_catalog"."default", + "type" varchar(128) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "db_name" varchar(255) COLLATE "pg_catalog"."default", + "cluster_node_id" varchar(128) COLLATE "pg_catalog"."default", + "schema" varchar(255) COLLATE "pg_catalog"."default", + "cluster_id" varchar(128) COLLATE "pg_catalog"."default", + "query_count" int4, + "data_base" varchar(128) COLLATE "pg_catalog"."default", + "creator" int4[], + "manager" varchar(1024) COLLATE "pg_catalog"."default", + "developer" varchar(1024) COLLATE "pg_catalog"."default", + "visitor" varchar(1024) COLLATE "pg_catalog"."default", + "input_fileds" varchar(512) COLLATE "pg_catalog"."default", + "output_fields" varchar(512) COLLATE "pg_catalog"."default", + "operator_content_auto_save" text COLLATE "pg_catalog"."default" +) +; +COMMENT ON COLUMN "public"."modeling_data_flow"."name" IS '数据流名称'; +COMMENT ON COLUMN "public"."modeling_data_flow"."u_id" IS '数据流全局唯一ID'; +COMMENT ON COLUMN "public"."modeling_data_flow"."tags" IS '标签'; +COMMENT ON COLUMN "public"."modeling_data_flow"."remark" IS '备注'; +COMMENT ON COLUMN "public"."modeling_data_flow"."operator_content" IS '算子完整内容json'; +COMMENT ON COLUMN "public"."modeling_data_flow"."type" IS '查询类型'; +COMMENT ON COLUMN "public"."modeling_data_flow"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."modeling_data_flow"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."modeling_data_flow"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."modeling_data_flow"."update_time" IS '更新时间'; +COMMENT ON COLUMN "public"."modeling_data_flow"."data_base" IS '默认访问数据库'; +COMMENT ON COLUMN "public"."modeling_data_flow"."creator" IS '创建人'; +COMMENT ON COLUMN "public"."modeling_data_flow"."manager" IS '管理权限'; +COMMENT ON COLUMN "public"."modeling_data_flow"."developer" IS '开发权限'; +COMMENT ON COLUMN "public"."modeling_data_flow"."visitor" IS '访客权限'; +COMMENT ON COLUMN "public"."modeling_data_flow"."input_fileds" IS '输入字段'; +COMMENT ON COLUMN "public"."modeling_data_flow"."output_fields" IS '输出字段'; +COMMENT ON COLUMN "public"."modeling_data_flow"."operator_content_auto_save" IS '算子完整内容json定时自动保存'; +COMMENT ON TABLE "public"."modeling_data_flow" IS '数据流表'; + +-- ---------------------------- +-- Records of modeling_data_flow +-- ---------------------------- + +-- ---------------------------- +-- Table structure for modeling_data_flow_operator +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."modeling_data_flow_operator" ( + "id" int4 NOT NULL PRIMARY KEY, + "name" varchar(255) COLLATE "pg_catalog"."default", + "package_path" varchar(255) COLLATE "pg_catalog"."default", + "type" int2, + "group_id" int2, + "sort_id" int2, + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "remark" varchar(255) COLLATE "pg_catalog"."default" +) +; +COMMENT ON COLUMN "public"."modeling_data_flow_operator"."name" IS '算子名称'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator"."package_path" IS '算子包路径'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator"."type" IS '可用数据流类型'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator"."group_id" IS '所属分组'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator"."sort_id" IS '菜单排序'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator"."update_time" IS '更新时间'; +COMMENT ON TABLE "public"."modeling_data_flow_operator" IS '数据库操作算子表'; + +-- ---------------------------- +-- Records of modeling_data_flow_operator +-- ---------------------------- +INSERT INTO "public"."modeling_data_flow_operator" VALUES (202, '条件算子', 'condition', 2, 2, 2, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; +INSERT INTO "public"."modeling_data_flow_operator" VALUES (102, '条件算子', 'condition', 1, 2, 2, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; +INSERT INTO "public"."modeling_data_flow_operator" VALUES (108, '映射算子', 'mapping', 1, 2, 8, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; +INSERT INTO "public"."modeling_data_flow_operator" VALUES (101, '查询算子', 'query', 1, 1, 1, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; +INSERT INTO "public"."modeling_data_flow_operator" VALUES (103, '排序算子', 'sort', 1, 2, 3, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; +INSERT INTO "public"."modeling_data_flow_operator" VALUES (104, '限条算子', 'limit', 1, 2, 4, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; +INSERT INTO "public"."modeling_data_flow_operator" VALUES (105, '分组算子', 'group', 1, 2, 5, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; +INSERT INTO "public"."modeling_data_flow_operator" VALUES (106, '聚合算子', 'polymerization', 1, 2, 6, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; +INSERT INTO "public"."modeling_data_flow_operator" VALUES (107, '桥接算子', 'join', 1, 2, 7, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; +INSERT INTO "public"."modeling_data_flow_operator" VALUES (201, '更新算子', 'update', 2, 1, 1, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; +INSERT INTO "public"."modeling_data_flow_operator" VALUES (1001, '数据源算子', 'dataSource', 10, 0, 1, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; +INSERT INTO "public"."modeling_data_flow_operator" VALUES (301, '插入算子', 'insert', 3, 1, 1, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; +INSERT INTO "public"."modeling_data_flow_operator" VALUES (401, '删除算子', 'delete', 4, 1, 1, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; + +-- ---------------------------- +-- Table structure for modeling_data_flow_operator_group +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."modeling_data_flow_operator_group" ( + "id" int4 NOT NULL PRIMARY KEY, + "name" varchar(255) COLLATE "pg_catalog"."default", + "sort_id" int4, + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "remark" varchar(255) COLLATE "pg_catalog"."default" +) +; +COMMENT ON COLUMN "public"."modeling_data_flow_operator_group"."name" IS '分组名称'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator_group"."sort_id" IS '菜单排序'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator_group"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator_group"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator_group"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator_group"."update_time" IS '更新时间'; +COMMENT ON COLUMN "public"."modeling_data_flow_operator_group"."remark" IS '描述'; +COMMENT ON TABLE "public"."modeling_data_flow_operator_group" IS '数据库算子分类'; + +-- ---------------------------- +-- Records of modeling_data_flow_operator_group +-- ---------------------------- +INSERT INTO "public"."modeling_data_flow_operator_group" VALUES (1, '基础算子', 1, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; +INSERT INTO "public"."modeling_data_flow_operator_group" VALUES (2, '组合算子', 2, NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING;; + +-- ---------------------------- +-- Table structure for modeling_data_flow_type +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."modeling_data_flow_type" ( + "id" int4 NOT NULL PRIMARY KEY, + "name" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "remark" varchar(255) COLLATE "pg_catalog"."default" +) +; +COMMENT ON COLUMN "public"."modeling_data_flow_type"."name" IS '类型名称'; +COMMENT ON COLUMN "public"."modeling_data_flow_type"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."modeling_data_flow_type"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."modeling_data_flow_type"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."modeling_data_flow_type"."update_time" IS '更新时间'; +COMMENT ON COLUMN "public"."modeling_data_flow_type"."remark" IS '描述'; +COMMENT ON TABLE "public"."modeling_data_flow_type" IS '数据流操作类型'; + +-- ---------------------------- +-- Records of modeling_data_flow_type +-- ---------------------------- +INSERT INTO "public"."modeling_data_flow_type" VALUES (1, '查询类', NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."modeling_data_flow_type" VALUES (2, '更新类', NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."modeling_data_flow_type" VALUES (3, '删除类', NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING; +INSERT INTO "public"."modeling_data_flow_type" VALUES (4, '插入类', NULL, NULL, NULL, NULL, NULL) ON DUPLICATE KEY UPDATE NOTHING; + +-- ---------------------------- +-- Table structure for modeling_visualization_custom_dimensions +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."modeling_visualization_custom_dimensions" ( + "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_modeling_custom_dimension_id'::regclass), + "name" varchar(255) COLLATE "pg_catalog"."default", + "field" varchar(128) COLLATE "pg_catalog"."default", + "categories_json" text COLLATE "pg_catalog"."default", + "operator_id" varchar(128) COLLATE "pg_catalog"."default", + "data_flow_id" int8, + "sort_id" int4, + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "remark" varchar(255) COLLATE "pg_catalog"."default" +) +; +COMMENT ON COLUMN "public"."modeling_visualization_custom_dimensions"."id" IS 'id'; +COMMENT ON COLUMN "public"."modeling_visualization_custom_dimensions"."name" IS '维度名称'; +COMMENT ON COLUMN "public"."modeling_visualization_custom_dimensions"."field" IS '维度对应字段'; +COMMENT ON COLUMN "public"."modeling_visualization_custom_dimensions"."categories_json" IS '类目完整json'; +COMMENT ON COLUMN "public"."modeling_visualization_custom_dimensions"."operator_id" IS '算子唯一id'; +COMMENT ON COLUMN "public"."modeling_visualization_custom_dimensions"."data_flow_id" IS '所属数据流'; +COMMENT ON COLUMN "public"."modeling_visualization_custom_dimensions"."sort_id" IS '目录排序'; +COMMENT ON COLUMN "public"."modeling_visualization_custom_dimensions"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."modeling_visualization_custom_dimensions"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."modeling_visualization_custom_dimensions"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."modeling_visualization_custom_dimensions"."update_time" IS '更新时间'; +COMMENT ON COLUMN "public"."modeling_visualization_custom_dimensions"."remark" IS '描述'; + +-- ---------------------------- +-- Records of modeling_visualization_custom_dimensions +-- ---------------------------- + +-- ---------------------------- +-- Table structure for modeling_visualization_geo_files +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."modeling_visualization_geo_files" ( + "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_modeling_visualization_geo_id'::regclass), + "name" varchar(255) COLLATE "pg_catalog"."default", + "file_path" varchar(128) COLLATE "pg_catalog"."default", + "geo_json" text COLLATE "pg_catalog"."default", + "register_name" varchar(128) COLLATE "pg_catalog"."default", + "data_flow_id" int8, + "sort_id" int4, + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "remark" varchar(255) COLLATE "pg_catalog"."default" +) +; +COMMENT ON COLUMN "public"."modeling_visualization_geo_files"."id" IS 'id'; +COMMENT ON COLUMN "public"."modeling_visualization_geo_files"."name" IS '地图中文名称'; +COMMENT ON COLUMN "public"."modeling_visualization_geo_files"."file_path" IS 'http访问路径'; +COMMENT ON COLUMN "public"."modeling_visualization_geo_files"."geo_json" IS 'geo完整json'; +COMMENT ON COLUMN "public"."modeling_visualization_geo_files"."register_name" IS '地图注册名称'; +COMMENT ON COLUMN "public"."modeling_visualization_geo_files"."data_flow_id" IS '所属数据流'; +COMMENT ON COLUMN "public"."modeling_visualization_geo_files"."sort_id" IS '目录排序'; +COMMENT ON COLUMN "public"."modeling_visualization_geo_files"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."modeling_visualization_geo_files"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."modeling_visualization_geo_files"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."modeling_visualization_geo_files"."update_time" IS '更新时间'; +COMMENT ON COLUMN "public"."modeling_visualization_geo_files"."remark" IS '描述'; + +-- ---------------------------- +-- Records of modeling_visualization_geo_files +-- ---------------------------- + +-- ---------------------------- +-- Table structure for modeling_visualization_params +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."modeling_visualization_params" ( + "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_modeling_visualization_params_id'::regclass), + "name" varchar(255) COLLATE "pg_catalog"."default", + "type" varchar(128) COLLATE "pg_catalog"."default", + "params_json" text COLLATE "pg_catalog"."default", + "operator_id" varchar(128) COLLATE "pg_catalog"."default", + "data_flow_id" int8, + "sort_id" int4, + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "remark" varchar(255) COLLATE "pg_catalog"."default" +) +; +COMMENT ON COLUMN "public"."modeling_visualization_params"."id" IS 'id'; +COMMENT ON COLUMN "public"."modeling_visualization_params"."name" IS '图表名称'; +COMMENT ON COLUMN "public"."modeling_visualization_params"."type" IS '图表类型'; +COMMENT ON COLUMN "public"."modeling_visualization_params"."params_json" IS '图表参数完整json'; +COMMENT ON COLUMN "public"."modeling_visualization_params"."operator_id" IS '算子唯一id'; +COMMENT ON COLUMN "public"."modeling_visualization_params"."data_flow_id" IS '所属数据流'; +COMMENT ON COLUMN "public"."modeling_visualization_params"."sort_id" IS '目录排序'; +COMMENT ON COLUMN "public"."modeling_visualization_params"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."modeling_visualization_params"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."modeling_visualization_params"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."modeling_visualization_params"."update_time" IS '更新时间'; +COMMENT ON COLUMN "public"."modeling_visualization_params"."remark" IS '描述'; + +-- ---------------------------- +-- Records of modeling_visualization_params +-- ---------------------------- + +-- ---------------------------- +-- Table structure for modeling_visualization_reports +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."modeling_visualization_reports" ( + "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_modeling_visualization_report_id'::regclass), + "name" varchar(255) COLLATE "pg_catalog"."default", + "url" varchar(1024) COLLATE "pg_catalog"."default", + "intro" text COLLATE "pg_catalog"."default", + "data_flow_id" int8, + "params_json" text COLLATE "pg_catalog"."default", + "type" int8, + "resource_url" varchar(1024) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "remark" varchar(255) COLLATE "pg_catalog"."default" +) +; +COMMENT ON COLUMN "public"."modeling_visualization_reports"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."modeling_visualization_reports"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."modeling_visualization_reports"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."modeling_visualization_reports"."update_time" IS '更新时间'; +COMMENT ON COLUMN "public"."modeling_visualization_reports"."remark" IS '描述'; + +-- ---------------------------- +-- Records of modeling_visualization_reports +-- ---------------------------- + +-- ---------------------------- +-- Table structure for modeling_visualization_snapshots +-- ---------------------------- +CREATE TABLE IF NOT EXISTS "public"."modeling_visualization_snapshots" ( + "id" int8 NOT NULL PRIMARY KEY DEFAULT nextval('sq_modeling_visualization_snapshot_id'::regclass), + "name" varchar(255) COLLATE "pg_catalog"."default", + "img_base64" text COLLATE "pg_catalog"."default", + "chart_data_json" text COLLATE "pg_catalog"."default", + "data_flow_id" int8, + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "remark" varchar(255) COLLATE "pg_catalog"."default" +) +; +COMMENT ON COLUMN "public"."modeling_visualization_snapshots"."create_by" IS '创建者'; +COMMENT ON COLUMN "public"."modeling_visualization_snapshots"."create_time" IS '创建时间'; +COMMENT ON COLUMN "public"."modeling_visualization_snapshots"."update_by" IS '更新者'; +COMMENT ON COLUMN "public"."modeling_visualization_snapshots"."update_time" IS '更新时间'; +COMMENT ON COLUMN "public"."modeling_visualization_snapshots"."remark" IS '描述'; + +CREATE TABLE IF NOT EXISTS "public"."ops_backup" ( + "backup_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "cluster_id" varchar(255) COLLATE "pg_catalog"."default", + "host_id" varchar(255) COLLATE "pg_catalog"."default", + "cluster_node_id" varchar(255) COLLATE "pg_catalog"."default", + "backup_path" text COLLATE "pg_catalog"."default", + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ); +CREATE TABLE IF NOT EXISTS "public"."ops_wdr" ( + "wdr_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "scope" varchar(255) COLLATE "pg_catalog"."default", + "report_at" timestamp(6), + "report_type" varchar(255) COLLATE "pg_catalog"."default", + "report_name" varchar(255) COLLATE "pg_catalog"."default", + "report_path" varchar(255) COLLATE "pg_catalog"."default", + "cluster_id" varchar(255) COLLATE "pg_catalog"."default", + "node_id" varchar(255) COLLATE "pg_catalog"."default", + "host_id" varchar(255) COLLATE "pg_catalog"."default", + "user_id" varchar(255) COLLATE "pg_catalog"."default", + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6), + "start_snapshot_id" varchar(255) COLLATE "pg_catalog"."default", + "end_snapshot_id" varchar(255) COLLATE "pg_catalog"."default" + ) +; + +CREATE TABLE IF NOT EXISTS "public"."ops_package_manager" ( + "package_id" varchar(255) COLLATE "pg_catalog"."default" NOT NULL PRIMARY KEY, + "os" varchar(255) COLLATE "pg_catalog"."default", + "cpu_arch" varchar(255) COLLATE "pg_catalog"."default", + "package_version" varchar(255) COLLATE "pg_catalog"."default", + "package_version_num" varchar(255) COLLATE "pg_catalog"."default", + "package_url" varchar(1024) COLLATE "pg_catalog"."default", + "remark" varchar(255) COLLATE "pg_catalog"."default", + "create_by" varchar(64) COLLATE "pg_catalog"."default", + "create_time" timestamp(6), + "update_by" varchar(64) COLLATE "pg_catalog"."default", + "update_time" timestamp(6) + ) +; + +CREATE TABLE IF NOT EXISTS "public"."ops_olk" ( + "id" VARCHAR(64) NOT NULL PRIMARY KEY, + "name" VARCHAR(255) NOT NULL, + "olk_tar_id" VARCHAR(255) NOT NULL, + "ss_tar_id" VARCHAR(255) NOT NULL, + "dad_tar_id" VARCHAR(255) NOT NULL, + "dad_port" VARCHAR(10) NOT NULL, + "dad_install_path" TEXT NOT NULL, + "dad_install_host_id" VARCHAR(255) NOT NULL, + "dad_install_username" VARCHAR(255) NOT NULL, + "zk_tar_id" VARCHAR(255) NOT NULL, + "ss_port" VARCHAR(10) NOT NULL, + "olk_port" VARCHAR(10) NOT NULL, + "zk_port" VARCHAR(10) NOT NULL, + "ss_install_path" TEXT NOT NULL, + "ss_upload_path" TEXT NOT NULL, + "olk_install_path" TEXT NOT NULL, + "olk_upload_path" TEXT NOT NULL, + "ss_install_host_id" VARCHAR(255) NOT NULL, + "olk_install_host_id" VARCHAR(255) NOT NULL, + "ss_install_username" VARCHAR(255) NOT NULL, + "olk_install_username" VARCHAR(255) NOT NULL, + "remark" TEXT NULL DEFAULT NULL, + "rule_yaml" TEXT NOT NULL, + "table_name" TEXT NULL DEFAULT NULL, + "columns" TEXT NULL DEFAULT NULL, + "create_by" VARCHAR(64) NULL DEFAULT NULL, + "update_by" VARCHAR(64) NULL DEFAULT NULL, + "update_time" TIMESTAMP NULL DEFAULT NULL, + "create_time" TIMESTAMP NULL DEFAULT NULL, + "dad_install_password" TEXT NULL DEFAULT NULL +); + + + +CREATE OR REPLACE FUNCTION add_field_db_name() RETURNS integer AS ' + BEGIN +IF +( SELECT COUNT ( * ) AS ct1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ''modeling_data_flow'' AND COLUMN_NAME = ''db_name'' ) = 0 +THEN +ALTER TABLE modeling_data_flow ADD COLUMN db_name VARCHAR(255); +END IF; +RETURN 0; + END;' + LANGUAGE plpgsql; + +SELECT add_field_db_name(); + +DROP FUNCTION add_field_db_name; + +DELETE FROM sys_menu WHERE menu_name IN ('磁阵管理', '容灾集群管理', '容灾集群安装'); \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/src/test/java/BackupTest.java b/ER-Model/plugins/base-ops/src/test/java/BackupTest.java new file mode 100644 index 000000000..67b548399 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/test/java/BackupTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; + +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsBackupEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.JschResult; +import org.opengauss.admin.plugin.domain.model.ops.WsSession; +import org.opengauss.admin.plugin.domain.model.ops.cache.WsConnectorManager; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.service.ops.IOpsBackupService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterService; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.plugin.vo.ops.BackupInputDto; +import org.opengauss.admin.plugin.vo.ops.RecoverInputDto; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import jakarta.servlet.http.HttpServletRequest; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; + +/** + * BackupTest + * + * @author: wangchao + * @Date: 2025/9/11 21:21 + * @since 7.0.0-RC2 + **/ +@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +public class BackupTest { + private static final String MASTER_NODE_HOST_ID = "testHostId"; + private static final String MASTER_NODE_INSTALL_USER_ID = "testUserId"; + + @Mock + private IOpsClusterService opsClusterService; + @Mock + private IOpsClusterNodeService clusterNodeService; + @Mock + private HostFacade hostFacade; + @Mock + private HostUserFacade hostUserFacade; + @Mock + private WsConnectorManager wsConnectorManager; + @Mock + private HttpServletRequest request; + @Mock + private JschUtil jschUtil; + @InjectMocks + @Spy + private IOpsBackupService backupService; + @Mock + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + + @Before + public void setup() throws IOException, InterruptedException, JSchException { + List clusterEntities = getClusterList(); + when(opsClusterService.getById("1")).thenReturn(getCluster()); + when(clusterNodeService.listClusterNodeByClusterId("1")).thenReturn(getClusterNodeList()); + when(clusterNodeService.getById("1")).thenReturn(getClusterNode()); + when(opsClusterService.list()).thenReturn(clusterEntities); + when(hostFacade.getById(MASTER_NODE_HOST_ID)).thenReturn(getHost()); + when(hostUserFacade.getById(MASTER_NODE_INSTALL_USER_ID)).thenReturn(getHostUser()); + when(hostUserFacade.getRootUserByHostId(MASTER_NODE_HOST_ID)).thenReturn(getHostUser()); + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); + when(wsConnectorManager.getSession(any())).thenReturn(Optional.of(new WsSession())); + when(threadPoolTaskExecutor.submit(any(Runnable.class))).thenReturn(new CompletableFuture<>()); + doReturn(getBackupEntity()).when(backupService).getById(any()); + doReturn(true).when(backupService).removeById(any()); + Session session = null; + JSch jSch = new JSch(); + session = jSch.getSession(""); + doReturn(Optional.of(session)).when(jschUtil).getSession(any(), any(), any(), any()); + doReturn(getCmdResult()).when(jschUtil).executeCommand(any(), any()); + } + + @Test + public void testBackup() { + BackupInputDto inputDto = new BackupInputDto(); + inputDto.setBusinessId(null); + List voList = opsClusterService.list(); + inputDto.setClusterId(voList.get(0).getClusterId()); + inputDto.setBackupPath("/home/"); + inputDto.setBusinessId("testBusinessId"); + inputDto.setRemark("testRemark"); + backupService.backup(inputDto); + } + + @Test + public void testRecover() { + RecoverInputDto recoverInputDto = new RecoverInputDto(); + recoverInputDto.setBusinessId("testBusinessId"); + backupService.recover("1", recoverInputDto); + } + + @Test + public void testDel() { + backupService.del("1"); + } + + private List getClusterList() { + List voList = new ArrayList<>(); + voList.add(getCluster()); + return voList; + } + + private OpsClusterEntity getCluster() { + OpsClusterEntity opsCluster = new OpsClusterEntity(); + opsCluster.setClusterId("1"); + return opsCluster; + } + + private List getClusterNodeList() { + List nodeList = new ArrayList<>(); + OpsClusterNodeEntity masterNode = new OpsClusterNodeEntity(); + masterNode.setClusterRole(ClusterRoleEnum.MASTER); + masterNode.setHostId(MASTER_NODE_HOST_ID); + masterNode.setInstallUserId(MASTER_NODE_INSTALL_USER_ID); + nodeList.add(masterNode); + OpsClusterNodeEntity slaveNode = new OpsClusterNodeEntity(); + slaveNode.setClusterRole(ClusterRoleEnum.SLAVE); + nodeList.add(slaveNode); + return nodeList; + } + + private OpsClusterNodeEntity getClusterNode() { + OpsClusterNodeEntity masterNode = new OpsClusterNodeEntity(); + masterNode.setClusterRole(ClusterRoleEnum.MASTER); + masterNode.setHostId(MASTER_NODE_HOST_ID); + masterNode.setInstallUserId(MASTER_NODE_INSTALL_USER_ID); + return masterNode; + } + + private OpsHostEntity getHost() { + return new OpsHostEntity(); + } + + private OpsHostUserEntity getHostUser() { + return new OpsHostUserEntity(); + } + + private OpsBackupEntity getBackupEntity() { + OpsBackupEntity backupEntity = new OpsBackupEntity(); + backupEntity.setBackupId("1"); + backupEntity.setClusterId("1"); + backupEntity.setHostId(MASTER_NODE_HOST_ID); + backupEntity.setClusterNodeId("1"); + return backupEntity; + } + + private JschResult getCmdResult() { + JschResult result = new JschResult(); + result.setExitCode(0); + return result; + } +} diff --git a/ER-Model/plugins/base-ops/src/test/java/DirTest.java b/ER-Model/plugins/base-ops/src/test/java/DirTest.java new file mode 100644 index 000000000..d233615d8 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/test/java/DirTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +import lombok.extern.slf4j.Slf4j; + +import org.junit.jupiter.api.Test; +import org.opengauss.admin.plugin.utils.PathUtils; + +/** + * DirTest + * + * @author: wangchao + * @Date: 2025/9/11 21:21 + * @since 7.0.0-RC2 + **/ +@Slf4j +public class DirTest { + @Test + void checkDir() { + String dir = "/data2/testog1/opengauss"; + String[] split = dir.split("/"); + int length = split.length; + if (length > 0) { + String[] subString = new String[length - 1]; + System.arraycopy(split, 0, subString, 0, length - 1); + String join = String.join("/", subString); + log.info(join); + } + } + + private int chmodStringToInt(String perm) { + int permInt = 0; + for (int i = 0; i < perm.length(); i++) { + char c = perm.charAt(i); + switch (c) { + case 'd': + permInt += 1000; + break; + case 'r': + permInt += 100; + break; + case 'w': + permInt += 10; + break; + case 'x': + permInt += 1; + break; + case '-': + break; + default: + throw new IllegalArgumentException("Invalid permission character: " + c); + } + } + return permInt; + } + + @Test + void testChmodStringToInt() { + String perm = "drwxr-xr-x"; + log.info("Permission integer: " + chmodStringToInt(perm)); + log.info("Permission integer: " + convertPermission(perm)); + } + + private static int convertPermission(String permissionString) { + if (permissionString == null || permissionString.length() != 10) { + throw new IllegalArgumentException("Invalid permission string"); + } + int owner = 0; + if (permissionString.charAt(1) == 'r') { + owner += 4; + } + if (permissionString.charAt(2) == 'w') { + owner += 2; + } + if (permissionString.charAt(3) == 'x') { + owner += 1; + } + int group = 0; + if (permissionString.charAt(4) == 'r') { + group += 4; + } + if (permissionString.charAt(5) == 'w') { + group += 2; + } + if (permissionString.charAt(6) == 'x') { + group += 1; + } + int others = 0; + if (permissionString.charAt(7) == 'r') { + others += 4; + } + if (permissionString.charAt(8) == 'w') { + others += 2; + } + if (permissionString.charAt(9) == 'x') { + others += 1; + } + return Integer.valueOf(owner + String.valueOf(group) + others); + } + + @Test + void checkDirConflict1() { + String path1 = "/a/b/c"; + String path2 = "/a/b/c/d/e"; + String path3 = "/a/b"; + PathUtils pathUtils = new PathUtils(); + log.info("Conflict: " + pathUtils.checkDirNoConflict(path1, path2, path3)); + } + + @Test + void checkDirNoConflict() { + String path1 = "/a/b/c"; + String path2 = "/a/b/d/e"; + String path3 = "/a/b/e"; + PathUtils pathUtils = new PathUtils(); + log.info("Conflict: " + pathUtils.checkDirNoConflict(path1, path2, path3)); + } +} diff --git a/ER-Model/plugins/base-ops/src/test/java/HostTest.java b/ER-Model/plugins/base-ops/src/test/java/HostTest.java new file mode 100644 index 000000000..f64cac4fb --- /dev/null +++ b/ER-Model/plugins/base-ops/src/test/java/HostTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +import com.jcraft.jsch.Session; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.plugin.domain.model.ops.JschResult; +import org.opengauss.admin.plugin.service.ops.IHostService; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; + +/** + * HostTest + * + * @author: wangchao + * @Date: 2025/9/11 21:21 + * @since 7.0.0-RC2 + **/ +@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +public class HostTest { + private static final String MASTER_NODE_HOST_ID = "testHostId"; + private static final String ROOT_PASSWORD = "1qaz2wsx#EDC"; + + @Mock + private HostFacade hostFacade; + @Mock + private HostUserFacade hostUserFacade; + @Mock + private JschUtil jschUtil; + @InjectMocks + @Spy + private IHostService hostService; + + @Before + public void setup() throws IOException, InterruptedException { + when(hostFacade.getById(MASTER_NODE_HOST_ID)).thenReturn(getHost()); + when(hostUserFacade.getRootUserByHostId(MASTER_NODE_HOST_ID)).thenReturn(getHostUser()); + Session session = null; + doReturn(Optional.of(session)).when(jschUtil).getSession(any(), any(), any(), any()); + doReturn(getCmdResult()).when(jschUtil).executeCommand(any(), any()); + } + + @Test + public void testPathEmpty() { + boolean isPathEmptyResult = hostService.pathEmpty(MASTER_NODE_HOST_ID, "/test/path", ROOT_PASSWORD); + Assert.assertTrue(isPathEmptyResult); + } + + @Test + public void testPortUsed() { + boolean isPortUsedResult = hostService.portUsed(MASTER_NODE_HOST_ID, 5432, ROOT_PASSWORD); + Assert.assertTrue(isPortUsedResult); + } + + @Test + public void testFileExist() { + boolean isFileExistResult = hostService.fileExist(MASTER_NODE_HOST_ID, "/ops/files/1.txt", ROOT_PASSWORD); + Assert.assertTrue(isFileExistResult); + } + + @Test + public void testMultiPathQuery() { + List result = hostService.multiPathQuery(MASTER_NODE_HOST_ID, ROOT_PASSWORD); + Assert.assertNotNull(result); + } + + private OpsHostEntity getHost() { + return new OpsHostEntity(); + } + + private OpsHostUserEntity getHostUser() { + return new OpsHostUserEntity(); + } + + private JschResult getCmdResult() { + JschResult result = new JschResult(); + result.setExitCode(0); + result.setResult("0"); + return result; + } +} diff --git a/ER-Model/plugins/base-ops/src/test/java/HttpUtilTest.java b/ER-Model/plugins/base-ops/src/test/java/HttpUtilTest.java new file mode 100644 index 000000000..3ec356fab --- /dev/null +++ b/ER-Model/plugins/base-ops/src/test/java/HttpUtilTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +import org.junit.jupiter.api.Test; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.utils.http.HttpUtils; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import lombok.extern.slf4j.Slf4j; + +/** + * HttpUtilTest + * + * @author: wangchao + * @Date: 2025/9/11 21:21 + * @since 7.0.0-RC2 + **/ +@Slf4j +public class HttpUtilTest { + @Test + void checkBaiduUrl() { + AjaxResult baiduResult = HttpUtils.checkUrl("https://www.baidu.com"); + log.info("{}", baiduResult); + assertTrue(baiduResult.isOk()); + } + + @Test + void checkOpenGaussDownUrl() { + AjaxResult openGaussDownUrlResult = HttpUtils.checkUrl( + "https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.2/x86/openGauss-5.0.2-CentOS-64bit-all.tar.gz"); + log.info("{}", openGaussDownUrlResult); + assertTrue(openGaussDownUrlResult.isOk()); + } + + @Test + void checkOpenGaussDownErrorUrl() { + AjaxResult openGaussDownUrlResult2 = HttpUtils.checkUrl( + "https://opengauss.obs.cn-south-1.myhuaweicloud.com/7.0.5/x86/openGauss-5.0.2-CentOS-64bit-all.tar.gz"); + log.info("{}", openGaussDownUrlResult2); + assertFalse(openGaussDownUrlResult2.isOk()); + } + + @Test + void checkOpenGaussHome() { + AjaxResult openGaussDownUrlResult2 = HttpUtils.checkUrl("https://opengauss.org/zh/"); + log.info("{}", openGaussDownUrlResult2); + assertTrue(openGaussDownUrlResult2.isOk()); + } +} diff --git a/ER-Model/plugins/base-ops/src/test/java/InstallPackageTest.java b/ER-Model/plugins/base-ops/src/test/java/InstallPackageTest.java new file mode 100644 index 000000000..d7718998b --- /dev/null +++ b/ER-Model/plugins/base-ops/src/test/java/InstallPackageTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opengauss.admin.common.core.domain.UploadInfo; +import org.opengauss.admin.common.core.domain.entity.SysSettingEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsPackageManagerEntity; +import org.opengauss.admin.plugin.domain.model.ops.OpsPackageVO; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.enums.ops.PackageTypeEnum; +import org.opengauss.admin.plugin.service.ops.IOpsPackageManagerService; +import org.opengauss.admin.plugin.service.ops.impl.OpsPackageManagerService; +import org.opengauss.admin.system.plugin.facade.SysSettingFacade; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; + +/** + * InstallPackageTest + * + * @author: wangchao + * @Date: 2025/9/11 21:21 + * @since 7.0.0-RC2 + **/ +@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +public class InstallPackageTest { + private static final int USER_ID = 1; + + @InjectMocks + @Spy + private IOpsPackageManagerService opsPackageManagerService = new OpsPackageManagerService(); + @Mock + private SysSettingFacade sysSettingFacade; + + @Before + public void setup() throws IOException, InterruptedException { + when(sysSettingFacade.getSysSetting(USER_ID)).thenReturn(getSysSetting()); + doReturn(true).when(opsPackageManagerService).saveOrUpdate(any()); + doReturn(true).when(opsPackageManagerService).removeById(anyString()); + doReturn(true).when(opsPackageManagerService).update(any()); + doReturn(getPkg()).when(opsPackageManagerService).getById(any()); + } + + @Test + public void testSavePackage() throws IOException { + OpsPackageManagerEntity pkgEntity = new OpsPackageManagerEntity(); + File file = new File("upload/openGauss-5.1.0-CentOS-64bit-all.tar.gz"); + // 创建一个FileInputStream对象,用于读取文件内容 + FileInputStream fis = new FileInputStream(file); + MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), "text/plain", fis); + pkgEntity.setFile(multipartFile); + pkgEntity.setName("TestPkgName"); + opsPackageManagerService.savePackage(pkgEntity, USER_ID); + } + + @Test + public void testDelPackage() throws IOException { + opsPackageManagerService.delPackage("1"); + } + + @Test + public void testGetCpuArchByPackagePath() throws IOException { + String result = opsPackageManagerService.getCpuArchByPackagePath( + "/upload/openGauss-Lite-5.1.1-CentOS-x86_64.tar.gz", OpenGaussVersionEnum.LITE); + Assert.assertEquals(result, "x86_64"); + } + + @Test + public void testUpdatePackage() throws IOException { + opsPackageManagerService.updatePackage(getPkg(), 1); + } + + @Test + public void testAnalysisPkg() { + OpsPackageVO result = opsPackageManagerService.analysisPkg("openGauss-Lite-5.0.1-CentOS-x86_64.tar.gz", + PackageTypeEnum.OPENGAUSS.getPackageType()); + Assert.assertEquals(result.getOs(), "centos"); + Assert.assertEquals(result.getCpuArch(), "x86_64"); + Assert.assertEquals(result.getPackageVersion(), "LITE"); + Assert.assertEquals(result.getPackageVersionNum(), "5.0.1"); + } + + @Test + public void testDeletePkgTar() { + Assert.assertTrue(opsPackageManagerService.deletePkgTar("upload/openGauss-5.0.1-CentOS-64bit.tar.bz2", "1")); + } + + @Test + public void testGetSysUploadPath() { + String result = opsPackageManagerService.getSysUploadPath(1); + Assert.assertEquals(result, "upload"); + } + + private SysSettingEntity getSysSetting() { + SysSettingEntity sysSetting = new SysSettingEntity(); + sysSetting.setId(1); + sysSetting.setUserId(1); + sysSetting.setUploadPath("upload"); + return sysSetting; + } + + private OpsPackageManagerEntity getPkg() { + OpsPackageManagerEntity entity = new OpsPackageManagerEntity(); + entity.setPackageId("1"); + UploadInfo uploadInfo = new UploadInfo(); + uploadInfo.setName("TestPkgName"); + uploadInfo.setRealPath("upload/1.txt"); + entity.setPackagePath(uploadInfo); + return entity; + } +} diff --git a/ER-Model/plugins/base-ops/src/test/java/OpenLooKengTest.java b/ER-Model/plugins/base-ops/src/test/java/OpenLooKengTest.java new file mode 100644 index 000000000..fda5ed1c4 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/test/java/OpenLooKengTest.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +import cn.hutool.core.collection.ListUtil; +import jakarta.websocket.Session; + +import com.alibaba.fastjson.JSON; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.utils.http.HttpUtils; +import org.opengauss.admin.plugin.domain.entity.ops.OpsOlkEntity; +import org.opengauss.admin.plugin.domain.model.ops.WsSession; +import org.opengauss.admin.plugin.domain.model.ops.cache.WsConnectorManager; +import org.opengauss.admin.plugin.domain.model.ops.olk.InstallOlkBody; +import org.opengauss.admin.plugin.domain.model.ops.olk.InstallOlkContext; +import org.opengauss.admin.plugin.domain.model.ops.olk.OlkConfig; +import org.opengauss.admin.plugin.domain.model.ops.olk.ShardingDatasourceConfig; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.DadReqPath; +import org.opengauss.admin.plugin.domain.model.ops.olk.dadReq.DadResult; +import org.opengauss.admin.plugin.service.ops.IOpsOlkService; +import org.opengauss.admin.plugin.service.ops.impl.OpsOlkServiceImpl; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; + +/** + * OpenLooKengTest + * + * @author: wangchao + * @Date: 2025/9/11 21:21 + * @since 7.0.0-RC2 + **/ +@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +public class OpenLooKengTest { + private static final String BUSINESS_ID = "testBusinessId"; + + @InjectMocks + @Spy + private IOpsOlkService olkService = new OpsOlkServiceImpl(); + @Mock + private WsConnectorManager wsConnectorManager; + @Mock + private ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); + @Mock + private HostFacade hostFacade; + + @Before + public void setup() throws IOException, InterruptedException { + when(wsConnectorManager.getSession(any())).thenReturn( + Optional.of(new WsSession(getMockSession(), "testSessionId"))); + when(threadPoolTaskExecutor.submit(any(Runnable.class))).thenReturn(new CompletableFuture<>()); + doReturn(true).when(olkService).removeById(anyString()); + doReturn(getOlkEntity()).when(olkService).getById(anyString()); + when(hostFacade.getById(any())).thenReturn(getHost()); + } + + @Test + public void testInstall() { + InstallOlkBody installOlkBody = new InstallOlkBody(); + installOlkBody.setBusinessId(BUSINESS_ID); + InstallOlkContext olkContext = new InstallOlkContext(); + OlkConfig olkConfig = new OlkConfig(); + olkConfig.setRuleYaml("testRuleYaml"); + olkContext.setOlkConfig(olkConfig); + installOlkBody.setInstallContext(olkContext); + olkService.install(installOlkBody); + } + + @Test + public void testDelete() { + Assert.assertTrue(olkService.removeById("1")); + } + + @Test + public void testDestroy() { + MockedStatic httpUtils = Mockito.mockStatic(HttpUtils.class); + httpUtils.when(() -> HttpUtils.sendPost(any(), any())).thenReturn(getDadResult()); + olkService.destroy("1", "bid"); + httpUtils.close(); + } + + @Test + public void testGenerateRuleYaml() { + String yaml = olkService.generateRuleYaml(getYamlConfig()); + Assert.assertNotNull(yaml); + } + + @Test + public void testStart() { + MockedStatic httpUtils = Mockito.mockStatic(HttpUtils.class); + httpUtils.when(() -> HttpUtils.sendPost(any(), any())).thenReturn(getDadResult()); + olkService.start("1", "testBusinessId"); + httpUtils.close(); + } + + @Test + public void testStop() { + MockedStatic httpUtils = Mockito.mockStatic(HttpUtils.class); + httpUtils.when(() -> HttpUtils.sendPost(any(), any())).thenReturn(getDadResult()); + olkService.stop("1", "testBusinessId"); + httpUtils.close(); + } + + private OpsOlkEntity getOlkEntity() { + OpsOlkEntity olkEntity = new OpsOlkEntity(); + olkEntity.setId("1"); + return olkEntity; + } + + private OpsHostEntity getHost() { + OpsHostEntity hostEntity = new OpsHostEntity(); + hostEntity.setPort(22); + hostEntity.setPublicIp("127.0.0.1"); + return hostEntity; + } + + private HttpUtils.PostResponse getDadResult() { + HttpUtils.PostResponse response = new HttpUtils.PostResponse(); + DadResult dadResult = DadResult.success("test return msg"); + response.setBody(JSON.toJSONString(dadResult)); + Map> headers = new HashMap<>(); + headers.put(DadReqPath.REQ_ID_KEY, ListUtil.toList("testReqId")); + response.setHeaders(headers); + return response; + } + + private OlkConfig getYamlConfig() { + List dsConfigList = new ArrayList<>(); + ShardingDatasourceConfig dsConfig1 = new ShardingDatasourceConfig(); + dsConfig1.setPort("5432"); + dsConfig1.setUsername("gaussdb"); + dsConfig1.setDbName("template1"); + dsConfig1.setPassword("1qaz2wsx#EDC"); + dsConfig1.setHost("175.24.226.99"); + dsConfigList.add(dsConfig1); + ShardingDatasourceConfig dsConfig2 = new ShardingDatasourceConfig(); + dsConfig2.setPort("5432"); + dsConfig2.setUsername("gaussdb"); + dsConfig2.setDbName("template1"); + dsConfig2.setPassword("1qaz2wsx#EDC"); + dsConfig2.setHost("175.24.226.99"); + dsConfigList.add(dsConfig2); + OlkConfig olkConfig = new OlkConfig(); + olkConfig.setDsConfig(dsConfigList); + olkConfig.setColumns("key1,key2"); + olkConfig.setTableName("table1,table2"); + return olkConfig; + } + + private Session getMockSession() { + return Mockito.mock(Session.class); + } +} diff --git a/ER-Model/plugins/base-ops/src/test/java/OpsClusterTest.java b/ER-Model/plugins/base-ops/src/test/java/OpsClusterTest.java new file mode 100644 index 000000000..6760dab71 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/test/java/OpsClusterTest.java @@ -0,0 +1,492 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +import cn.hutool.core.collection.ListUtil; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.DownloadBody; +import org.opengauss.admin.plugin.domain.model.ops.EnterpriseInstallConfig; +import org.opengauss.admin.plugin.domain.model.ops.HostFile; +import org.opengauss.admin.plugin.domain.model.ops.InstallBody; +import org.opengauss.admin.plugin.domain.model.ops.InstallContext; +import org.opengauss.admin.plugin.domain.model.ops.JschResult; +import org.opengauss.admin.plugin.domain.model.ops.LiteInstallConfig; +import org.opengauss.admin.plugin.domain.model.ops.MinimalistInstallConfig; +import org.opengauss.admin.plugin.domain.model.ops.OpsClusterBody; +import org.opengauss.admin.plugin.domain.model.ops.UnInstallBody; +import org.opengauss.admin.plugin.domain.model.ops.UpgradeBody; +import org.opengauss.admin.plugin.domain.model.ops.WsSession; +import org.opengauss.admin.plugin.domain.model.ops.cache.WsConnectorManager; +import org.opengauss.admin.plugin.domain.model.ops.node.EnterpriseInstallNodeConfig; +import org.opengauss.admin.plugin.domain.model.ops.node.LiteInstallNodeConfig; +import org.opengauss.admin.plugin.domain.model.ops.node.MinimalistInstallNodeConfig; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.enums.ops.DatabaseKernelArch; +import org.opengauss.admin.plugin.enums.ops.DeployTypeEnum; +import org.opengauss.admin.plugin.enums.ops.InstallModeEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.enums.ops.UpgradeTypeEnum; +import org.opengauss.admin.plugin.service.ops.IOpsClusterNodeService; +import org.opengauss.admin.plugin.service.ops.IOpsClusterService; +import org.opengauss.admin.plugin.service.ops.impl.OpsClusterServiceImpl; +import org.opengauss.admin.plugin.utils.DownloadUtil; +import org.opengauss.admin.plugin.utils.JschUtil; +import org.opengauss.admin.plugin.vo.ops.AuditLogVO; +import org.opengauss.admin.plugin.vo.ops.OpsNodeLogVO; +import org.opengauss.admin.plugin.vo.ops.SessionVO; +import org.opengauss.admin.plugin.vo.ops.SlowSqlVO; +import org.opengauss.admin.plugin.vo.ops.UpgradeOsCheckVO; +import org.opengauss.admin.system.plugin.facade.HostFacade; +import org.opengauss.admin.system.plugin.facade.HostUserFacade; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; + +/** + * OpsClusterTest + * + * @author: wangchao + * @Date: 2025/9/11 21:21 + * @since 7.0.0-RC2 + **/ +@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +public class OpsClusterTest { + private static final String HOST_ID = "testHostId"; + private static final String NEW_INSTALL_CLUSTER_ID = "newInstallClusterId"; + private static final String OLD_CLUSTER_ID = "testClusterId"; + private static final String BUSINESS_ID = "testBusinessId"; + private static final String ROOT_PASSWORD = "testRootPassword"; + + @InjectMocks + @Spy + private IOpsClusterService opsClusterService = new OpsClusterServiceImpl(); + @Mock + private IOpsClusterNodeService opsClusterNodeService; + @Mock + private WsConnectorManager wsConnectorManager; + @Mock + private HostFacade hostFacade; + @Mock + private HostUserFacade hostUserFacade; + @Mock + private ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); + @Mock + private JschUtil jschUtil; + @Mock + private HttpServletRequest request; + @Mock + private DownloadUtil downloadUtil; + + @Before + public void setup() throws IOException, InterruptedException, JSchException { + doReturn(getClusterEntity()).when(opsClusterService).getById(OLD_CLUSTER_ID); + doReturn(null).when(opsClusterService).getById(NEW_INSTALL_CLUSTER_ID); + doReturn(getClusterEntity()).when(opsClusterService).getById("testName"); + when(wsConnectorManager.getSession(any())).thenReturn(Optional.of(new WsSession())); + when(threadPoolTaskExecutor.submit(any(Runnable.class))).thenReturn(new CompletableFuture<>()); + when(hostFacade.getById(any())).thenReturn(getHost()); + when(hostFacade.listByIds(any())).thenReturn(ListUtil.toList(getHost())); + when(hostUserFacade.getById(any())).thenReturn(getHostUser()); + when(hostUserFacade.getRootUserByHostId(any())).thenReturn(getRootUser()); + when(hostUserFacade.listHostUserByHostId(any())).thenReturn(getHostUserList()); + when(hostUserFacade.listHostUserByHostIdList(any())).thenReturn(getHostUserList()); + when(opsClusterNodeService.listClusterNodeByClusterId(any())).thenReturn(getClusterNodeEntity()); + doNothing().when(downloadUtil).download(any(), any(), any(), any()); + Session session = null; + JSch jSch = new JSch(); + session = jSch.getSession(""); + doReturn(Optional.of(session)).when(jschUtil).getSession(any(), any(), any(), any()); + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); + } + + @Test + public void testHasName() throws IOException, InterruptedException { + boolean hasNameResult = opsClusterService.hasName("testName"); + Assert.assertTrue(hasNameResult); + } + + @Test + public void testDownload() throws IOException, InterruptedException { + DownloadBody body = new DownloadBody(); + body.setBusinessId(BUSINESS_ID); + opsClusterService.download(body); + HttpServletResponse response = null; + opsClusterService.download("testHostId", "/mnt/d/", "testFileName", response); + } + + @Test + public void testInstallEnterpriseCluster() throws IOException, InterruptedException { + doReturn(getCmdResult()).when(jschUtil).executeCommand(any(), any()); + InstallBody body = getEnterpriseInstallBody(); + opsClusterService.install(body); + } + + @Test + public void testInstallLiteCluster() throws IOException, InterruptedException { + doReturn(getCmdResult()).when(jschUtil).executeCommand(any(), any()); + InstallBody body = getLiteInstallBody(); + opsClusterService.install(body); + } + + @Test + public void testInstallMiniCluster() throws IOException, InterruptedException { + doReturn(getCmdResult()).when(jschUtil).executeCommand(any(), any()); + InstallBody body = getSimpleInstallBody(); + opsClusterService.install(body); + } + + @Test + public void testUpgradeOsCheck() throws IOException, InterruptedException { + doReturn(getCmdDfResult()).when(jschUtil).executeCommand(any(), any()); + UpgradeOsCheckVO result = opsClusterService.upgradeOsCheck(OLD_CLUSTER_ID, ROOT_PASSWORD); + Assert.assertEquals(result.getDiskUsed(), Integer.valueOf(9)); + Assert.assertNotNull(result.getOsInfo()); + } + + @Test + public void testUpgrade() throws IOException, InterruptedException { + doReturn(getCmdDfResult()).when(jschUtil).executeCommand(any(), any()); + UpgradeBody upgradeBody = getUpgradeBody(); + opsClusterService.upgrade(upgradeBody); + } + + @Test + public void testUpgradeCommit() { + UpgradeBody upgradeBody = getUpgradeBody(); + opsClusterService.upgradeCommit(upgradeBody); + } + + @Test + public void testUpgradeRollback() { + UpgradeBody upgradeBody = getUpgradeBody(); + opsClusterService.upgradeRollback(upgradeBody); + } + + @Test + public void testQuickInstall() throws IOException, InterruptedException { + doReturn(getCmdResult()).when(jschUtil).executeCommand(any(), any()); + InstallBody installBody = getQuickInstallBody(); + opsClusterService.quickInstall(installBody); + } + + @Test + public void testUninstall() throws IOException, InterruptedException { + doReturn(getCmdResult()).when(jschUtil).executeCommand(any(), any()); + UnInstallBody unInstallBody = new UnInstallBody(); + unInstallBody.setClusterId(OLD_CLUSTER_ID); + unInstallBody.setForce(false); + unInstallBody.setBusinessId(BUSINESS_ID); + opsClusterService.uninstall(unInstallBody); + } + + @Test + public void testRestart() throws IOException, InterruptedException { + doReturn(getCmdResult()).when(jschUtil).executeCommand(any(), any()); + OpsClusterBody opsClusterBody = new OpsClusterBody(); + opsClusterBody.setClusterId(OLD_CLUSTER_ID); + opsClusterBody.setBusinessId(BUSINESS_ID); + opsClusterService.restart(opsClusterBody); + } + + @Test + public void testStart() throws IOException, InterruptedException { + doReturn(getCmdResult()).when(jschUtil).executeCommand(any(), any()); + OpsClusterBody opsClusterBody = new OpsClusterBody(); + opsClusterBody.setClusterId(OLD_CLUSTER_ID); + opsClusterBody.setBusinessId(BUSINESS_ID); + opsClusterService.start(opsClusterBody); + } + + @Test + public void testStop() throws IOException, InterruptedException { + doReturn(getCmdResult()).when(jschUtil).executeCommand(any(), any()); + OpsClusterBody opsClusterBody = new OpsClusterBody(); + opsClusterBody.setClusterId(OLD_CLUSTER_ID); + opsClusterBody.setBusinessId(BUSINESS_ID); + opsClusterService.stop(opsClusterBody); + } + + @Test + public void testLs() throws IOException, InterruptedException { + List hostFiles = opsClusterService.ls(HOST_ID, "/ops"); + Assert.assertNotNull(hostFiles); + } + + @Test + public void testLogPath() throws IOException, InterruptedException { + doReturn(getCmdResult()).when(jschUtil).executeCommand(any(), any()); + OpsNodeLogVO nodeLogVO = opsClusterService.logPath(OLD_CLUSTER_ID, HOST_ID); + Assert.assertEquals(nodeLogVO.getOpLogPath(), "testCmdResult"); + } + + @Test + public void testAuditLog() { + List auditLogVOList = opsClusterService.auditLog(getPage(), OLD_CLUSTER_ID, HOST_ID, + "2023-12-24+00:00:00", "2023-12-24+23:59:59"); + Assert.assertNotNull(auditLogVOList); + } + + @Test + public void testListSession() { + List sessionVOList = opsClusterService.listSession(OLD_CLUSTER_ID, HOST_ID); + Assert.assertNotNull(sessionVOList); + } + + @Test + public void testSlowSql() { + List slowSqlVOS = opsClusterService.slowSql(getPage(), OLD_CLUSTER_ID, HOST_ID, + "2023-12-24+00:00:00", "2023-12-24+23:59:59"); + Assert.assertNotNull(slowSqlVOS); + } + + private OpsClusterEntity getClusterEntity() { + OpsClusterEntity clusterEntity = new OpsClusterEntity(); + clusterEntity.setClusterId(OLD_CLUSTER_ID); + clusterEntity.setInstallPath("/ops/opengauss"); + clusterEntity.setVersion(OpenGaussVersionEnum.ENTERPRISE); + return clusterEntity; + } + + private List getClusterList() { + List clusterList = new ArrayList<>(); + org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO clusterVO + = new org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO(); + clusterVO.setClusterId(OLD_CLUSTER_ID); + clusterVO.setVersionNum("5.1.1"); + clusterVO.setVersion(OpenGaussVersionEnum.ENTERPRISE.name()); + clusterList.add(clusterVO); + return clusterList; + } + + private Page getPage() { + Page page = new Page<>(); + page.setCurrent(1); + page.setSize(10); + return page; + } + + private List getClusterNodeEntity() { + List nodeEntityList = new ArrayList<>(); + OpsClusterNodeEntity clusterNodeEntity = new OpsClusterNodeEntity(); + clusterNodeEntity.setClusterId(OLD_CLUSTER_ID); + clusterNodeEntity.setHostId(HOST_ID); + clusterNodeEntity.setClusterRole(ClusterRoleEnum.MASTER); + nodeEntityList.add(clusterNodeEntity); + return nodeEntityList; + } + + private OpsHostEntity getHost() { + OpsHostEntity hostEntity = new OpsHostEntity(); + hostEntity.setHostId(HOST_ID); + return hostEntity; + } + + private OpsHostUserEntity getHostUser() { + OpsHostUserEntity userEntity1 = new OpsHostUserEntity(); + userEntity1.setUsername("lhf"); + userEntity1.setHostId(HOST_ID); + userEntity1.setPassword("testPassword"); + return userEntity1; + } + + private OpsHostUserEntity getRootUser() { + OpsHostUserEntity userEntity1 = new OpsHostUserEntity(); + userEntity1.setUsername("root"); + userEntity1.setHostId(HOST_ID); + userEntity1.setPassword(ROOT_PASSWORD); + return userEntity1; + } + + private List getHostUserList() { + List userList = new ArrayList<>(); + OpsHostUserEntity userEntity1 = new OpsHostUserEntity(); + userEntity1.setUsername("lhf"); + userEntity1.setHostId(HOST_ID); + userEntity1.setPassword("testPassword"); + userList.add(userEntity1); + OpsHostUserEntity userEntity2 = new OpsHostUserEntity(); + userEntity2.setUsername("root"); + userEntity2.setHostId(HOST_ID); + userEntity2.setPassword(ROOT_PASSWORD); + userList.add(userEntity2); + return userList; + } + + private JschResult getCmdResult() { + JschResult result = new JschResult(); + result.setExitCode(0); + result.setResult("testCmdResult"); + return result; + } + + private JschResult getCmdDfResult() { + JschResult result = new JschResult(); + result.setExitCode(0); + result.setResult( + "Filesystem Size Used Avail Use% Mounted on\n" + "devtmpfs 3.8G 0 3.8G 0% /dev\n" + + "tmpfs 3.8G 48K 3.8G 1% /dev/shm\n" + "tmpfs 3.8G 756K 3.8G 1% /run\n" + + "tmpfs 3.8G 0 3.8G 0% /sys/fs/cgroup\n" + "/dev/vda1 99G 8.0G 87G 9% /\n" + + "tmpfs 773M 0 773M 0% /run/user/0\n" + + "tmpfs 773M 0 773M 0% /run/user/1002"); + return result; + } + + private InstallBody getEnterpriseInstallBody() { + InstallContext installContext = new InstallContext(); + installContext.setOpenGaussVersion(OpenGaussVersionEnum.ENTERPRISE); + EnterpriseInstallConfig enterpriseInstallConfig = new EnterpriseInstallConfig(); + enterpriseInstallConfig.setInstallPath("/opt/openGauss/install/app"); + enterpriseInstallConfig.setIsInstallCM(false); + enterpriseInstallConfig.setCorePath("/opt/openGauss/corefile"); + enterpriseInstallConfig.setPort(5432); + enterpriseInstallConfig.setLogPath("/opt/openGauss/log/omm"); + enterpriseInstallConfig.setEnableDCF(false); + enterpriseInstallConfig.setOmToolsPath("/opt/openGauss/install/om"); + enterpriseInstallConfig.setTmpPath("/opt/openGauss/tmp"); + enterpriseInstallConfig.setDatabaseKernelArch(DatabaseKernelArch.MASTER_SLAVE); + enterpriseInstallConfig.setDatabaseUsername(""); + enterpriseInstallConfig.setDatabasePassword("1qaz2wsx#EDC"); + + EnterpriseInstallNodeConfig enterpriseInstallNodeConfig = new EnterpriseInstallNodeConfig(); + enterpriseInstallNodeConfig.setClusterRole(ClusterRoleEnum.MASTER); + enterpriseInstallNodeConfig.setHostId("1729406555136229377"); + enterpriseInstallNodeConfig.setIsCMMaster(false); + enterpriseInstallNodeConfig.setPublicIp("124.222.71.6"); + enterpriseInstallNodeConfig.setPrivateIp("10.0.4.12"); + enterpriseInstallNodeConfig.setHostname("hostname"); + enterpriseInstallNodeConfig.setInstallUserId("1729407004216164353"); + enterpriseInstallNodeConfig.setInstallUsername("wangn"); + enterpriseInstallNodeConfig.setCmDataPath("/opt/openGauss/data/cmserver"); + enterpriseInstallNodeConfig.setCmPort(15300); + enterpriseInstallNodeConfig.setDataPath("/opt/openGauss/install/data/dn"); + enterpriseInstallNodeConfig.setAzPriority("1"); + + List nodeConfigList = new ArrayList<>(); + nodeConfigList.add(enterpriseInstallNodeConfig); + enterpriseInstallConfig.setNodeConfigList(nodeConfigList); + installContext.setEnterpriseInstallConfig(enterpriseInstallConfig); + installContext.setClusterId(NEW_INSTALL_CLUSTER_ID); + installContext.setOpenGaussVersionNum("5.1.0"); + installContext.setInstallMode(InstallModeEnum.OFF_LINE); + installContext.setDeployType(DeployTypeEnum.SINGLE_NODE); + installContext.setInstallPackagePath("/ops"); + InstallBody installBody = new InstallBody(); + installBody.setInstallContext(installContext); + installBody.setBusinessId(BUSINESS_ID); + return installBody; + } + + private InstallBody getLiteInstallBody() { + InstallContext installContext = new InstallContext(); + installContext.setOpenGaussVersion(OpenGaussVersionEnum.LITE); + LiteInstallConfig liteInstallConfig = new LiteInstallConfig(); + liteInstallConfig.setPort(5432); + liteInstallConfig.setDatabaseUsername(""); + liteInstallConfig.setDatabasePassword("1qaz2wsx#EDC"); + liteInstallConfig.setInstallPackagePath("/opt/software/openGauss"); + List nodeConfigList = new ArrayList<>(); + LiteInstallNodeConfig liteInstallNodeConfig = new LiteInstallNodeConfig(); + liteInstallNodeConfig.setInstallPath("/opt/openGauss/install/app"); + liteInstallNodeConfig.setInstallUserId("testUserId"); + liteInstallNodeConfig.setClusterRole(ClusterRoleEnum.MASTER); + liteInstallNodeConfig.setHostId(HOST_ID); + liteInstallNodeConfig.setDataPath("/opt/openGauss/data"); + liteInstallNodeConfig.setRootPassword(ROOT_PASSWORD); + nodeConfigList.add(liteInstallNodeConfig); + liteInstallConfig.setNodeConfigList(nodeConfigList); + installContext.setLiteInstallConfig(liteInstallConfig); + installContext.setClusterId(NEW_INSTALL_CLUSTER_ID); + installContext.setOpenGaussVersionNum("5.0.0"); + installContext.setInstallMode(InstallModeEnum.OFF_LINE); + installContext.setDeployType(DeployTypeEnum.SINGLE_NODE); + installContext.setInstallPackagePath("/ops"); + InstallBody installBody = new InstallBody(); + installBody.setInstallContext(installContext); + installBody.setBusinessId(BUSINESS_ID); + return installBody; + } + + private InstallBody getSimpleInstallBody() { + InstallContext installContext = new InstallContext(); + installContext.setOpenGaussVersion(OpenGaussVersionEnum.MINIMAL_LIST); + MinimalistInstallConfig miniInstallConfig = new MinimalistInstallConfig(); + miniInstallConfig.setPort(5432); + miniInstallConfig.setDatabaseUsername(""); + miniInstallConfig.setDatabasePassword("1qaz2wsx#EDC"); + miniInstallConfig.setInstallPackagePath("/opt/software/openGauss"); + List nodeConfigList = new ArrayList<>(); + MinimalistInstallNodeConfig miniInstallNodeConfig = new MinimalistInstallNodeConfig(); + miniInstallNodeConfig.setInstallPath("/opt/openGauss/install/app"); + miniInstallNodeConfig.setInstallUserId("testUserId"); + miniInstallNodeConfig.setClusterRole(ClusterRoleEnum.MASTER); + miniInstallNodeConfig.setHostId(HOST_ID); + miniInstallNodeConfig.setRootPassword(ROOT_PASSWORD); + miniInstallNodeConfig.setIsInstallDemoDatabase(true); + nodeConfigList.add(miniInstallNodeConfig); + miniInstallConfig.setNodeConfigList(nodeConfigList); + installContext.setMinimalistInstallConfig(miniInstallConfig); + installContext.setClusterId(NEW_INSTALL_CLUSTER_ID); + installContext.setOpenGaussVersionNum("5.0.0"); + installContext.setInstallMode(InstallModeEnum.OFF_LINE); + installContext.setDeployType(DeployTypeEnum.SINGLE_NODE); + installContext.setInstallPackagePath("/ops"); + InstallBody installBody = new InstallBody(); + installBody.setInstallContext(installContext); + installBody.setBusinessId(BUSINESS_ID); + return installBody; + } + + private InstallBody getQuickInstallBody() { + InstallBody installBody = getSimpleInstallBody(); + installBody.setQuickInstall(true); + installBody.setQuickInstallResourceUrl( + "https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.1.0/x86/openGauss-5.1.0-CentOS-64bit.tar.bz2"); + installBody.setBusinessId(BUSINESS_ID); + return installBody; + } + + private UpgradeBody getUpgradeBody() { + UpgradeBody upgradeBody = new UpgradeBody(); + upgradeBody.setClusterId(OLD_CLUSTER_ID); + upgradeBody.setUpgradeType(UpgradeTypeEnum.GRAY_UPGRADE); + upgradeBody.setBusinessId(BUSINESS_ID); + upgradeBody.setUpgradePackagePath("D:\\upload\\openGauss-5.1.0-CentOS-64bit-all.tar.gz"); + upgradeBody.setVersionNum("5.1.0"); + upgradeBody.setHostRootPassword(ROOT_PASSWORD); + return upgradeBody; + } +} diff --git a/ER-Model/plugins/base-ops/src/test/java/WdrTest.java b/ER-Model/plugins/base-ops/src/test/java/WdrTest.java new file mode 100644 index 000000000..ab8f33073 --- /dev/null +++ b/ER-Model/plugins/base-ops/src/test/java/WdrTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + */ + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostEntity; +import org.opengauss.admin.common.core.domain.entity.ops.OpsHostUserEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterEntity; +import org.opengauss.admin.plugin.domain.entity.ops.OpsClusterNodeEntity; +import org.opengauss.admin.plugin.domain.model.ops.JschResult; +import org.opengauss.admin.plugin.enums.ops.ClusterRoleEnum; +import org.opengauss.admin.plugin.enums.ops.OpenGaussVersionEnum; +import org.opengauss.admin.plugin.service.ops.IOpsClusterService; +import org.opengauss.admin.plugin.service.ops.IOpsWdrService; +import org.opengauss.admin.plugin.service.ops.impl.OpsWdrServiceImpl; +import org.opengauss.admin.plugin.vo.ops.DwrSnapshotVO; +import org.opengauss.admin.system.plugin.facade.HostFacade; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +/** + * WdrTest + * + * @author: wangchao + * @Date: 2025/9/11 21:21 + * @since 7.0.0-RC2 + **/ +@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +public class WdrTest { + private static final String HOST_ID = "testHostId"; + private static final String OLD_CLUSTER_ID = "testClusterId"; + + @InjectMocks + @Spy + private IOpsWdrService wdrService = new OpsWdrServiceImpl(); + @Mock + private IOpsClusterService opsClusterService; + @Mock + private HostFacade hostFacade; + + @Before + public void setup() throws IOException, InterruptedException { + when(opsClusterService.getById(any())).thenReturn(getClusterEntity()); + when(hostFacade.getById(any())).thenReturn(getHost()); + } + + @Test + public void testListSnapshot() { + Page result = wdrService.listSnapshot(getPage(), OLD_CLUSTER_ID, HOST_ID); + Assert.assertNotNull(result); + } + + private Page getPage() { + Page page = new Page<>(); + page.setCurrent(1); + page.setSize(10); + return page; + } + + private OpsClusterEntity getClusterEntity() { + OpsClusterEntity clusterEntity = new OpsClusterEntity(); + clusterEntity.setClusterId(OLD_CLUSTER_ID); + clusterEntity.setInstallPath("/ops/opengauss"); + clusterEntity.setPort(5432); + clusterEntity.setDatabaseUsername("gaussdb"); + clusterEntity.setDatabasePassword("1qaz2wsx#EDC"); + clusterEntity.setVersion(OpenGaussVersionEnum.ENTERPRISE); + return clusterEntity; + } + + private OpsHostEntity getHost() { + OpsHostEntity hostEntity = new OpsHostEntity(); + hostEntity.setHostId(HOST_ID); + hostEntity.setPublicIp("124.222.78.113"); + hostEntity.setPort(22); + return hostEntity; + } + + private List getClusterNodeEntity() { + List nodeEntityList = new ArrayList<>(); + OpsClusterNodeEntity clusterNodeEntity = new OpsClusterNodeEntity(); + clusterNodeEntity.setClusterId(OLD_CLUSTER_ID); + clusterNodeEntity.setHostId(HOST_ID); + clusterNodeEntity.setClusterRole(ClusterRoleEnum.MASTER); + nodeEntityList.add(clusterNodeEntity); + return nodeEntityList; + } + + private OpsHostUserEntity getHostUser() { + OpsHostUserEntity userEntity1 = new OpsHostUserEntity(); + userEntity1.setUsername("lhf"); + userEntity1.setHostId(HOST_ID); + userEntity1.setPassword("testPassword"); + return userEntity1; + } + + private JschResult getCmdResult() { + JschResult result = new JschResult(); + result.setExitCode(0); + result.setResult("testCmdResult"); + return result; + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/.browserslistrc b/ER-Model/plugins/base-ops/web-ui/.browserslistrc new file mode 100644 index 000000000..dc3bc09a2 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/.browserslistrc @@ -0,0 +1,4 @@ +> 1% +last 2 versions +not dead +not ie 11 diff --git a/ER-Model/plugins/base-ops/web-ui/.editorconfig b/ER-Model/plugins/base-ops/web-ui/.editorconfig new file mode 100644 index 000000000..caa0bf5ed --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/.editorconfig @@ -0,0 +1,20 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.py] +indent_style = space +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false + +[COMMIT_EDITMSG] +max_line_length = 80 diff --git a/ER-Model/plugins/base-ops/web-ui/.env.development b/ER-Model/plugins/base-ops/web-ui/.env.development new file mode 100644 index 000000000..ca190dac1 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/.env.development @@ -0,0 +1,14 @@ +# Development environment configuration +ENV = 'development' + +# development environment +VUE_APP_BASE_API = '/plugins/base-ops' + +# test env api base url +API_BASE_URL= 'http://localhost:9494' + +# test env port +port = 8081 + +# websocket address +VUE_APP_WS_BASE_URL='localhost:9494' diff --git a/ER-Model/plugins/base-ops/web-ui/.env.production b/ER-Model/plugins/base-ops/web-ui/.env.production new file mode 100644 index 000000000..90c88f4f1 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/.env.production @@ -0,0 +1,8 @@ +# Production environment configuration +ENV = 'production' + +# Production environment api base url +VUE_APP_BASE_API = '/plugins/base-ops' + +# websocket address +VUE_APP_WS_BASE_URL='localhost:9494' diff --git a/ER-Model/plugins/base-ops/web-ui/.env.staging b/ER-Model/plugins/base-ops/web-ui/.env.staging new file mode 100644 index 000000000..99ccd1d12 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/.env.staging @@ -0,0 +1,7 @@ +NODE_ENV = production + +# Test environment configuration +ENV = 'staging' + +# Test environment api base url +VUE_APP_BASE_API = '/stage-api' diff --git a/ER-Model/plugins/base-ops/web-ui/.eslintignore b/ER-Model/plugins/base-ops/web-ui/.eslintignore new file mode 100644 index 000000000..e0762ebb1 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/.eslintignore @@ -0,0 +1,6 @@ +# Ignore syntax checking for files of type js in the public directory +public/*.js +# Ignore syntax checking for files in the src directory +src/assets +# Ignore syntax checking for font files in the modeling directory +src/views/modeling/style diff --git a/ER-Model/plugins/base-ops/web-ui/.eslintrc.js b/ER-Model/plugins/base-ops/web-ui/.eslintrc.js new file mode 100644 index 000000000..42d43a9ec --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/.eslintrc.js @@ -0,0 +1,101 @@ +module.exports = { + root: true, + env: { + node: true, + 'vue/setup-compiler-macros': true + }, + 'extends': [ + 'plugin:vue/vue3-essential', + 'eslint:recommended', + '@vue/typescript/recommended' + ], + parserOptions: { + ecmaVersion: 2020 + }, + rules: { + 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'vue/multi-word-component-names': 'off', + // 'vue/html-self-closing': ['error', { + // 'html': { + // 'void': 'always', + // 'normal': 'never', + // 'component': 'always' + // } + // }], + '@typescript-eslint/ban-ts-comment': [ + 'error', + { + 'ts-ignore': 'allow-with-description', + 'minimumDescriptionLength': 10 + } + ], + '@typescript-eslint/no-explicit-any': 'off', + 'arrow-spacing': [2, { + 'before': true, + 'after': true + }], + 'block-spacing': [2, 'always'], + 'brace-style': [2, '1tbs', { + 'allowSingleLine': true + }], + 'quotes': [2, 'single', { + 'avoidEscape': true, + 'allowTemplateLiterals': true + }], + 'camelcase': [0, { + 'properties': 'always' + }], + 'comma-dangle': [2, 'never'], + 'comma-spacing': [2, { + 'before': false, + 'after': true + }], + 'comma-style': [2, 'last'], + 'jsx-quotes': [2, 'prefer-single'], + 'key-spacing': [2, { + 'beforeColon': false, + 'afterColon': true + }], + 'keyword-spacing': [2, { + 'before': true, + 'after': true + }], + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty': 'off', + 'no-empty-character-class': 2, + 'no-empty-pattern': 2, + 'no-lone-blocks': 2, + 'no-mixed-spaces-and-tabs': 2, + 'no-multi-spaces': 2, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [2, { + 'max': 1 + }], + 'require-atomic-updates': 'off', + 'semi': ['error', 'never'], + 'semi-spacing': [2, { + 'before': false, + 'after': true + }], + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'always'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': [2, { + 'words': true, + 'nonwords': false + }], + 'spaced-comment': [2, 'always', { + 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] + }], + 'template-curly-spacing': [2, 'never'], + 'object-curly-spacing': [2, 'always', { + objectsInObjects: false + }], + 'array-bracket-spacing': [2, 'never'] + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/.gitignore b/ER-Model/plugins/base-ops/web-ui/.gitignore new file mode 100644 index 000000000..403adbc1e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules +/dist + + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/ER-Model/plugins/base-ops/web-ui/.prettierrc b/ER-Model/plugins/base-ops/web-ui/.prettierrc new file mode 100644 index 000000000..d25e2bc13 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/.prettierrc @@ -0,0 +1,5 @@ +{ + "printWidth": 100, + "semi": true, + "tabWidth": 2 +} diff --git a/ER-Model/plugins/base-ops/web-ui/README.md b/ER-Model/plugins/base-ops/web-ui/README.md new file mode 100644 index 000000000..63ac1bc57 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/README.md @@ -0,0 +1,24 @@ +# web-ui + +## Project setup +``` +yarn install +``` + +### Compiles and hot-reloads for development +``` +yarn dev +``` + +### Compiles and minifies for production +``` +yarn build +``` + +### Lints and fixes files +``` +yarn lint +``` + +### Customize configuration +See [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/ER-Model/plugins/base-ops/web-ui/babel.config.js b/ER-Model/plugins/base-ops/web-ui/babel.config.js new file mode 100644 index 000000000..e9558405f --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/babel.config.js @@ -0,0 +1,5 @@ +module.exports = { + presets: [ + '@vue/cli-plugin-babel/preset' + ] +} diff --git a/ER-Model/plugins/base-ops/web-ui/jsconfig.json b/ER-Model/plugins/base-ops/web-ui/jsconfig.json new file mode 100644 index 000000000..4aafc5f6e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/jsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "esnext", + "baseUrl": "./", + "moduleResolution": "node", + "paths": { + "@/*": [ + "src/*" + ] + }, + "lib": [ + "esnext", + "dom", + "dom.iterable", + "scripthost" + ] + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/lib/opendesign-icons-1.0.3.tgz b/ER-Model/plugins/base-ops/web-ui/lib/opendesign-icons-1.0.3.tgz new file mode 100644 index 000000000..9e302607c Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/lib/opendesign-icons-1.0.3.tgz differ diff --git a/ER-Model/plugins/base-ops/web-ui/lib/opendesign2-2.0.22.tgz b/ER-Model/plugins/base-ops/web-ui/lib/opendesign2-2.0.22.tgz new file mode 100644 index 000000000..a507b647a Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/lib/opendesign2-2.0.22.tgz differ diff --git a/ER-Model/plugins/base-ops/web-ui/package-lock.json b/ER-Model/plugins/base-ops/web-ui/package-lock.json new file mode 100644 index 000000000..094624cfb --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/package-lock.json @@ -0,0 +1,16644 @@ +{ + "name": "web-ui", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web-ui", + "version": "1.0.0", + "dependencies": { + "@antv/x6": "1.32.10", + "@antv/x6-vue-shape": "1.4.2", + "@arco-design/web-vue": "2.54.6", + "@computing/opendesign-icons": "file:lib\\opendesign-icons-1.0.3.tgz", + "@computing/opendesign2": "file:lib\\opendesign2-2.0.22.tgz", + "@highlightjs/vue-plugin": "2.1.0", + "@types/lodash": "4.14.190", + "@vueuse/core": "7.3.0", + "@xterm/addon-attach": "^0.10.0-beta.1", + "@xterm/addon-fit": "^0.9.0-beta.1", + "animate.css": "4.1.1", + "axios": "0.27.2", + "codemirror": "^5.6.0", + "codemirror-editor-vue3": "^2.3.0", + "core-js": "^3.8.3", + "dayjs": "1.11.4", + "echarts": "5.2.2", + "highlight.js": "11.6.0", + "jsencrypt": "3.0.0-rc.1", + "mitt": "3.0.0", + "pinia": "2.0.17", + "query-string": "7.1.1", + "sass": "^1.77.6", + "vue": "3.2.13", + "vue-echarts": "6.2.3", + "vue-grid-layout": "^3.0.0-beta1", + "vue-i18n": "^9.3.0-beta.6", + "vue-router": "4.0.3", + "vue-winbox": "^0.1.0", + "xterm": "^5.3.0" + }, + "devDependencies": { + "@computing/opendesign-icons": "file:lib\\opendesign-icons-1.0.3.tgz", + "@computing/opendesign2": "file:lib\\opendesign2-2.0.22.tgz", + "@typescript-eslint/eslint-plugin": "5.4.0", + "@typescript-eslint/parser": "5.4.0", + "@vue/cli-plugin-babel": "~5.0.0", + "@vue/cli-plugin-eslint": "~5.0.0", + "@vue/cli-plugin-router": "~5.0.0", + "@vue/cli-plugin-typescript": "~5.0.0", + "@vue/cli-service": "~5.0.0", + "@vue/eslint-config-typescript": "9.1.0", + "eslint": "7.32.0", + "eslint-plugin-vue": "8.0.3", + "husky": "1.3.1", + "less": "4.0.0", + "less-loader": "8.0.0", + "sass-loader": "^14.2.1", + "svg-sprite-loader": "6.0.11", + "typescript": "4.9" + } + }, + "node_modules/@achrinza/node-ipc": { + "version": "9.2.9", + "resolved": "https://registry.npmmirror.com/@achrinza/node-ipc/-/node-ipc-9.2.9.tgz", + "integrity": "sha512-7s0VcTwiK/0tNOVdSX9FWMeFdOEcsAOz9HesBldXxFMaGvIak7KC2z9tV9EgsQXn6KUsWsfIkViMNuIo0GoZDQ==", + "dev": true, + "dependencies": { + "@node-ipc/js-queue": "2.0.3", + "event-pubsub": "4.3.0", + "js-message": "1.0.7" + }, + "engines": { + "node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21 || 22" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@antv/x6": { + "version": "1.32.10", + "resolved": "https://registry.npmmirror.com/@antv/x6/-/x6-1.32.10.tgz", + "integrity": "sha512-qlTWNJoGdH6VDvG6t5zo3SUhH5yt+cdxfKPWn0EGH36roGt14RGJBn/JHe7Oe2E2TOxVBu8cMyzlKFI8xkyGMA==", + "dependencies": { + "csstype": "^3.0.3", + "jquery": "^3.5.1", + "jquery-mousewheel": "^3.1.13", + "lodash-es": "^4.17.15", + "mousetrap": "^1.6.5", + "utility-types": "^3.10.0" + } + }, + "node_modules/@antv/x6-vue-shape": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/@antv/x6-vue-shape/-/x6-vue-shape-1.4.2.tgz", + "integrity": "sha512-2V0MjZ/rE2xIURNzl54I5OywcbbsbgLXfG7ko/69WRRbISTbSdYQRfOPseO19KIpHHZlGj00HIDad0JyHnN6vQ==", + "dependencies": { + "vue-demi": "latest" + }, + "peerDependencies": { + "@antv/x6": ">=1.0.0", + "@vue/composition-api": "^1.0.0-rc.6", + "vue": "^2.6.12 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@antv/x6-vue-shape/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@arco-design/color": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/@arco-design/color/-/color-0.4.0.tgz", + "integrity": "sha512-s7p9MSwJgHeL8DwcATaXvWT3m2SigKpxx4JA1BGPHL4gfvaQsmQfrLBDpjOJFJuJ2jG2dMt3R3P8Pm9E65q18g==", + "dependencies": { + "color": "^3.1.3" + } + }, + "node_modules/@arco-design/web-vue": { + "version": "2.54.6", + "resolved": "https://registry.npmmirror.com/@arco-design/web-vue/-/web-vue-2.54.6.tgz", + "integrity": "sha512-Qx+HHL5G5gIvrbxGC6ZF/L8ACD1L/RkPJd9e4SR+vI6lEg+nGlUBplkR4pY7e7mqYXY2H6uzh+radT+QxXKVRg==", + "dependencies": { + "@arco-design/color": "^0.4.0", + "b-tween": "^0.3.3", + "b-validate": "^1.4.4", + "compute-scroll-into-view": "^1.0.17", + "dayjs": "^1.10.3", + "number-precision": "^1.5.0", + "resize-observer-polyfill": "^1.5.1", + "scroll-into-view-if-needed": "^2.2.28" + }, + "peerDependencies": { + "vue": "^3.1.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", + "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.27.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.10" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", + "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.9", + "resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.25.9.tgz", + "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", + "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", + "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", + "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", + "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", + "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", + "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", + "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", + "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", + "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", + "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", + "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", + "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.0.tgz", + "integrity": "sha512-LOAozRVbqxEVjSKfhGnuLoE4Kz4Oc5UJzuvFUhSsQzdCdaAQu06mG8zDv2GFSerM62nImUZ7K92vxnQcLSDlCQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.0.tgz", + "integrity": "sha512-dGopk9nZrtCs2+nfIem25UuHyt5moSJamArzIoh9/vezUQPmYDOzjaHDCkAzuGJibCIkPup8rMT2+wYB6S73cA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/preset-env/-/preset-env-7.28.0.tgz", + "integrity": "sha512-VmaxeGOwuDqzLl5JUkIRM1X2Qu2uKGxHEQWh+cvvbl7JuJRgKGJSfsEF/bUaxFhJl/XAyxBe7q7qSuTbKFuCyg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.0", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.0", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.27.1", + "@babel/plugin-transform-classes": "^7.28.0", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.0", + "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.0", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "core-js-compat": "^3.43.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmmirror.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.0.tgz", + "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@computing/opendesign-icons": { + "version": "1.0.3", + "resolved": "file:lib/opendesign-icons-1.0.3.tgz", + "integrity": "sha512-fPc0ktjXFcDg2SODP8DIJaa0fHqsTXeR7FdmddaazPSVLE86f6vI7S0y0AucCj7r6EET+Vv4UpMzxmcweP6Asw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@computing/opendesign2": { + "version": "2.0.22", + "resolved": "file:lib/opendesign2-2.0.22.tgz", + "integrity": "sha512-cNmMsXIUKWeh8qT4pRWYwapOKRRBF7HYTDqcY4XQoWHC6XhDAaxZL9zAXmZmIlJK1ebJg+n4qWUlFxt/G4Vn2w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "element-plus": ">=2.6.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmmirror.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@element-plus/icons-vue": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz", + "integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==", + "dev": true, + "peer": true, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.2", + "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.2.tgz", + "integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==", + "dev": true, + "peer": true, + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.2", + "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.2.tgz", + "integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==", + "dev": true, + "peer": true, + "dependencies": { + "@floating-ui/core": "^1.7.2", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "dev": true, + "peer": true + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmmirror.com/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@highlightjs/vue-plugin": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/@highlightjs/vue-plugin/-/vue-plugin-2.1.0.tgz", + "integrity": "sha512-E+bmk4ncca+hBEYRV2a+1aIzIV0VSY/e5ArjpuSN9IO7wBJrzUE2u4ESCwrbQD7sAy+jWQjkV5qCCWgc+pu7CQ==", + "peerDependencies": { + "highlight.js": "^11.0.1", + "vue": "^3" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@interactjs/actions": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/actions/-/actions-1.10.27.tgz", + "integrity": "sha512-FCRg5KwB+stkPcAMx/Cn0fgGP6p4LyMX9S/Upcn/W+hpYme31bPi54PCqmOebzz6myTthN6zFf9jMyLOqtI/gg==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/auto-scroll": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/auto-scroll/-/auto-scroll-1.10.27.tgz", + "integrity": "sha512-zPg5TnVsZv+9Hnt4qnbxLvBMf+rIWHkoJVoSETEbLNaj90C8hIyr0pVwukSUySSgDhCgQ7np0f3pg4INLq9beQ==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/auto-start": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/auto-start/-/auto-start-1.10.27.tgz", + "integrity": "sha512-ECLBO/nxmaF1knncJKIE5F7la3KKRgEkn0Cu2JTPOYj9xy/LpfYElo3wkRHsodgOqF651nR70GK2/IzPR2lO9A==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/core": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/core/-/core-1.10.27.tgz", + "integrity": "sha512-SliUr/3ZbLAdED8LokzYzWHWMdCB5Cq+UnpXuRy+BIod1j97m4IUFf/D1iIKUBBjBcucgXbz28z96WnenVCB7Q==", + "peerDependencies": { + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/dev-tools": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/dev-tools/-/dev-tools-1.10.27.tgz", + "integrity": "sha512-YolmBwRaKH1gWbvyLeV3m5QSwtD38lOZnCBA87PCAlcd9PQAC2gb03fEPeEyD336bE20oLB8f0WZt4Wre+afiw==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27", + "vue": "3" + }, + "peerDependencies": { + "@interactjs/modifiers": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/inertia": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/inertia/-/inertia-1.10.27.tgz", + "integrity": "sha512-S/SVj/M0D+wWWPVXHcXN/YUWOK51LFJsEA+CTgVnFhlSU04+1FUvNLwilCZcHgECu1RJxZNKDwZysDATg+r8jQ==", + "dependencies": { + "@interactjs/offset": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/modifiers": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/interact": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/interact/-/interact-1.10.27.tgz", + "integrity": "sha512-XdH3A2UUzjEFGGJgFuJlhiz99tE8jB8xNh/DmnoMuL6uOQPxNA+sWRnzEVjG0+zY2P3/dbhEpi4Cn3FLPzydwA==", + "dependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/interactjs": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/interactjs/-/interactjs-1.10.27.tgz", + "integrity": "sha512-UwhfUZMZVXUY72efPABuKSBz1sUY+r+49v8t6Ku9o5Jq76AKg9mwmdGszIlOn3ppnFDDjvtzK/8TL+Sbd0EQEA==", + "dependencies": { + "@interactjs/actions": "1.10.27", + "@interactjs/auto-scroll": "1.10.27", + "@interactjs/auto-start": "1.10.27", + "@interactjs/core": "1.10.27", + "@interactjs/dev-tools": "1.10.27", + "@interactjs/inertia": "1.10.27", + "@interactjs/interact": "1.10.27", + "@interactjs/modifiers": "1.10.27", + "@interactjs/offset": "1.10.27", + "@interactjs/pointer-events": "1.10.27", + "@interactjs/reflow": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/modifiers": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/modifiers/-/modifiers-1.10.27.tgz", + "integrity": "sha512-ei/qfoQ+9/8k6WzNzdNqHI6cWkIV576N4Ap16r5CoqOWwhA6Xzj3OMHf1g0t1O4eSq2HdJsVJn3eLNfw9HsbeQ==", + "dependencies": { + "@interactjs/snappers": "1.10.27" + }, + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/offset": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/offset/-/offset-1.10.27.tgz", + "integrity": "sha512-AezsLiuK+Qv4jXdYuRa65HJ2pMFMZPlqiAep6ZRLwhP9HE7O75c0EAm+gfx+dpPrHNHs6J9LaiKSZl+B+A2qAw==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/pointer-events": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/pointer-events/-/pointer-events-1.10.27.tgz", + "integrity": "sha512-Yo5SS6PhWfC93gHNxnwwW0wvebo5hSYJKGaSnAHO4f9Lh25yibecMnmPBmiEfWVcdMboK/kXrme43mHQaRegVg==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/reflow": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/reflow/-/reflow-1.10.27.tgz", + "integrity": "sha512-Msm0QdYFr40oSsPFxyCR3dHN/pQx34k7QSkdN1uIsUn/drrm+YSFvrvVOu99DFOwr7gTThr5vNe06Sz4vubTSA==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/core": "1.10.27", + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/snappers": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/snappers/-/snappers-1.10.27.tgz", + "integrity": "sha512-HZLZ0XSi6HI08OmTv/HKG6AltQoaKAALLQ+KDW92utj3XSgw7oren0KsWUKPhaPg3Av7R1jFQd08s+uafqIlLw==", + "optionalDependencies": { + "@interactjs/interact": "1.10.27" + }, + "peerDependencies": { + "@interactjs/utils": "1.10.27" + } + }, + "node_modules/@interactjs/utils": { + "version": "1.10.27", + "resolved": "https://registry.npmmirror.com/@interactjs/utils/-/utils-1.10.27.tgz", + "integrity": "sha512-+qfLOio2OxQqg1cXSnRaCl+N8MQDQLDS9w+aOGxH8YLAhIMyt7Asxx/46//sT8orgsi16pmlBPtngPHT9s8zKw==" + }, + "node_modules/@intlify/core-base": { + "version": "9.14.4", + "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.14.4.tgz", + "integrity": "sha512-vtZCt7NqWhKEtHa3SD/322DlgP5uR9MqWxnE0y8Q0tjDs9H5Lxhss+b5wv8rmuXRoHKLESNgw9d+EN9ybBbj9g==", + "dependencies": { + "@intlify/message-compiler": "9.14.4", + "@intlify/shared": "9.14.4" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "9.14.4", + "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.14.4.tgz", + "integrity": "sha512-vcyCLiVRN628U38c3PbahrhbbXrckrM9zpy0KZVlDk2Z0OnGwv8uQNNXP3twwGtfLsCf4gu3ci6FMIZnPaqZsw==", + "dependencies": { + "@intlify/shared": "9.14.4", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/shared": { + "version": "9.14.4", + "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.14.4.tgz", + "integrity": "sha512-P9zv6i1WvMc9qDBWvIgKkymjY2ptIiQ065PjDv7z7fDqH3J/HBRBN5IoiR46r/ujRcU7hCuSIZWvCAFCyuOYZA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.10", + "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.10.tgz", + "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true + }, + "node_modules/@node-ipc/js-queue": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/@node-ipc/js-queue/-/js-queue-2.0.3.tgz", + "integrity": "sha512-fL1wpr8hhD5gT2dA1qifeVaoDFlQR5es8tFuKqjHX+kdOtdNHnxkVZbtIrR2rxnMFvehkjaZRNV2H/gPXlb0hw==", + "dev": true, + "dependencies": { + "easy-stack": "1.0.1" + }, + "engines": { + "node": ">=1.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true + }, + "node_modules/@popperjs/core": { + "name": "@sxzz/popperjs-es", + "version": "2.11.7", + "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", + "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==", + "dev": true, + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmmirror.com/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dev": true, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true + }, + "node_modules/@soda/friendly-errors-webpack-plugin": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz", + "integrity": "sha512-h2ooWqP8XuFqTXT+NyAFbrArzfQA7R6HTezADrvD9Re8fxMLTPPniLdqVTdDaO0eIoLaAwKT+d6w+5GeTk7Vbg==", + "dev": true, + "dependencies": { + "chalk": "^3.0.0", + "error-stack-parser": "^2.0.6", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/@soda/get-current-script": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/@soda/get-current-script/-/get-current-script-1.0.2.tgz", + "integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==", + "dev": true + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmmirror.com/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmmirror.com/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.56.12", + "resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-8.56.12.tgz", + "integrity": "sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmmirror.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.23", + "resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.23.tgz", + "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.6", + "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true + }, + "node_modules/@types/http-proxy": { + "version": "1.17.16", + "resolved": "https://registry.npmmirror.com/@types/http-proxy/-/http-proxy-1.17.16.tgz", + "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/lodash": { + "version": "4.14.190", + "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.190.tgz", + "integrity": "sha512-5iJ3FBJBvQHQ8sFhEhJfjUP+G+LalhavTkYyrAYqz5MEJG+erSv0k9KJLb6q7++17Lafk1scaTIFXcMJlwK8Mw==" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "dev": true, + "peer": true, + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "node_modules/@types/node": { + "version": "24.0.10", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-24.0.10.tgz", + "integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==", + "dev": true, + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmmirror.com/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmmirror.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmmirror.com/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmmirror.com/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.8", + "resolved": "https://registry.npmmirror.com/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmmirror.com/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.16", + "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz", + "integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==", + "dev": true, + "peer": true + }, + "node_modules/@types/webpack-env": { + "version": "1.18.8", + "resolved": "https://registry.npmmirror.com/@types/webpack-env/-/webpack-env-1.18.8.tgz", + "integrity": "sha512-G9eAoJRMLjcvN4I08wB5I7YofOb/kaJNd5uoCMX+LbKXTPCF+ZIHuqTnFaK9Jz1rgs035f9JUPUhNFtqgucy/A==", + "dev": true + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.4.0.tgz", + "integrity": "sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "5.4.0", + "@typescript-eslint/scope-manager": "5.4.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.4.0.tgz", + "integrity": "sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/typescript-estree": "5.4.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-5.4.0.tgz", + "integrity": "sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.4.0", + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/typescript-estree": "5.4.0", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-5.4.0.tgz", + "integrity": "sha512-pRxFjYwoi8R+n+sibjgF9iUiAELU9ihPBtHzocyW8v8D8G8KeQvXTsW7+CBYIyTYsmhtNk50QPGLE3vrvhM5KA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/visitor-keys": "5.4.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-5.4.0.tgz", + "integrity": "sha512-GjXNpmn+n1LvnttarX+sPD6+S7giO+9LxDIGlRl4wK3a7qMWALOHYuVSZpPTfEIklYjaWuMtfKdeByx0AcaThA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.4.0.tgz", + "integrity": "sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.4.0", + "@typescript-eslint/visitor-keys": "5.4.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz", + "integrity": "sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.4.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vue/babel-helper-vue-jsx-merge-props": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz", + "integrity": "sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA==", + "dev": true + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.4.0.tgz", + "integrity": "sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==", + "dev": true + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.4.0.tgz", + "integrity": "sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.9", + "@babel/types": "^7.26.9", + "@vue/babel-helper-vue-transform-on": "1.4.0", + "@vue/babel-plugin-resolve-type": "1.4.0", + "@vue/shared": "^3.5.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.4.0.tgz", + "integrity": "sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/parser": "^7.26.9", + "@vue/compiler-sfc": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-plugin-transform-vue-jsx": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.4.0.tgz", + "integrity": "sha512-Fmastxw4MMx0vlgLS4XBX0XiBbUFzoMGeVXuMV08wyOfXdikAFqBTuYPR0tlk+XskL19EzHc39SgjrPGY23JnA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0", + "html-tags": "^2.0.0", + "lodash.kebabcase": "^4.1.1", + "svg-tags": "^1.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-preset-app": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/babel-preset-app/-/babel-preset-app-5.0.8.tgz", + "integrity": "sha512-yl+5qhpjd8e1G4cMXfORkkBlvtPCIgmRf3IYCWYDKIQ7m+PPa5iTm4feiNmCMD6yGqQWMhhK/7M3oWGL9boKwg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.16", + "@babel/helper-compilation-targets": "^7.12.16", + "@babel/helper-module-imports": "^7.12.13", + "@babel/plugin-proposal-class-properties": "^7.12.13", + "@babel/plugin-proposal-decorators": "^7.12.13", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/plugin-transform-runtime": "^7.12.15", + "@babel/preset-env": "^7.12.16", + "@babel/runtime": "^7.12.13", + "@vue/babel-plugin-jsx": "^1.0.3", + "@vue/babel-preset-jsx": "^1.1.2", + "babel-plugin-dynamic-import-node": "^2.3.3", + "core-js": "^3.8.3", + "core-js-compat": "^3.8.3", + "semver": "^7.3.4" + }, + "peerDependencies": { + "@babel/core": "*", + "core-js": "^3", + "vue": "^2 || ^3.2.13" + }, + "peerDependenciesMeta": { + "core-js": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/babel-preset-jsx": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-preset-jsx/-/babel-preset-jsx-1.4.0.tgz", + "integrity": "sha512-QmfRpssBOPZWL5xw7fOuHNifCQcNQC1PrOo/4fu6xlhlKJJKSA3HqX92Nvgyx8fqHZTUGMPHmFA+IDqwXlqkSA==", + "dev": true, + "dependencies": { + "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.4.0", + "@vue/babel-sugar-composition-api-inject-h": "^1.4.0", + "@vue/babel-sugar-composition-api-render-instance": "^1.4.0", + "@vue/babel-sugar-functional-vue": "^1.4.0", + "@vue/babel-sugar-inject-h": "^1.4.0", + "@vue/babel-sugar-v-model": "^1.4.0", + "@vue/babel-sugar-v-on": "^1.4.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0", + "vue": "*" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/babel-sugar-composition-api-inject-h": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-composition-api-inject-h/-/babel-sugar-composition-api-inject-h-1.4.0.tgz", + "integrity": "sha512-VQq6zEddJHctnG4w3TfmlVp5FzDavUSut/DwR0xVoe/mJKXyMcsIibL42wPntozITEoY90aBV0/1d2KjxHU52g==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-composition-api-render-instance": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-composition-api-render-instance/-/babel-sugar-composition-api-render-instance-1.4.0.tgz", + "integrity": "sha512-6ZDAzcxvy7VcnCjNdHJ59mwK02ZFuP5CnucloidqlZwVQv5CQLijc3lGpR7MD3TWFi78J7+a8J56YxbCtHgT9Q==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-functional-vue": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.4.0.tgz", + "integrity": "sha512-lTEB4WUFNzYt2In6JsoF9sAYVTo84wC4e+PoZWSgM6FUtqRJz7wMylaEhSRgG71YF+wfLD6cc9nqVeXN2rwBvw==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-inject-h": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.4.0.tgz", + "integrity": "sha512-muwWrPKli77uO2fFM7eA3G1lAGnERuSz2NgAxuOLzrsTlQl8W4G+wwbM4nB6iewlKbwKRae3nL03UaF5ffAPMA==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-v-model": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.4.0.tgz", + "integrity": "sha512-0t4HGgXb7WHYLBciZzN5s0Hzqan4Ue+p/3FdQdcaHAb7s5D9WZFGoSxEZHrR1TFVZlAPu1bejTKGeAzaaG3NCQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.4.0", + "camelcase": "^5.0.0", + "html-tags": "^2.0.0", + "svg-tags": "^1.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/babel-sugar-v-on": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.4.0.tgz", + "integrity": "sha512-m+zud4wKLzSKgQrWwhqRObWzmTuyzl6vOP7024lrpeJM4x2UhQtRDLgYjXAw9xBXjCwS0pP9kXjg91F9ZNo9JA==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.4.0", + "camelcase": "^5.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/cli-overlay": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-overlay/-/cli-overlay-5.0.8.tgz", + "integrity": "sha512-KmtievE/B4kcXp6SuM2gzsnSd8WebkQpg3XaB6GmFh1BJGRqa1UiW9up7L/Q67uOdTigHxr5Ar2lZms4RcDjwQ==", + "dev": true + }, + "node_modules/@vue/cli-plugin-babel": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-babel/-/cli-plugin-babel-5.0.8.tgz", + "integrity": "sha512-a4qqkml3FAJ3auqB2kN2EMPocb/iu0ykeELwed+9B1c1nQ1HKgslKMHMPavYx3Cd/QAx2mBD4hwKBqZXEI/CsQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.16", + "@vue/babel-preset-app": "^5.0.8", + "@vue/cli-shared-utils": "^5.0.8", + "babel-loader": "^8.2.2", + "thread-loader": "^3.0.0", + "webpack": "^5.54.0" + }, + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0" + } + }, + "node_modules/@vue/cli-plugin-eslint": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-5.0.8.tgz", + "integrity": "sha512-d11+I5ONYaAPW1KyZj9GlrV/E6HZePq5L5eAF5GgoVdu6sxr6bDgEoxzhcS1Pk2eh8rn1MxG/FyyR+eCBj/CNg==", + "dev": true, + "dependencies": { + "@vue/cli-shared-utils": "^5.0.8", + "eslint-webpack-plugin": "^3.1.0", + "globby": "^11.0.2", + "webpack": "^5.54.0", + "yorkie": "^2.0.0" + }, + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0", + "eslint": ">=7.5.0" + } + }, + "node_modules/@vue/cli-plugin-router": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-router/-/cli-plugin-router-5.0.8.tgz", + "integrity": "sha512-Gmv4dsGdAsWPqVijz3Ux2OS2HkMrWi1ENj2cYL75nUeL+Xj5HEstSqdtfZ0b1q9NCce+BFB6QnHfTBXc/fCvMg==", + "dev": true, + "dependencies": { + "@vue/cli-shared-utils": "^5.0.8" + }, + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0" + } + }, + "node_modules/@vue/cli-plugin-typescript": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-typescript/-/cli-plugin-typescript-5.0.8.tgz", + "integrity": "sha512-JKJOwzJshBqsmp4yLBexwVMebOZ4VGJgbnYvmHVxasJOStF2RxwyW28ZF+zIvASGdat4sAUuo/3mAQyVhm7JHg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.16", + "@types/webpack-env": "^1.15.2", + "@vue/cli-shared-utils": "^5.0.8", + "babel-loader": "^8.2.2", + "fork-ts-checker-webpack-plugin": "^6.4.0", + "globby": "^11.0.2", + "thread-loader": "^3.0.0", + "ts-loader": "^9.2.5", + "webpack": "^5.54.0" + }, + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0", + "cache-loader": "^4.1.0", + "typescript": ">=2", + "vue": "^2 || ^3.2.13", + "vue-template-compiler": "^2.0.0" + }, + "peerDependenciesMeta": { + "cache-loader": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/@vue/cli-plugin-vuex": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.8.tgz", + "integrity": "sha512-HSYWPqrunRE5ZZs8kVwiY6oWcn95qf/OQabwLfprhdpFWAGtLStShjsGED2aDpSSeGAskQETrtR/5h7VqgIlBA==", + "dev": true, + "peerDependencies": { + "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0" + } + }, + "node_modules/@vue/cli-service": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-service/-/cli-service-5.0.8.tgz", + "integrity": "sha512-nV7tYQLe7YsTtzFrfOMIHc5N2hp5lHG2rpYr0aNja9rNljdgcPZLyQRb2YRivTHqTv7lI962UXFURcpStHgyFw==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.12.16", + "@soda/friendly-errors-webpack-plugin": "^1.8.0", + "@soda/get-current-script": "^1.0.2", + "@types/minimist": "^1.2.0", + "@vue/cli-overlay": "^5.0.8", + "@vue/cli-plugin-router": "^5.0.8", + "@vue/cli-plugin-vuex": "^5.0.8", + "@vue/cli-shared-utils": "^5.0.8", + "@vue/component-compiler-utils": "^3.3.0", + "@vue/vue-loader-v15": "npm:vue-loader@^15.9.7", + "@vue/web-component-wrapper": "^1.3.0", + "acorn": "^8.0.5", + "acorn-walk": "^8.0.2", + "address": "^1.1.2", + "autoprefixer": "^10.2.4", + "browserslist": "^4.16.3", + "case-sensitive-paths-webpack-plugin": "^2.3.0", + "cli-highlight": "^2.1.10", + "clipboardy": "^2.3.0", + "cliui": "^7.0.4", + "copy-webpack-plugin": "^9.0.1", + "css-loader": "^6.5.0", + "css-minimizer-webpack-plugin": "^3.0.2", + "cssnano": "^5.0.0", + "debug": "^4.1.1", + "default-gateway": "^6.0.3", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "fs-extra": "^9.1.0", + "globby": "^11.0.2", + "hash-sum": "^2.0.0", + "html-webpack-plugin": "^5.1.0", + "is-file-esm": "^1.0.0", + "launch-editor-middleware": "^2.2.1", + "lodash.defaultsdeep": "^4.6.1", + "lodash.mapvalues": "^4.6.0", + "mini-css-extract-plugin": "^2.5.3", + "minimist": "^1.2.5", + "module-alias": "^2.2.2", + "portfinder": "^1.0.26", + "postcss": "^8.2.6", + "postcss-loader": "^6.1.1", + "progress-webpack-plugin": "^1.0.12", + "ssri": "^8.0.1", + "terser-webpack-plugin": "^5.1.1", + "thread-loader": "^3.0.0", + "vue-loader": "^17.0.0", + "vue-style-loader": "^4.1.3", + "webpack": "^5.54.0", + "webpack-bundle-analyzer": "^4.4.0", + "webpack-chain": "^6.5.1", + "webpack-dev-server": "^4.7.3", + "webpack-merge": "^5.7.3", + "webpack-virtual-modules": "^0.4.2", + "whatwg-fetch": "^3.6.2" + }, + "bin": { + "vue-cli-service": "bin/vue-cli-service.js" + }, + "engines": { + "node": "^12.0.0 || >= 14.0.0" + }, + "peerDependencies": { + "vue-template-compiler": "^2.0.0", + "webpack-sources": "*" + }, + "peerDependenciesMeta": { + "cache-loader": { + "optional": true + }, + "less-loader": { + "optional": true + }, + "pug-plain-loader": { + "optional": true + }, + "raw-loader": { + "optional": true + }, + "sass-loader": { + "optional": true + }, + "stylus-loader": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/@vue/cli-shared-utils": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/@vue/cli-shared-utils/-/cli-shared-utils-5.0.8.tgz", + "integrity": "sha512-uK2YB7bBVuQhjOJF+O52P9yFMXeJVj7ozqJkwYE9PlMHL1LMHjtCYm4cSdOebuPzyP+/9p0BimM/OqxsevIopQ==", + "dev": true, + "dependencies": { + "@achrinza/node-ipc": "^9.2.5", + "chalk": "^4.1.2", + "execa": "^1.0.0", + "joi": "^17.4.0", + "launch-editor": "^2.2.1", + "lru-cache": "^6.0.0", + "node-fetch": "^2.6.7", + "open": "^8.0.2", + "ora": "^5.3.0", + "read-pkg": "^5.1.1", + "semver": "^7.3.4", + "strip-ansi": "^6.0.0" + } + }, + "node_modules/@vue/cli-shared-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@vue/cli-shared-utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vue/cli-shared-utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.17", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.17.tgz", + "integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.27.5", + "@vue/shared": "3.5.17", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.17", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz", + "integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==", + "dev": true, + "dependencies": { + "@vue/compiler-core": "3.5.17", + "@vue/shared": "3.5.17" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.17", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz", + "integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.27.5", + "@vue/compiler-core": "3.5.17", + "@vue/compiler-dom": "3.5.17", + "@vue/compiler-ssr": "3.5.17", + "@vue/shared": "3.5.17", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.17", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.17", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz", + "integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==", + "dev": true, + "dependencies": { + "@vue/compiler-dom": "3.5.17", + "@vue/shared": "3.5.17" + } + }, + "node_modules/@vue/component-compiler-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", + "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==", + "dev": true, + "dependencies": { + "consolidate": "^0.15.1", + "hash-sum": "^1.0.2", + "lru-cache": "^4.1.2", + "merge-source-map": "^1.1.0", + "postcss": "^7.0.36", + "postcss-selector-parser": "^6.0.2", + "source-map": "~0.6.1", + "vue-template-es2015-compiler": "^1.9.0" + }, + "optionalDependencies": { + "prettier": "^1.18.2 || ^2.0.0" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + }, + "node_modules/@vue/component-compiler-utils/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/@vue/component-compiler-utils/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/@vue/component-compiler-utils/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==" + }, + "node_modules/@vue/eslint-config-typescript": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/@vue/eslint-config-typescript/-/eslint-config-typescript-9.1.0.tgz", + "integrity": "sha512-j/852/ZYQ5wDvCD3HE2q4uqJwJAceer2FwoEch1nFo+zTOsPrbzbE3cuWIs3kvu5hdFsGTMYwRwjI6fqZKDMxQ==", + "dev": true, + "dependencies": { + "vue-eslint-parser": "^8.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0", + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0", + "eslint-plugin-vue": "^8.0.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.13.tgz", + "integrity": "sha512-j3ByCiRgrr4uEZpXJM8XowrbYKeNHMHlbmMZE/2QpVzVPIfrQWS2fpLmbchJeMrnwIrzEl+dub3hgwkV4KRn4w==", + "dependencies": { + "@vue/shared": "3.2.13" + } + }, + "node_modules/@vue/reactivity/node_modules/@vue/shared": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.13.tgz", + "integrity": "sha512-F/gs3kHQ8Xeo24F6EImOvBiIoYQsBjF9qoLzvk+LHxYN6ZhIDEL1NWrBFYzdFQ7NphjEYd4EvPZ+Qee+WX8P6w==" + }, + "node_modules/@vue/ref-transform": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/ref-transform/-/ref-transform-3.2.13.tgz", + "integrity": "sha512-q6GXHZFzXjpx1K3UFRF8fa+xSmD9xV/FjhGzTNnfrryBr8tBUNYgP2f0s5K5N+21Ay7+MlQ1XXMUp8McGvsryQ==", + "dependencies": { + "@babel/parser": "^7.15.0", + "@vue/compiler-core": "3.2.13", + "@vue/shared": "3.2.13", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7" + } + }, + "node_modules/@vue/ref-transform/node_modules/@vue/compiler-core": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.13.tgz", + "integrity": "sha512-H8MUuKVCfAT6C0vth/+1LAriKnM+RTFo/5MoFycwRPwworTvkpWq/EuGoIXdLBblo8Y2/bNsOmIBEEoOtrb/bQ==", + "dependencies": { + "@babel/parser": "^7.15.0", + "@vue/shared": "3.2.13", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "node_modules/@vue/ref-transform/node_modules/@vue/shared": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.13.tgz", + "integrity": "sha512-F/gs3kHQ8Xeo24F6EImOvBiIoYQsBjF9qoLzvk+LHxYN6ZhIDEL1NWrBFYzdFQ7NphjEYd4EvPZ+Qee+WX8P6w==" + }, + "node_modules/@vue/ref-transform/node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.13.tgz", + "integrity": "sha512-VQedL9Wa7yWMPVDrIkxzLCm6cWCDBoXcXc+jrsOJkqpWhEeA7+zGOsDsHzhLH8aaJD6vdnUR5Cy0EKvoJDqEWQ==", + "dependencies": { + "@vue/reactivity": "3.2.13", + "@vue/shared": "3.2.13" + } + }, + "node_modules/@vue/runtime-core/node_modules/@vue/shared": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.13.tgz", + "integrity": "sha512-F/gs3kHQ8Xeo24F6EImOvBiIoYQsBjF9qoLzvk+LHxYN6ZhIDEL1NWrBFYzdFQ7NphjEYd4EvPZ+Qee+WX8P6w==" + }, + "node_modules/@vue/runtime-dom": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.13.tgz", + "integrity": "sha512-DVG+ItkrnCOEa9HSrmGBTLwv/gBVYCO8wkm/yv+d5ChoTnyIILxP0oCiZEPJsgWZfUSRPNi5rXozwo7F99MiwQ==", + "dependencies": { + "@vue/runtime-core": "3.2.13", + "@vue/shared": "3.2.13", + "csstype": "^2.6.8" + } + }, + "node_modules/@vue/runtime-dom/node_modules/@vue/shared": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.13.tgz", + "integrity": "sha512-F/gs3kHQ8Xeo24F6EImOvBiIoYQsBjF9qoLzvk+LHxYN6ZhIDEL1NWrBFYzdFQ7NphjEYd4EvPZ+Qee+WX8P6w==" + }, + "node_modules/@vue/runtime-dom/node_modules/csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + }, + "node_modules/@vue/server-renderer": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.13.tgz", + "integrity": "sha512-KI+JFV+vRb95+Jb6IwRRm4Vhvj8wrJTNs+OlATfqwwIRpBGAyxn/4knDJYzlnUf/mrKAkrbw751mHhi+pEwILQ==", + "dependencies": { + "@vue/compiler-ssr": "3.2.13", + "@vue/shared": "3.2.13" + }, + "peerDependencies": { + "vue": "3.2.13" + } + }, + "node_modules/@vue/server-renderer/node_modules/@vue/compiler-core": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.13.tgz", + "integrity": "sha512-H8MUuKVCfAT6C0vth/+1LAriKnM+RTFo/5MoFycwRPwworTvkpWq/EuGoIXdLBblo8Y2/bNsOmIBEEoOtrb/bQ==", + "dependencies": { + "@babel/parser": "^7.15.0", + "@vue/shared": "3.2.13", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "node_modules/@vue/server-renderer/node_modules/@vue/compiler-dom": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.13.tgz", + "integrity": "sha512-5+2dYgQyNzM97EEgbdAusUpLjulcKkvLM26jOGpd14+qwEcW/KCnns5DGjlZD/tsdEwToOoTDCm+mjx7cO/G1Q==", + "dependencies": { + "@vue/compiler-core": "3.2.13", + "@vue/shared": "3.2.13" + } + }, + "node_modules/@vue/server-renderer/node_modules/@vue/compiler-ssr": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.13.tgz", + "integrity": "sha512-ZbO6uDhUWTdKBRguYNEZXj2FU3nh1cudoHBiidbxj9q5J0tVT+j1PSVFAXPq6SquUBdJpa4HvGkQ5kQzv6upXg==", + "dependencies": { + "@vue/compiler-dom": "3.2.13", + "@vue/shared": "3.2.13" + } + }, + "node_modules/@vue/server-renderer/node_modules/@vue/shared": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.13.tgz", + "integrity": "sha512-F/gs3kHQ8Xeo24F6EImOvBiIoYQsBjF9qoLzvk+LHxYN6ZhIDEL1NWrBFYzdFQ7NphjEYd4EvPZ+Qee+WX8P6w==" + }, + "node_modules/@vue/shared": { + "version": "3.5.17", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.17.tgz", + "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==", + "dev": true + }, + "node_modules/@vue/vue-loader-v15": { + "name": "vue-loader", + "version": "15.11.1", + "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-15.11.1.tgz", + "integrity": "sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==", + "dev": true, + "dependencies": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "peerDependencies": { + "css-loader": "*", + "webpack": "^3.0.0 || ^4.1.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "cache-loader": { + "optional": true + }, + "prettier": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/@vue/vue-loader-v15/node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + }, + "node_modules/@vue/web-component-wrapper": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz", + "integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==", + "dev": true + }, + "node_modules/@vueuse/core": { + "version": "7.3.0", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-7.3.0.tgz", + "integrity": "sha512-gPJyMMAquva9Qwqz63qGQT122m5hWI8Kuy8kfPV/JLQU7m01CXooyv8FIrX9TV8OxVcHBTPXPJHY0oyUiFoNgw==", + "dependencies": { + "@vueuse/shared": "7.3.0", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.1.0", + "vue": "^2.6.0 || ^3.2.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vueuse/core/node_modules/@vueuse/shared": { + "version": "7.3.0", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-7.3.0.tgz", + "integrity": "sha512-vOAeI84tIXKVkzm8s/Mxbrzhj0QN6NyVc/sC6LrW0AjVNdvpD8sB1dZiDn9yh8T77WJmloCEt4zZVIppeq7I+w==", + "dependencies": { + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.1.0", + "vue": "^2.6.0 || ^3.2.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz", + "integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==", + "dev": true, + "peer": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz", + "integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==", + "dev": true, + "peer": true, + "dependencies": { + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "dev": true, + "hasInstallScript": true, + "peer": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xterm/addon-attach": { + "version": "0.10.0", + "resolved": "https://registry.npmmirror.com/@xterm/addon-attach/-/addon-attach-0.10.0.tgz", + "integrity": "sha512-ES/XO8pC1tPHSkh4j7qzM8ajFt++u8KMvfRc9vKIbjHTDOxjl9IUVo+vcQgLn3FTCM3w2czTvBss8nMWlD83Cg==", + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/addon-fit": { + "version": "0.9.0", + "resolved": "https://registry.npmmirror.com/@xterm/addon-fit/-/addon-fit-0.9.0.tgz", + "integrity": "sha512-hDlPPbTVPYyvwXu/asW8HbJkI/2RMi0cMaJnBZYVeJB0SWP2NeESMCNr+I7CvBlyI0sAxpxOg8Wk4OMkxBz9WA==", + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/xterm": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/@xterm/xterm/-/xterm-5.5.0.tgz", + "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", + "peer": true + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/animate.css": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/animate.css/-/animate.css-4.1.1.tgz", + "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==" + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmmirror.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmmirror.com/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", + "dev": true, + "peer": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmmirror.com/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/b-tween": { + "version": "0.3.3", + "resolved": "https://registry.npmmirror.com/b-tween/-/b-tween-0.3.3.tgz", + "integrity": "sha512-oEHegcRpA7fAuc9KC4nktucuZn2aS8htymCPcP3qkEGPqiBH+GfqtqoG2l7LxHngg6O0HFM7hOeOYExl1Oz4ZA==" + }, + "node_modules/b-validate": { + "version": "1.5.3", + "resolved": "https://registry.npmmirror.com/b-validate/-/b-validate-1.5.3.tgz", + "integrity": "sha512-iCvCkGFskbaYtfQ0a3GmcQCHl/Sv1GufXFGuUQ+FE+WJa7A/espLOuFIn09B944V8/ImPj71T4+rTASxO2PAuA==" + }, + "node_modules/babel-loader": { + "version": "8.4.1", + "resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-8.4.1.tgz", + "integrity": "sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.4", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.14", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.5", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmmirror.com/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/batch-processor": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/batch-processor/-/batch-processor-1.0.0.tgz", + "integrity": "sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==" + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/bonjour-service": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/bonjour-service/-/bonjour-service-1.3.0.tgz", + "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "devOptional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cache-base/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "dev": true, + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-callsite/node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "dev": true, + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001726", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", + "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmmirror.com/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/class-utils/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmmirror.com/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmmirror.com/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/clipboardy/-/clipboardy-2.3.0.tgz", + "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==", + "dev": true, + "dependencies": { + "arch": "^2.1.1", + "execa": "^1.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/codemirror": { + "version": "5.65.19", + "resolved": "https://registry.npmmirror.com/codemirror/-/codemirror-5.65.19.tgz", + "integrity": "sha512-+aFkvqhaAVr1gferNMuN8vkTSrWIFvzlMV9I2KBLCWS2WpZ2+UAkZjlMZmEuT+gcXTi6RrGQCkWq1/bDtGqhIA==" + }, + "node_modules/codemirror-editor-vue3": { + "version": "2.8.0", + "resolved": "https://registry.npmmirror.com/codemirror-editor-vue3/-/codemirror-editor-vue3-2.8.0.tgz", + "integrity": "sha512-ebYGNhBpLmQNLguXzNyMMkn6K8v3lcS5/Ncvdn6YS4bLGEHE67MfsJIS/WV0L7I6WavUuFlY/Rs/AJKChIwSwg==", + "dependencies": { + "codemirror": "^5", + "diff-match-patch": "^1.0.5" + }, + "peerDependencies": { + "codemirror": "^5", + "diff-match-patch": "^1.0.5", + "vue": "^3.x" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmmirror.com/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmmirror.com/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/compression/-/compression-1.8.0.tgz", + "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.0.2", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/compute-scroll-into-view": { + "version": "1.0.20", + "resolved": "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", + "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmmirror.com/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "deprecated": "Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog", + "dev": true, + "dependencies": { + "bluebird": "^3.1.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz", + "integrity": "sha512-rxnR7PaGigJzhqETHGmAcxKnLZSR5u1Y3/bcIv/1FnqXedcL/E2ewK7ZCNrArJKCiSv8yVXhTqetJh8inDvfsA==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^11.0.3", + "normalize-path": "^3.0.0", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/core-js": { + "version": "3.43.0", + "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.43.0.tgz", + "integrity": "sha512-N6wEbTTZSYOY2rYAn85CuvWWkCK6QweMn7/4Nr3w+gDBeBhk/x4EJeY6FPo4QzDoJZxVTv8U7CMvgWk6pOHHqA==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.43.0", + "resolved": "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.43.0.tgz", + "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", + "dev": true, + "dependencies": { + "browserslist": "^4.25.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.4.1", + "resolved": "https://registry.npmmirror.com/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", + "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-loader": { + "version": "6.11.0", + "resolved": "https://registry.npmmirror.com/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmmirror.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "dev": true, + "dependencies": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.15", + "resolved": "https://registry.npmmirror.com/cssnano/-/cssnano-5.1.15.tgz", + "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "dev": true, + "dependencies": { + "cssnano-preset-default": "^5.2.14", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.14", + "resolved": "https://registry.npmmirror.com/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", + "dev": true, + "dependencies": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.2", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dayjs": { + "version": "1.11.4", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.4.tgz", + "integrity": "sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g==" + }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/default-gateway/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/default-gateway/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-gateway/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-gateway/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmmirror.com/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domready": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/domready/-/domready-1.0.8.tgz", + "integrity": "sha512-uIzsOJUNk+AdGE9a6VDeessoMCzF8RrZvJCX/W8QtyfgdR6Uofn/MvRonih3OtCO79b2VDzDOymuiABrQ4z3XA==", + "dev": true + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmmirror.com/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/easy-stack": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/easy-stack/-/easy-stack-1.0.1.tgz", + "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/echarts": { + "version": "5.2.2", + "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.2.2.tgz", + "integrity": "sha512-yxuBfeIH5c+0FsoRP60w4De6omXhA06c7eUYBsC1ykB6Ys2yK5fSteIYWvkJ4xJVLQgCvAdO8C4mN6MLeJpBaw==", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.2.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.179", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.179.tgz", + "integrity": "sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==", + "dev": true + }, + "node_modules/element-plus": { + "version": "2.10.2", + "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.10.2.tgz", + "integrity": "sha512-p2KiAa0jEGXrzdlTAfpiS7HQFAhla4gvx6H7RuDf+OO0uC3DGpolxvdHjFR8gt7+vaWyxQNcHa1sAdBkmjqlgA==", + "dev": true, + "peer": true, + "dependencies": { + "@ctrl/tinycolor": "^3.4.1", + "@element-plus/icons-vue": "^2.3.1", + "@floating-ui/dom": "^1.0.1", + "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", + "@types/lodash": "^4.14.182", + "@types/lodash-es": "^4.17.6", + "@vueuse/core": "^9.1.0", + "async-validator": "^4.2.5", + "dayjs": "^1.11.13", + "escape-html": "^1.0.3", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "lodash-unified": "^1.0.2", + "memoize-one": "^6.0.0", + "normalize-wheel-es": "^1.2.0" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/element-plus/node_modules/@vueuse/core": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz", + "integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/web-bluetooth": "^0.0.16", + "@vueuse/metadata": "9.13.0", + "@vueuse/shared": "9.13.0", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/element-plus/node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "dev": true, + "hasInstallScript": true, + "peer": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/element-plus/node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true, + "peer": true + }, + "node_modules/element-resize-detector": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/element-resize-detector/-/element-resize-detector-1.2.4.tgz", + "integrity": "sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==", + "dependencies": { + "batch-processor": "1.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.2", + "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/enhanced-resolve/node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dev": true, + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-vue": { + "version": "8.0.3", + "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-8.0.3.tgz", + "integrity": "sha512-Rlhhy5ltzde0sRwSkqHuNePTXLMMaJ5+qsQubM4RYloYsQ8cXlnJT5MDaCzSirkGADipOHtmQXIbbPFAzUrADg==", + "dev": true, + "dependencies": { + "eslint-utils": "^3.0.0", + "natural-compare": "^1.4.0", + "semver": "^7.3.5", + "vue-eslint-parser": "^8.0.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "dev": true, + "dependencies": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/eslint/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-pubsub": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/event-pubsub/-/event-pubsub-4.3.0.tgz", + "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmmirror.com/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmmirror.com/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "devOptional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmmirror.com/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "6.5.3", + "resolved": "https://registry.npmmirror.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", + "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=10", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dev": true, + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-value/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-sum": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", + "dev": true + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/highlight.js": { + "version": "11.6.0", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.6.0.tgz", + "integrity": "sha512-ig1eqDzJaB0pqEvlPVIpSSyMaO92bH1N2rJpLMN/nX396wTpDA4Eq0uK+7I/2XG17pFaaKE0kjV/XPeGt7Evjw==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmmirror.com/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmmirror.com/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-tags": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-2.0.0.tgz", + "integrity": "sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.6.3", + "resolved": "https://registry.npmmirror.com/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", + "integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==", + "dev": true, + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/html-webpack-plugin/node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmmirror.com/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmmirror.com/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.9", + "resolved": "https://registry.npmmirror.com/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/husky/-/husky-1.3.1.tgz", + "integrity": "sha512-86U6sVVVf4b5NYSZ0yvv88dRgBSSXXmHaiq5pP4KDj5JVzdwKgBjEtUPOm8hcoytezFwbU+7gotXNhpHdystlg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "cosmiconfig": "^5.0.7", + "execa": "^1.0.0", + "find-up": "^3.0.0", + "get-stdin": "^6.0.0", + "is-ci": "^2.0.0", + "pkg-dir": "^3.0.0", + "please-upgrade-node": "^3.1.1", + "read-pkg": "^4.0.1", + "run-node": "^1.0.0", + "slash": "^2.0.0" + }, + "bin": { + "husky-upgrade": "lib/upgrader/bin.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/husky/node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/husky/node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dev": true, + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/husky/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/husky/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/husky/node_modules/read-pkg": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-4.0.1.tgz", + "integrity": "sha512-+UBirHHDm5J+3WDmLBZYSklRYg82nMlz+enn+GMZ22nSR2f4bzxmhso6rzQW/3mT2PVzpzDTiYIZahk8UmZ44w==", + "dev": true, + "dependencies": { + "normalize-package-data": "^2.3.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/husky/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/husky/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", + "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz", + "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-1.0.3.tgz", + "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-file-esm": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-file-esm/-/is-file-esm-1.0.0.tgz", + "integrity": "sha512-rZlaNKb4Mr8WlRu2A9XdeoKgnO5aA53XdPHgCKVyCrQ/rWi89RET1+bq37Ru46obaQXeiX4vmFIm1vks41hoSA==", + "dev": true, + "dependencies": { + "read-pkg-up": "^7.0.1" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "devOptional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "devOptional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/javascript-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz", + "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", + "dev": true + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmmirror.com/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dev": true, + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmmirror.com/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" + }, + "node_modules/jquery-mousewheel": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/jquery-mousewheel/-/jquery-mousewheel-3.2.2.tgz", + "integrity": "sha512-JP71xTAg08ZY3hcs9ZbYUZ5i+dkSsz4yRl/zpWkAmtzc+kMs5EfPkpkINSidiLYMaR0MTo3DfFGF9WIezMsFQQ==", + "dependencies": { + "jquery": ">=1.2.6" + } + }, + "node_modules/js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true + }, + "node_modules/js-message": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/js-message/-/js-message-1.0.7.tgz", + "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", + "dev": true, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsencrypt": { + "version": "3.0.0-rc.1", + "resolved": "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.0.0-rc.1.tgz", + "integrity": "sha512-gcvGaqerlUJy1Kq6tNgPYteVEoWNemu+9hBe2CdsCIz4rVcwjoTQ72iD1W76/PRMlnkzG0yVh7nwOOMOOUfKmg==" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/launch-editor": { + "version": "2.10.0", + "resolved": "https://registry.npmmirror.com/launch-editor/-/launch-editor-2.10.0.tgz", + "integrity": "sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "node_modules/launch-editor-middleware": { + "version": "2.10.0", + "resolved": "https://registry.npmmirror.com/launch-editor-middleware/-/launch-editor-middleware-2.10.0.tgz", + "integrity": "sha512-RzZu7MeVlE3p1H6Sadc2BhuDGAj7bkeDCBpNq/zSENP4ohJGhso00k5+iYaRwKshIpiOAhMmimce+5D389xmSg==", + "dev": true, + "dependencies": { + "launch-editor": "^2.10.0" + } + }, + "node_modules/less": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/less/-/less-4.0.0.tgz", + "integrity": "sha512-av1eEa2D0xZfF7fjLJS/Dld7zAYSLU7EOEJvuOELeaNI3i6L/81AdjbK5/pytaRkBwi7ZEa0433IDvMLskKCOw==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^1.10.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "native-request": "^1.0.5", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/less-loader/-/less-loader-8.0.0.tgz", + "integrity": "sha512-tnDs0ZdwPZgNOg0NGJ0sAD2KViG9TvOMDVibT33fH1bpLkT4xMo5Ue2FsbjFsVsUKtuRTlU0tYp2/lRizrycLg==", + "dev": true, + "dependencies": { + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/loader-utils/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash-unified": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz", + "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "@types/lodash-es": "*", + "lodash": "*", + "lodash-es": "*" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.defaultsdeep": { + "version": "4.6.1", + "resolved": "https://registry.npmmirror.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", + "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", + "dev": true + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true + }, + "node_modules/lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==", + "dev": true + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmmirror.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-update": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/log-update/-/log-update-2.3.0.tgz", + "integrity": "sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^3.0.0", + "cli-cursor": "^2.0.0", + "wrap-ansi": "^3.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-update/node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dev": true, + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-update/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-update/node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-update/node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz", + "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==", + "dev": true, + "dependencies": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmmirror.com/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "dev": true, + "peer": true + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-options": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/merge-options/-/merge-options-1.0.1.tgz", + "integrity": "sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==", + "dev": true, + "dependencies": { + "is-plain-obj": "^1.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "devOptional": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.9.2", + "resolved": "https://registry.npmmirror.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", + "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mitt": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.0.tgz", + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==" + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module-alias": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/module-alias/-/module-alias-2.2.3.tgz", + "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==", + "dev": true + }, + "node_modules/mousetrap": { + "version": "1.6.5", + "resolved": "https://registry.npmmirror.com/mousetrap/-/mousetrap-1.6.5.tgz", + "integrity": "sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmmirror.com/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmmirror.com/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/native-request": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/native-request/-/native-request-1.1.2.tgz", + "integrity": "sha512-/etjwrK0J4Ebbcnt35VMWnfiUX/B04uwGJxyJInagxDqf2z5drSt/lsOvEMWGYunz1kaLZAFrV4NDAbOoDKvAQ==", + "dev": true, + "optional": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "optional": true + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/normalize-wheel-es": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", + "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==", + "dev": true, + "peer": true + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/number-precision": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/number-precision/-/number-precision-1.6.0.tgz", + "integrity": "sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-visit/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmmirror.com/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmmirror.com/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "devOptional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pinia": { + "version": "2.0.17", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.0.17.tgz", + "integrity": "sha512-AtwLwEWQgIjofjgeFT+nxbnK5lT2QwQjaHNEDqpsi2AiCwf/NY78uWTeHUyEhiiJy8+sBmw0ujgQMoQbWiZDfA==", + "dependencies": { + "@vue/devtools-api": "^6.2.1", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@vue/composition-api": "^1.4.0", + "typescript": ">=4.4.4", + "vue": "^2.6.14 || ^3.2.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "dependencies": { + "semver-compare": "^1.0.0" + } + }, + "node_modules/portfinder": { + "version": "1.0.37", + "resolved": "https://registry.npmmirror.com/portfinder/-/portfinder-1.0.37.tgz", + "integrity": "sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==", + "dev": true, + "dependencies": { + "async": "^3.2.6", + "debug": "^4.3.6" + }, + "engines": { + "node": ">= 10.12" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmmirror.com/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.1", + "resolved": "https://registry.npmmirror.com/postcss-colormin/-/postcss-colormin-5.3.1.tgz", + "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmmirror.com/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-loader/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmmirror.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.4", + "resolved": "https://registry.npmmirror.com/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", + "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dev": true, + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmmirror.com/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dev": true, + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmmirror.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "dev": true, + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-prefix-selector": { + "version": "1.16.1", + "resolved": "https://registry.npmmirror.com/postcss-prefix-selector/-/postcss-prefix-selector-1.16.1.tgz", + "integrity": "sha512-Umxu+FvKMwlY6TyDzGFoSUnzW+NOfMBLyC1tAkIjgX+Z/qGspJeRjVC903D7mx7TuBpJlwti2ibXtWuA7fKMeQ==", + "dev": true, + "peerDependencies": { + "postcss": ">4 <9" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/posthtml": { + "version": "0.9.2", + "resolved": "https://registry.npmmirror.com/posthtml/-/posthtml-0.9.2.tgz", + "integrity": "sha512-spBB5sgC4cv2YcW03f/IAUN1pgDJWNWD8FzkyY4mArLUMJW+KlQhlmUdKAHQuPfb00Jl5xIfImeOsf6YL8QK7Q==", + "dev": true, + "dependencies": { + "posthtml-parser": "^0.2.0", + "posthtml-render": "^1.0.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/posthtml-parser": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/posthtml-parser/-/posthtml-parser-0.2.1.tgz", + "integrity": "sha512-nPC53YMqJnc/+1x4fRYFfm81KV2V+G9NZY+hTohpYg64Ay7NemWWcV4UWuy/SgMupqQ3kJ88M/iRfZmSnxT+pw==", + "dev": true, + "dependencies": { + "htmlparser2": "^3.8.3", + "isobject": "^2.1.0" + } + }, + "node_modules/posthtml-parser/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/posthtml-parser/node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/posthtml-parser/node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/posthtml-parser/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "node_modules/posthtml-parser/node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/posthtml-parser/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/posthtml-parser/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "node_modules/posthtml-parser/node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/posthtml-rename-id": { + "version": "1.0.12", + "resolved": "https://registry.npmmirror.com/posthtml-rename-id/-/posthtml-rename-id-1.0.12.tgz", + "integrity": "sha512-UKXf9OF/no8WZo9edRzvuMenb6AD5hDLzIepJW+a4oJT+T/Lx7vfMYWT4aWlGNQh0WMhnUx1ipN9OkZ9q+ddEw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "1.0.5" + } + }, + "node_modules/posthtml-rename-id/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/posthtml-render": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/posthtml-render/-/posthtml-render-1.4.0.tgz", + "integrity": "sha512-W1779iVHGfq0Fvh2PROhCe2QhB8mEErgqzo1wpIt36tCgChafP+hbXIhLDOM8ePJrZcFs0vkNEtdibEWVqChqw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/posthtml-svg-mode": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/posthtml-svg-mode/-/posthtml-svg-mode-1.0.3.tgz", + "integrity": "sha512-hEqw9NHZ9YgJ2/0G7CECOeuLQKZi8HjWLkBaSVtOWjygQ9ZD8P7tqeowYs7WrFdKsWEKG7o+IlsPY8jrr0CJpQ==", + "dev": true, + "dependencies": { + "merge-options": "1.0.1", + "posthtml": "^0.9.2", + "posthtml-parser": "^0.2.1", + "posthtml-render": "^1.0.6" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/progress-webpack-plugin": { + "version": "1.0.16", + "resolved": "https://registry.npmmirror.com/progress-webpack-plugin/-/progress-webpack-plugin-1.0.16.tgz", + "integrity": "sha512-sdiHuuKOzELcBANHfrupYo+r99iPRyOnw15qX+rNlVUqXGfjXdH4IgxriKwG1kNJwVswKQHMdj1hYZMcb9jFaA==", + "dev": true, + "dependencies": { + "chalk": "^2.1.0", + "figures": "^2.0.0", + "log-update": "^2.3.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/progress-webpack-plugin/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/progress-webpack-plugin/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/progress-webpack-plugin/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/progress-webpack-plugin/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/progress-webpack-plugin/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/query-string/-/query-string-7.1.1.tgz", + "integrity": "sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w==", + "dependencies": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmmirror.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regex-not/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmmirror.com/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "dev": true, + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmmirror.com/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dev": true, + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmmirror.com/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resize-detector": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/resize-detector/-/resize-detector-0.3.0.tgz", + "integrity": "sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ==" + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmmirror.com/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-node": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/run-node/-/run-node-1.0.0.tgz", + "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==", + "dev": true, + "bin": { + "run-node": "run-node" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sass": { + "version": "1.89.2", + "resolved": "https://registry.npmmirror.com/sass/-/sass-1.89.2.tgz", + "integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass-loader": { + "version": "14.2.1", + "resolved": "https://registry.npmmirror.com/sass-loader/-/sass-loader-14.2.1.tgz", + "integrity": "sha512-G0VcnMYU18a4N7VoNDegg2OuMjYtxnqzQWARVWCIVSZwJeiL9kg8QMsuIZOplsJgTzZLF6jGxI3AClj8I9nRdQ==", + "dev": true, + "dependencies": { + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/scroll-into-view-if-needed": { + "version": "2.2.31", + "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", + "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", + "dependencies": { + "compute-scroll-into-view": "^1.0.20" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmmirror.com/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmmirror.com/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmmirror.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "dev": true + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", + "dev": true + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmmirror.com/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "dev": true + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.7.tgz", + "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.1", + "is-data-descriptor": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-baker": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/svg-baker/-/svg-baker-1.7.0.tgz", + "integrity": "sha512-nibslMbkXOIkqKVrfcncwha45f97fGuAOn1G99YwnwTj8kF9YiM6XexPcUso97NxOm6GsP0SIvYVIosBis1xLg==", + "dev": true, + "dependencies": { + "bluebird": "^3.5.0", + "clone": "^2.1.1", + "he": "^1.1.1", + "image-size": "^0.5.1", + "loader-utils": "^1.1.0", + "merge-options": "1.0.1", + "micromatch": "3.1.0", + "postcss": "^5.2.17", + "postcss-prefix-selector": "^1.6.0", + "posthtml-rename-id": "^1.0", + "posthtml-svg-mode": "^1.0.3", + "query-string": "^4.3.2", + "traverse": "^0.6.6" + } + }, + "node_modules/svg-baker-runtime": { + "version": "1.4.7", + "resolved": "https://registry.npmmirror.com/svg-baker-runtime/-/svg-baker-runtime-1.4.7.tgz", + "integrity": "sha512-Zorfwwj5+lWjk/oxwSMsRdS2sPQQdTmmsvaSpzU+i9ZWi3zugHLt6VckWfnswphQP0LmOel3nggpF5nETbt6xw==", + "dev": true, + "dependencies": { + "deepmerge": "1.3.2", + "mitt": "1.1.2", + "svg-baker": "^1.7.0" + } + }, + "node_modules/svg-baker-runtime/node_modules/deepmerge": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-1.3.2.tgz", + "integrity": "sha512-qjMjTrk+RKv/sp4RPDpV5CnKhxjFI9p+GkLBOls5A8EEElldYWCWA9zceAkmfd0xIo2aU1nxiaLFoiya2sb6Cg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker-runtime/node_modules/mitt": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-1.1.2.tgz", + "integrity": "sha512-3btxP0O9iGADGWAkteQ8mzDtEspZqu4I32y4GZYCV5BrwtzdcRpF4dQgNdJadCrbBx7Lu6Sq9AVrerMHR0Hkmw==", + "dev": true + }, + "node_modules/svg-baker/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/svg-baker/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/svg-baker/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/micromatch": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-3.1.0.tgz", + "integrity": "sha512-3StSelAE+hnRvMs8IdVW7Uhk8CVed5tp+kLLGlBP6WiRAXS21GPGu/Nat4WNPXj2Eoc24B02SaeoyozPMfj0/g==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.2.2", + "define-property": "^1.0.0", + "extend-shallow": "^2.0.1", + "extglob": "^2.0.2", + "fragment-cache": "^0.2.1", + "kind-of": "^5.0.2", + "nanomatch": "^1.2.1", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/svg-baker/node_modules/query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmmirror.com/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-baker/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/svg-baker/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-sprite-loader": { + "version": "6.0.11", + "resolved": "https://registry.npmmirror.com/svg-sprite-loader/-/svg-sprite-loader-6.0.11.tgz", + "integrity": "sha512-TedsTf8wsHH6HgdwKjUveDZRC6q5gPloYV8A8/zZaRWP929J7x6TzQ6MvZFl+YYDJuJ0Akyuu/vNVJ+fbPuYXg==", + "dev": true, + "dependencies": { + "bluebird": "^3.5.0", + "deepmerge": "1.3.2", + "domready": "1.0.8", + "escape-string-regexp": "1.0.5", + "loader-utils": "^1.1.0", + "svg-baker": "^1.5.0", + "svg-baker-runtime": "^1.4.7", + "url-slug": "2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/svg-sprite-loader/node_modules/deepmerge": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-1.3.2.tgz", + "integrity": "sha512-qjMjTrk+RKv/sp4RPDpV5CnKhxjFI9p+GkLBOls5A8EEElldYWCWA9zceAkmfd0xIo2aU1nxiaLFoiya2sb6Cg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svg-sprite-loader/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmmirror.com/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmmirror.com/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.43.1", + "resolved": "https://registry.npmmirror.com/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.14.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmmirror.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/thread-loader": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/thread-loader/-/thread-loader-3.0.4.tgz", + "integrity": "sha512-ByaL2TPb+m6yArpqQUZvP+5S1mZtXsEP7nWKKlAUTm7fCml8kB5s1uI3+eHRP2bk5mVYfRSBI7FFf+tWEyLZwA==", + "dev": true, + "dependencies": { + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.1.0", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.27.0 || ^5.0.0" + } + }, + "node_modules/thread-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/thread-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "devOptional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/to-regex/node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/traverse": { + "version": "0.6.11", + "resolved": "https://registry.npmmirror.com/traverse/-/traverse-0.6.11.tgz", + "integrity": "sha512-vxXDZg8/+p3gblxB6BhhG5yWVn1kGRlaL8O78UDXc3wRnPizB5g83dcvWV1jpDMIPnjZjOFuxlMmE82XJ4407w==", + "dev": true, + "dependencies": { + "gopd": "^1.2.0", + "typedarray.prototype.slice": "^1.0.5", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ts-loader": { + "version": "9.5.2", + "resolved": "https://registry.npmmirror.com/ts-loader/-/ts-loader-9.5.2.tgz", + "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray.prototype.slice": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.5.tgz", + "integrity": "sha512-q7QNVDGTdl702bVFiI5eY4l/HkgCM6at9KhcFbgUAzezHFbOVy4+0O/lCjsABEQwbZPravVfBIiBVGo89yzHFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "math-intrinsics": "^1.1.0", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-offset": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unidecode": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/unidecode/-/unidecode-0.1.8.tgz", + "integrity": "sha512-SdoZNxCWpN2tXTCrGkPF/0rL2HEq+i2gwRG1ReBvx8/0yTzC3enHfugOf8A9JBShVwwrRIkLX0YcDUGbzjbVCA==", + "dev": true, + "engines": { + "node": ">= 0.4.12" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/url-slug": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/url-slug/-/url-slug-2.0.0.tgz", + "integrity": "sha512-aiNmSsVgrjCiJ2+KWPferjT46YFKoE8i0YX04BlMVDue022Xwhg/zYlnZ6V9/mP3p8Wj7LEp0myiTkC/p6sxew==", + "dev": true, + "dependencies": { + "unidecode": "0.1.8" + } + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "dev": true + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmmirror.com/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vue": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.2.13.tgz", + "integrity": "sha512-raTGvLXXTdMxrhQKY1r1YFXZMmjbjTe7QHBW9EU4CgCBhq8DbgyLqgILcSUZmeFyazk5WY7a7xu0VYmHElf4lA==", + "dependencies": { + "@vue/compiler-dom": "3.2.13", + "@vue/compiler-sfc": "3.2.13", + "@vue/runtime-dom": "3.2.13", + "@vue/server-renderer": "3.2.13", + "@vue/shared": "3.2.13" + } + }, + "node_modules/vue-echarts": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/vue-echarts/-/vue-echarts-6.2.3.tgz", + "integrity": "sha512-xHzUvgsgk/asJTcNa8iVVwoovZU3iEUHvmBa3bzbiP3Y6OMxM1YXsoWOKVmVVaUusGs4ob4pSwjwNy2FemAz9w==", + "hasInstallScript": true, + "dependencies": { + "resize-detector": "^0.3.0", + "vue-demi": "^0.13.2" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.5", + "echarts": "^5.1.2", + "vue": "^2.6.12 || ^3.1.1" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-echarts/node_modules/vue-demi": { + "version": "0.13.11", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz", + "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-eslint-parser": { + "version": "8.3.0", + "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz", + "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==", + "dev": true, + "dependencies": { + "debug": "^4.3.2", + "eslint-scope": "^7.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/vue-grid-layout": { + "version": "3.0.0-beta1", + "resolved": "https://registry.npmmirror.com/vue-grid-layout/-/vue-grid-layout-3.0.0-beta1.tgz", + "integrity": "sha512-MsW0yfYNtnAO/uDhfZvkP6effxSJxvhAFbIL37x6Rn3vW9xf0WHVefKaSbQMLpSq3mXnR6ut0pg2Cd5lqIIZzg==", + "dependencies": { + "@interactjs/actions": "^1.10.2", + "@interactjs/auto-start": "^1.10.2", + "@interactjs/dev-tools": "^1.10.2", + "@interactjs/interactjs": "^1.10.2", + "@interactjs/modifiers": "^1.10.2", + "element-resize-detector": "^1.2.1", + "mitt": "^2.1.0" + } + }, + "node_modules/vue-grid-layout/node_modules/mitt": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-2.1.0.tgz", + "integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==" + }, + "node_modules/vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmmirror.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", + "dev": true + }, + "node_modules/vue-i18n": { + "version": "9.14.4", + "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.14.4.tgz", + "integrity": "sha512-B934C8yUyWLT0EMud3DySrwSUJI7ZNiWYsEEz2gknTthqKiG4dzWE/WSa8AzCuSQzwBEv4HtG1jZDhgzPfWSKQ==", + "dependencies": { + "@intlify/core-base": "9.14.4", + "@intlify/shared": "9.14.4", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-loader": { + "version": "17.4.2", + "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-17.4.2.tgz", + "integrity": "sha512-yTKOA4R/VN4jqjw4y5HrynFL8AK0Z3/Jt7eOJXEitsm0GMRHDBjCfCiuTiLP7OESvsZYo2pATCWhDqxC5ZrM6w==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "watchpack": "^2.4.0" + }, + "peerDependencies": { + "webpack": "^4.1.0 || ^5.0.0-0" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/vue-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/vue-router": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.0.3.tgz", + "integrity": "sha512-AD1OjtVPyQHTSpoRsEGfPpxRQwhAhxcacOYO3zJ3KNkYP/r09mileSp6kdMQKhZWP2cFsPR3E2M3PZguSN5/ww==", + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-style-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmmirror.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz", + "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", + "dev": true, + "dependencies": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "node_modules/vue-style-loader/node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + }, + "node_modules/vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true + }, + "node_modules/vue-winbox": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/vue-winbox/-/vue-winbox-0.1.0.tgz", + "integrity": "sha512-JaUnxzBAgyX1tIvjjWkWx0t8psTZG5mAzxz4hsxCvN59grGd1d+f9694Z11wkLI/aRem/9yLQ+PL1viAW+JKVw==", + "dependencies": { + "nanoid": "^3.1.23", + "vue-demi": "^0.11.2", + "winbox": "^0.2.0" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-beta.1", + "vue": "^2.0.0 || >=3.0.0-rc.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-winbox/node_modules/vue-demi": { + "version": "0.11.4", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.11.4.tgz", + "integrity": "sha512-/3xFwzSykLW2HiiLie43a+FFgNOcokbBJ+fzvFXd0r2T8MYohqvphUyDQ8lbAwzQ3Dlcrb1c9ykifGkhSIAk6A==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue/node_modules/@vue/compiler-core": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.13.tgz", + "integrity": "sha512-H8MUuKVCfAT6C0vth/+1LAriKnM+RTFo/5MoFycwRPwworTvkpWq/EuGoIXdLBblo8Y2/bNsOmIBEEoOtrb/bQ==", + "dependencies": { + "@babel/parser": "^7.15.0", + "@vue/shared": "3.2.13", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "node_modules/vue/node_modules/@vue/compiler-dom": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.13.tgz", + "integrity": "sha512-5+2dYgQyNzM97EEgbdAusUpLjulcKkvLM26jOGpd14+qwEcW/KCnns5DGjlZD/tsdEwToOoTDCm+mjx7cO/G1Q==", + "dependencies": { + "@vue/compiler-core": "3.2.13", + "@vue/shared": "3.2.13" + } + }, + "node_modules/vue/node_modules/@vue/compiler-sfc": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.13.tgz", + "integrity": "sha512-3j970d969aOILykcTstdihP33xH1Onm0wsvcl+rGv9AGxivB9xicRxBw93HCIA4dAPivr42WjHEoci9q2/85uw==", + "dependencies": { + "@babel/parser": "^7.15.0", + "@vue/compiler-core": "3.2.13", + "@vue/compiler-dom": "3.2.13", + "@vue/compiler-ssr": "3.2.13", + "@vue/ref-transform": "3.2.13", + "@vue/shared": "3.2.13", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7", + "postcss": "^8.1.10", + "source-map": "^0.6.1" + } + }, + "node_modules/vue/node_modules/@vue/compiler-ssr": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.13.tgz", + "integrity": "sha512-ZbO6uDhUWTdKBRguYNEZXj2FU3nh1cudoHBiidbxj9q5J0tVT+j1PSVFAXPq6SquUBdJpa4HvGkQ5kQzv6upXg==", + "dependencies": { + "@vue/compiler-dom": "3.2.13", + "@vue/shared": "3.2.13" + } + }, + "node_modules/vue/node_modules/@vue/shared": { + "version": "3.2.13", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.13.tgz", + "integrity": "sha512-F/gs3kHQ8Xeo24F6EImOvBiIoYQsBjF9qoLzvk+LHxYN6ZhIDEL1NWrBFYzdFQ7NphjEYd4EvPZ+Qee+WX8P6w==" + }, + "node_modules/vue/node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmmirror.com/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/webpack": { + "version": "5.99.9", + "resolved": "https://registry.npmmirror.com/webpack/-/webpack-5.99.9.tgz", + "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-bundle-analyzer": { + "version": "4.10.2", + "resolved": "https://registry.npmmirror.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", + "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "commander": "^7.2.0", + "debounce": "^1.2.1", + "escape-string-regexp": "^4.0.0", + "gzip-size": "^6.0.0", + "html-escaper": "^2.0.2", + "opener": "^1.5.2", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-chain": { + "version": "6.5.1", + "resolved": "https://registry.npmmirror.com/webpack-chain/-/webpack-chain-6.5.1.tgz", + "integrity": "sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "dependencies": { + "deepmerge": "^1.5.2", + "javascript-stringify": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-chain/node_modules/deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.4", + "resolved": "https://registry.npmmirror.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.15.2", + "resolved": "https://registry.npmmirror.com/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", + "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.4", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmmirror.com/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmmirror.com/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.4.6", + "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.4.6.tgz", + "integrity": "sha512-5tyDlKLqPfMqjT3Q9TAqf2YqjwmnUleZwzJi1A5qXnlBCdj2AtOJ6wAWdglTIDOPgOiOrXeBeFcsQ8+aGQ6QbA==", + "dev": true + }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack/node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmmirror.com/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmmirror.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true + }, + "node_modules/winbox": { + "version": "0.2.82", + "resolved": "https://registry.npmmirror.com/winbox/-/winbox-0.2.82.tgz", + "integrity": "sha512-fMHaLnnuhyw1mLvzmCTWMpRKDfXUK/Z08VdbyBqDaITFFAd3Fx2BGDCF6rzGSkWrQgo8fHTFpFICofyM36FTnA==" + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmmirror.com/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xterm": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/xterm/-/xterm-5.3.0.tgz", + "integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==", + "deprecated": "This package is now deprecated. Move to @xterm/xterm instead." + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yorkie": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/yorkie/-/yorkie-2.0.0.tgz", + "integrity": "sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "execa": "^0.8.0", + "is-ci": "^1.0.10", + "normalize-path": "^1.0.0", + "strip-indent": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yorkie/node_modules/ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, + "node_modules/yorkie/node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "dev": true, + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/yorkie/node_modules/execa": { + "version": "0.8.0", + "resolved": "https://registry.npmmirror.com/execa/-/execa-0.8.0.tgz", + "integrity": "sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==", + "dev": true, + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/yorkie/node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/yorkie/node_modules/is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "dependencies": { + "ci-info": "^1.5.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/yorkie/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/yorkie/node_modules/normalize-path": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-1.0.0.tgz", + "integrity": "sha512-7WyT0w8jhpDStXRq5836AMmihQwq2nrUVQrgjvUo/p/NZf9uy/MeJ246lBJVmWuYXMlJuG9BNZHF0hWjfTbQUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yorkie/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yorkie/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yorkie/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/yorkie/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true + }, + "node_modules/zrender": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.2.1.tgz", + "integrity": "sha512-M3bPGZuyLTNBC6LiNKXJwSCtglMp8XUEqEBG+2MdICDI3d1s500Y4P0CzldQGsqpRVB7fkvf3BKQQRxsEaTlsw==", + "dependencies": { + "tslib": "2.3.0" + } + } + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/package.json b/ER-Model/plugins/base-ops/web-ui/package.json new file mode 100644 index 000000000..c70bb6893 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/package.json @@ -0,0 +1,68 @@ +{ + "name": "web-ui", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "vue-cli-service serve", + "build:prod": "vue-cli-service build", + "build:stage": "vue-cli-service build --mode staging", + "lint": "vue-cli-service lint" + }, + "dependencies": { + "@computing/opendesign-icons": "file:lib\\opendesign-icons-1.0.3.tgz", + "@computing/opendesign2": "file:lib\\opendesign2-2.0.22.tgz", + "@types/lodash": "4.14.190", + "@antv/x6": "1.32.10", + "@antv/x6-vue-shape": "1.4.2", + "@arco-design/web-vue": "2.54.6", + "@highlightjs/vue-plugin": "2.1.0", + "@vueuse/core": "7.3.0", + "@xterm/addon-attach": "^0.10.0-beta.1", + "@xterm/addon-fit": "^0.9.0-beta.1", + "animate.css": "4.1.1", + "axios": "0.27.2", + "codemirror": "^5.6.0", + "codemirror-editor-vue3": "^2.3.0", + "core-js": "^3.8.3", + "dayjs": "1.11.4", + "echarts": "5.2.2", + "highlight.js": "11.6.0", + "jsencrypt": "3.0.0-rc.1", + "mitt": "3.0.0", + "pinia": "2.0.17", + "query-string": "7.1.1", + "sass": "^1.77.6", + "vue": "3.2.13", + "vue-echarts": "6.2.3", + "vue-grid-layout": "^3.0.0-beta1", + "vue-i18n": "^9.3.0-beta.6", + "vue-router": "4.0.3", + "vue-winbox": "^0.1.0", + "xterm": "^5.3.0" + }, + "devDependencies": { + "@computing/opendesign-icons": "file:lib\\opendesign-icons-1.0.3.tgz", + "@computing/opendesign2": "file:lib\\opendesign2-2.0.22.tgz", + "@typescript-eslint/eslint-plugin": "5.4.0", + "@typescript-eslint/parser": "5.4.0", + "@vue/cli-plugin-babel": "~5.0.0", + "@vue/cli-plugin-eslint": "~5.0.0", + "@vue/cli-plugin-router": "~5.0.0", + "@vue/cli-plugin-typescript": "~5.0.0", + "@vue/cli-service": "~5.0.0", + "@vue/eslint-config-typescript": "9.1.0", + "eslint": "7.32.0", + "eslint-plugin-vue": "8.0.3", + "husky": "1.3.1", + "less": "4.0.0", + "less-loader": "8.0.0", + "sass-loader": "^14.2.1", + "svg-sprite-loader": "6.0.11", + "typescript": "4.9" + }, + "husky": { + "hooks": { + "pre-commit": "" + } + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/public/favicon.ico b/ER-Model/plugins/base-ops/web-ui/public/favicon.ico new file mode 100644 index 000000000..df36fcfb7 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/public/favicon.ico differ diff --git a/ER-Model/plugins/base-ops/web-ui/public/index.html b/ER-Model/plugins/base-ops/web-ui/public/index.html new file mode 100644 index 000000000..931fb6956 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/public/index.html @@ -0,0 +1,17 @@ + + + + + + + + base-ops + + + +
+ + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/App.vue b/ER-Model/plugins/base-ops/web-ui/src/App.vue new file mode 100644 index 000000000..430a5c095 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/App.vue @@ -0,0 +1,79 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/api/dashboard.ts b/ER-Model/plugins/base-ops/web-ui/src/api/dashboard.ts new file mode 100644 index 000000000..9aff885f7 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/api/dashboard.ts @@ -0,0 +1,5 @@ +import axios from 'axios' + +export function instanceRoute () { + return axios.get('/getIndexInstanceRouters') +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/api/interceptor.ts b/ER-Model/plugins/base-ops/web-ui/src/api/interceptor.ts new file mode 100644 index 000000000..34825fbdc --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/api/interceptor.ts @@ -0,0 +1,105 @@ +import axios from 'axios' +import type { AxiosRequestConfig, AxiosResponse } from 'axios' +import { Message, Modal, Notification } from '@arco-design/web-vue' +import { getToken } from '@/utils/auth' +import errorCode from '@/utils/errorCode' + +// need to update url path +const whiteNameList = ['host/listAll', 'host/ping', 'hostUser/listAll/', 'hostUser/listAllWithoutRoot/', + 'az/listAll', 'encryption/getKey', 'opsCluster/check'] + +export interface HttpResponse { + status: number; + msg: string; + code: number; + data: T; +} + +// axios.defaults.baseURL = 'http://47.95.39.132:9494/' +axios.defaults.baseURL = process.env.VUE_APP_BASE_API || '' +axios.defaults.timeout = 1200000 + +let isTimeout = false + +axios.interceptors.request.use( + (config: AxiosRequestConfig) => { + // let each request carry token + // this example using the JWT token + // Authorization is a custom headers key + // please modify it according to the actual situation + const token = getToken() + if (token) { + if (!config.headers) { + config.headers = {} + } + config.headers.Authorization = `Bearer ${token}` + } + + // update url for request base project + whiteNameList.forEach(item => { + if (config.url?.includes(item)) { + if (process.env.NODE_ENV === 'development') { + config.baseURL = '/base_url' + } else { + config.baseURL = '/' + } + } + }) + return config + }, + (error) => { + // do something + return Promise.reject(error) + } +) +// add response interceptors +axios.interceptors.response.use( + (response: AxiosResponse) => { + const res = response + // If the status code is not set, the default status is success + const code = res.data.code || 200 + // get error message + const msg = (errorCode as any)[code] || res.data.msg || errorCode['default'] + + if (code === 401) { + if (!isTimeout) { + isTimeout = true + } + // @ts-ignore + window.$wujie?.bus.$emit('opengauss-login-expired') + return Promise.reject('Invalid session, or session expired, please log in again.') + } else if (code === 500) { + isTimeout = true + Message.error({ + content: msg, + duration: 5 * 1000 + }) + return Promise.reject(new Error(msg)) + } else if (code !== 200) { + isTimeout = true + Notification.error({ + title: 'Error', + content: msg + }) + return Promise.reject('error') + } else { + isTimeout = true + return res.data + } + }, + (error) => { + let { message } = error + if (message == 'Network Error') { + message = 'Network Error' + } else if (message.includes('timeout')) { + message = 'Request timed out' + } else if (message.includes('Request failed with status code')) { + message = 'System interface ' + message.substr(message.length - 3) + 'error' + } + Message.error({ + content: message || 'Request failed', + duration: 3 * 1000 + }) + return Promise.reject(error) + } +) diff --git a/ER-Model/plugins/base-ops/web-ui/src/api/modeling/index-dev2.ts b/ER-Model/plugins/base-ops/web-ui/src/api/modeling/index-dev2.ts new file mode 100644 index 000000000..3ff972e74 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/api/modeling/index-dev2.ts @@ -0,0 +1,149 @@ +import request, { KeyValue } from './request' +import { Message } from '@arco-design/web-vue' +// import axios from 'axios' + +// const baseUrl = `http://47.95.39.132:7777/` + +export const selectDealDataFlowTest = (data?: any) => request({ + url: 'select/dealDataFlowTest', method: 'post', data +}) + +export const getTableList = (params: string, data?: any) => request({ + url: 'modeling/dataSourceDb/getTablesByScheme/' + params, method: 'get', data +}) + +// get fields by table +export const getTableFields = (params: string, data?: any) => request({ + url: 'modeling/dataSourceDb/getFieldsByTable/' + params, method: 'get', data +}) + +export const getList = (data?: any) => request({ + url: 'modeling/dataFlow/list', method: 'get', + params: data ? data : {} +}) + +export const dataFlowAdd = (data?: KeyValue) => request({ + url: 'modeling/dataFlow/add', method: 'post', + data: data ? data : {} +}) + +export const dataFlowDelete = (id: string) => request({ + url: `modeling/dataFlow/delete/${id}`, method: 'delete' +}) + +export const dataFlowEdit = (data?: KeyValue) => request({ + url: `modeling/dataFlow/edit`, method: 'put', + data: data ? data : {} +}) + +export const getSQL = (data?: KeyValue) => request({ + url: `modeling/getSql`, method: 'post', + data: data ? data : {} +}) + +export const runSql = (data?: KeyValue) => request({ + url: `modeling/runSql`, method: 'post', + data: data ? data : {} +}) + +let canSave = true +export const saveJsonData = (id: string, jsonData: KeyValue) => { + if (!canSave) return + canSave = false + request({ + url: `modeling/dataFlow/edit`, method: 'put', + data: { id, operatorContent: JSON.stringify(jsonData) } + }).then((res: KeyValue) => { + canSave = true + if (Number(res.code) === 200) { + Message.success({ content: 'save success!' }) + } + }) +} + +export const getJsonData = (id: string) => request({ + url: 'modeling/dataFlow/list', method: 'get', + params: { id } +}) + +export const dataSourceDbAdd = (data?: KeyValue) => request({ + url: `modeling/dataSourceDb/add`, method: 'post', + data: data ? data : {} +}) + +export const dataSourceDbList = (data?: KeyValue) => request({ + url: `modeling/dataSourceDb/list`, method: 'get', + params: data ? data : {} +}) + +export const dataSourceDbDelete = (id: string) => request({ + url: `modeling/dataFlow/delete${id}`, method: 'delete' +}) + +export const dataSourceDbEdit = (data?: KeyValue) => request({ + url: `modeling/dataSourceDb/edit`, method: 'put', + data: data ? data : {} +}) + +/** visual */ +// Get the selection options of the visual editing area +export const modelingGetResultFieldsByOperator = (data?: KeyValue) => request({ + url: `modeling/getResultFieldsByOperator`, method: 'post', + data: data ? data : {} +}) +// New configuration +export const modelingVPAdd = (data?: KeyValue) => request({ + url: `modeling/visualization/params/add`, method: 'post', + data: data ? data : {} +}) +// configuration modify +export const modelingVPEdit = (data?: KeyValue) => request({ + url: `modeling/visualization/params/edit`, method: 'put', + data: data ? data : {} +}) +// get echarts config +export const modelingVPGenerateChart = (data?: KeyValue) => request({ + url: `modeling/visualization/params/generateChart`, method: 'post', + data: data ? data : {} +}) +// Obtain the visual chart configuration list of the operator +export const modelingVPGetListByOperatorId = (id: number | string, data?: KeyValue) => request({ + url: `modeling/visualization/params/getListByOperatorId/${id}`, method: 'get', + data: data ? data : {} +}) + +/** snapshot */ +// add +export const modelingVSAdd = (data?: KeyValue) => request({ + url: `modeling/visualization/snapshot/add`, method: 'post', + data: data ? data : {} +}) +// list +export const modelingVSGetListByDataFlowId = (id: number | string, data?: KeyValue) => request({ + url: `modeling/visualization/snapshot/getListByDataFlowId/${id}`, method: 'get', + data: data ? data : {} +}) +// delete +export const modelingVSDelete = (id: string) => request({ + url: `modeling/visualization/snapshot/delete/${id}`, method: 'delete' +}) +/** report */ +// list +export const modelingVRGetListByDataFlowId = (id: number | string, data?: KeyValue) => request({ + url: `modeling/visualization/report/getListByDataFlowId/${id}`, method: 'get', + data: data ? data : {} +}) +// add +export const modelingVRAdd = (data?: KeyValue) => request({ + url: `modeling/visualization/report/add`, method: 'post', + data: data ? data : {} +}) +// modify +export const modelingVRUpdate = (data?: KeyValue) => request({ + url: `modeling/visualization/report/update`, method: 'put', + data: data ? data : {} +}) +// delete +export const modelingVRDelete = (id: string) => request({ + url: `modeling/visualization/report/delete/${id}`, method: 'delete' +}) diff --git a/ER-Model/plugins/base-ops/web-ui/src/api/modeling/index.ts b/ER-Model/plugins/base-ops/web-ui/src/api/modeling/index.ts new file mode 100644 index 000000000..f1469fe60 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/api/modeling/index.ts @@ -0,0 +1,81 @@ +import { Message } from '@arco-design/web-vue' +import axios from 'axios' +import { KeyValue } from '@antv/x6/lib/types' +import i18n from '@/locale/index' + +export const selectDealDataFlowTest = (data?: any) => axios.post(`/select/dealDataFlowTest`, data ? data : {}) + +export const getTableList = (dbName: string, clusterNodeId: string, schema: string, data?: any) => axios.get(`/modeling/dataflow/dataSourceDb/getTablesBySchema/${dbName}/${clusterNodeId}/${schema}`, data ? data : {}) + +export const getTableFields = (params: string, data?: any) => axios.get(`/modeling/dataflow/dataSourceDb/getFieldsByTable/${params}`, data ? data : {}) + +export const getList = (data?: any) => axios.get(`/modeling/dataflow/list`, { + params: data +}) +export const dataFlowAdd = (data?: KeyValue) => axios.post(`/modeling/dataflow/add`, data ? data : {}) +export const dataFlowDelete = (id: string) => axios.delete(`/modeling/dataflow/delete/${id}`) +export const dataFlowEdit = (data?: KeyValue) => axios.put(`/modeling/dataflow/edit`, data ? data : {}) +export const dataFlowGetById = (dataFlowId: any) => axios.get(`/modeling/dataflow/getById/${dataFlowId}`) + +export const getSchemeByClusterNodeId = (data: any, data2:any) => axios.get(`/modeling/dataflow/dataSourceDb/getSchemaByClusterNodeId/${data}/${data2}`, data ? data : {}) +export const getSQL = (data?: KeyValue) => axios.post(`/modeling/dataflow/getSql`, data ? data : {}) +export const runSql = (data?: KeyValue) => axios.post(`/modeling/dataflow/runSql`, data ? data : {}) + +let canSave = true +export const saveJsonData = (id: string, jsonData: KeyValue, notShowMessage?: boolean) => new Promise(resolve => { + if (!canSave) return + canSave = false + axios.put(`/modeling/dataflow/edit`, { id, operatorContent: JSON.stringify(jsonData) }).then((res: KeyValue) => { + resolve(true) + canSave = true + if (Number(res.code) === 200) { + if (!notShowMessage) Message.success({ content: i18n.global.t('modeling.dy_common.saveSuccess') }) + } + }).catch(() => { + resolve(false) + if (!notShowMessage) Message.error({ content: i18n.global.t('modeling.dy_common.saveFailed') }) + canSave = true + }) +}) +// get full json +export const getJsonData = (id: string) => axios.get(`/modeling/dataflow/list`, { params: { id }}) + +export const dataSourceDbAdd = (data?: KeyValue) => axios.post(`/modeling/dataflow/dataSourceDb/add`, data ? data : {}) + +export const dataSourceDbList = (data?: KeyValue) => axios.get(`/modeling/dataflow/dataSourceDb/list`, data ? data : {}) + +export const dataSourceDbDelete = (id: string) => axios.delete(`/modeling/dataflow/delete${id}`) + +export const dataSourceDbEdit = (data?: KeyValue) => axios.put(`/modeling/dataflow/dataSourceDb/edit`, data ? data : {}) + +export const modelingGetResultFieldsByOperator = (data?: KeyValue) => axios.post(`/modeling/dataflow/getResultFieldsByOperator`, data ? data : {}) +export const modelingVPAdd = (data?: KeyValue) => axios.post(`/modeling/dataflow/visualization/params/add`, data ? data : {}) +export const modelingVPEdit = (data?: KeyValue) => axios.put(`/modeling/dataflow/visualization/params/edit`, data ? data : {}) +export const modelingVPDelete = (id: string) => axios.delete(`/modeling/dataflow/visualization/params/delete/${id}`) +export const modelingVPGenerateChart = (data?: KeyValue) => axios.post(`/modeling/dataflow/visualization/params/generateChart`, data ? data : {}) +export const modelingVPGetListByOperatorId = (id: number | string, data?: KeyValue) => + axios.get(`/modeling/dataflow/visualization/params/getListByOperatorId/${id}`, data ? data : {}) + +export const modelingVSAdd = (data?: KeyValue) => axios.post(`/modeling/dataflow/visualization/snapshot/add`, data ? data : {}) +export const modelingVSGetListByDataFlowId = (id: number | string, data?: KeyValue) => + axios.get(`/modeling/dataflow/visualization/snapshot/getListByDataFlowId/${id}`, data ? data : {}) +export const modelingVSDelete = (id: string) => axios.delete(`/modeling/dataflow/visualization/snapshot/delete/${id}`) + +export const modelingVRGetListByDataFlowId = (id: number | string, params?: KeyValue) => + axios.get(`/modeling/dataflow/visualization/report/getListByDataFlowId/${id}`, { params: params ? params : {}}) +export const modelingVRAdd = (data?: KeyValue) => axios.post(`/modeling/dataflow/visualization/report/add`, data ? data : {}) +export const modelingVRUpdate = (data?: KeyValue) => axios.put(`/modeling/dataflow/visualization/report/update`, data ? data : {}) +export const modelingVRDelete = (id: string) => axios.delete(`/modeling/dataflow/visualization/report/delete/${id}`) +export const modelingVRGetByReportId = (id: string) => axios.get(`/modeling/dataflow/visualization/report/share/getByReportId/${id}`) + +export const modelingVCGetListByOperatorId = (id: number | string, params?: KeyValue) => +axios.get(`/modeling/dataflow/visualization/customDimension/getListByOperatorId/${id}`, { params: params ? params : {}}) +export const modelingVCAdd = (data?: KeyValue) => axios.post(`/modeling/dataflow/visualization/customDimension/add`, data ? data : {}) +export const modelingVCEdit = (data?: KeyValue) => axios.put(`/modeling/dataflow/visualization/customDimension/edit`, data ? data : {}) +export const modelingVCDelete = (id: string) => axios.delete(`/modeling/dataflow/visualization/customDimension/delete/${id}`) + +export const getMapGeo = (name: string, data?: any) => axios.get(`/modeling/visualization/report/share/getMapGeo/${name}`, data ? data : {}) +export const uploadGeo = (data?: any) => axios.post(`/modeling/dataflow/visualization/params/uploadGeo`, data ? data : {}) +export const modelingDVGetGeo = (id: number | string, params?: KeyValue) => +axios.get(`/modeling/dataflow/visualization/params/getGeo/${id}`, { params: params ? params : {}}) +export const modelingDVdeleteGeo = (id: string) => axios.delete(`/modeling/dataflow/visualization/params/deleteGeo/${id}`) diff --git a/ER-Model/plugins/base-ops/web-ui/src/api/modeling/request.ts b/ER-Model/plugins/base-ops/web-ui/src/api/modeling/request.ts new file mode 100644 index 000000000..d3f85aab1 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/api/modeling/request.ts @@ -0,0 +1,82 @@ +import { Message } from '@arco-design/web-vue' +import Axios, { AxiosRequestConfig } from 'axios' +import router from '@/router' + +export interface KeyValue { + [key: string]: any +} + +export interface resType { + code: number | string, + data: any, + msg: string, + [key: string]: any +} + +const baseURL = `http://localhost:9494/` + +const axios = Axios.create({ + baseURL, + timeout: 10000 // timeout 10s +}) + +const whiteList = ['user/login'] +let timeoutNoNotice = false + +axios.interceptors.request.use( + (config: AxiosRequestConfig) => { + // if include by white list + if (config.url && !whiteList.includes(config.url.split(baseURL)[0])) { + if (!config.data) config.data = {} + } + if (config.data && config.data.timeout) config.timeout = config.data.timeout + if (config.data && config.data.timeoutNoNotice) timeoutNoNotice = true + return config + }, + (error) => { + Promise.reject(error) + } +) + +axios.interceptors.response.use( + (response: KeyValue) => { + if (whiteList.includes(response.config.url.split(baseURL)[1])) { + return response.data + } + const res = response.data + const statusCode = parseInt(response.data.code, 10) + const requestData = typeof response.config.data === 'object' ? {} : JSON.parse(response.config.data) + const notNeedMessage = requestData.notNeedMessage ? requestData.notNeedMessage : false + // if code is not 200 + if (statusCode !== 200) { + if (response.request.responseURL.includes('excel/importPatent')) { + return Promise.reject(res) + } else if (!notNeedMessage) Message.error({ content: res.msg || res.message || 'Error', duration: 5 * 1000 }) + else { + return Promise.reject(res) + } + } + timeoutNoNotice = false + return res as resType + }, + error => { + if (error === 'no_login') { + Message.error({ content: 'please login!', duration: 5 * 1000 }) + router.push(`/login`) + } else if (timeoutNoNotice) { + timeoutNoNotice = false + if (error.message.includes('timeout')) { + return Promise.reject(error.message) + } else { + Message.error({ content: error ? (error.message ? error.message : error.msg) : 'request error', duration: 5 * 1000 }) + return Promise.reject(error) + } + } else { + Message.error({ content: error ? (error.message ? error.message : error.msg) : 'request error', duration: 5 * 1000 }) + return Promise.reject(error) + } + } +) + +export default axios + diff --git a/ER-Model/plugins/base-ops/web-ui/src/api/ops/index.ts b/ER-Model/plugins/base-ops/web-ui/src/api/ops/index.ts new file mode 100755 index 000000000..8d8d2aa93 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/api/ops/index.ts @@ -0,0 +1,594 @@ +import axios from 'axios' +import { KeyValue } from '@/types/global' +import { downloadPackage, OpenLookengInstallConfig, SSHBody } from '@/types/ops/install' +import { UploadInfo } from '@/types/resource/package' +// import { hostData, hostUserData } from '@/types/resource/host' + +// get Key +export const getEntryKey = () => { + return axios.get('encryption/getKey') +} + +export const download = (data: downloadPackage) => { + return axios.post(`opsCluster/download`, data ? data : {}) +} + +export const getEnvMonitorData = (hostId: string, data: KeyValue) => { + return axios.get(`opsCluster/env/${hostId}`, { + params: data + }) +} + +export const installOpenGauss = (data: KeyValue) => { + return axios.post('opsCluster/install', data) +} + +export const importOpenGauss = (data: KeyValue) => { + return axios.post('opsCluster/import', data) +} + +export const quickInstall = (data: KeyValue) => { + return axios.post('opsCluster/quickInstall', data) +} + +// upgrade + +export const upgradeOsCheck = (data: KeyValue) => { + return axios.get('opsCluster/upgradeOsCheck', { + params: data + }) +} + +export const upgrade = (data: KeyValue) => { + return axios.post('opsCluster/upgrade', data) +} + +export const upgradeCommit = (data: KeyValue) => { + return axios.post('opsCluster/upgradeCommit', data) +} + +export const upgradeRollback = (data: KeyValue) => { + return axios.post('opsCluster/upgradeRollback', data) +} + +// one check +export const clusterCheck = (data: KeyValue) => { + return axios.get('opsCluster/check', { + params: data + }) +} + +export const uninstallOpenGauss = (data: KeyValue) => { + return axios.post('opsCluster/uninstall', data) +} + +export const hasName = (data: KeyValue) => { + return axios.get('opsCluster/hasName', { + params: data + }) +} + +export const clusterList = () => { + return axios.get('opsCluster/listCluster') +} + +export const getSummary = () => { + return axios.get('opsCluster/summary') +} + +export const openSSH = (data: SSHBody) => { + return axios.post('opsCluster/ssh', data) +} + +export const clusterMonitor = (data: KeyValue) => { + return axios.get('opsCluster/monitor', { + params: data + }) +} + +// just del platform data +export const delCluster = (clusterId: string) => { + return axios.delete(`opsCluster/remove/${clusterId}`) +} + +export const start = (data: KeyValue) => { + return axios.post('opsCluster/start', data) +} + +export const stop = (data: KeyValue) => { + return axios.post('opsCluster/stop', data) +} + +export const restart = (data: KeyValue) => { + return axios.post('opsCluster/restart', data) +} + +export const build = (data: KeyValue) => { + return axios.get('opsCluster/build', { + params: data + }) +} + +export const switchover = (data: KeyValue) => { + return axios.get('opsCluster/switchover', { + params: data + }) +} + +export const generateconf = (data: KeyValue) => { + return axios.get('opsCluster/generateconf', { + params: data + }) +} + +export const getHostByClusterId = (data: KeyValue) => { + return axios.get('opsCluster/listHost', { + params: data + }) +} + +// host interface +export const listInstallPackage = (data: any) => { + return axios.get('opsCluster/listInstallPackage', { + params: data + }) +} + +// page +export const hostPage = (query: any) => { + return axios.get('host/page', { + params: query + }) +} + +export const hostListAll = () => { + return axios.get('host/listAll') +} + +export const addHost = (data: KeyValue) => { + return axios.post('host', data) +} + +export const editHost = (hostId: string, data: KeyValue) => { + return axios.put(`host/${hostId}`, data) +} + +export const hostPing = (data: KeyValue) => { + return axios.post('host/ping', data) +} + +export const hostPingById = (hostId: string, data: KeyValue) => { + return axios.get(`host/ping/${hostId}`, { + params: data + }) +} + +export const delHost = (hostId: string) => { + return axios.delete(`host/${hostId}`) +} + +// Whether the directory is empty +export const pathEmpty = (hostId: string, data: KeyValue) => { + return axios.get(`host/pathEmpty/${hostId}`, { + params: data + }) +} + +// Whether the port is used +export const portUsed = (hostId: string, data: KeyValue) => { + return axios.get(`host/portUsed/${hostId}`, { + params: data + }) +} + +// Whether the file is exist +export const fileExist = (hostId: string, data: KeyValue) => { + return axios.get(`host/fileExist/${hostId}`, { + params: data + }) +} + +export const multiPathQuery = (hostId: string, data: KeyValue) => { + return axios.get(`host/multiPathQuery/${hostId}`, { + params: data + }) +} + +export const hostUserPage = (hostId: string) => { + return axios.get(`hostUser/page/${hostId}`) +} + +export const hostUserListAll = (hostId: string) => { + return axios.get(`hostUser/listAll/${hostId}`) +} + +export const hostUserListWithoutRoot = (hostId: string) => { + return axios.get(`hostUser/listAllWithoutRoot/${hostId}`) +} + +export const addHostUser = (data: KeyValue) => { + return axios.post('hostUser', data) +} + +export const editHostUser = (hostUserId: string, data: KeyValue) => { + return axios.put(`hostUser/${hostUserId}`, data) +} + +export const delHostUser = (hostUserId: string) => { + return axios.delete(`hostUser/${hostUserId}`) +} + +export const azListAll = () => { + return axios.get('az/listAll') +} + +export const addAz = (data: KeyValue) => { + return axios.post('az', data) +} + +export const editAz = (azId: string, data: KeyValue) => { + return axios.put(`az/${azId}`, data) +} + +// page +export const azPage = (query: any) => { + return axios.get('az/page', { + params: query + }) +} + +export const delAz = (azId: string) => { + return axios.delete(`az/${azId}`) +} + +export const logPath = (data: KeyValue) => { + return axios.get('opsCluster/logPath', { + params: data + }) +} + +export const getLs = (data: KeyValue) => { + return axios.get('opsCluster/ls', { + params: data + }) +} + +export const slowSql = (data: KeyValue) => { + return axios.get('opsCluster/slowSql', { + params: data + }) +} + +export const auditLog = (data: KeyValue) => { + return axios.get('opsCluster/auditLog', { + params: data + }) +} + +export const downloadFile = (data: KeyValue) => { + return axios.get('opsCluster/download', { + params: data + }) +} + +// WDR +export const wdrList = (data: KeyValue) => { + return axios.get('wdr/list', { + params: data + }) +} +export const wdrGenerate = (data: KeyValue) => { + return axios.post('wdr/generate', data) +} + +export const wdrDelete = (id: string) => { + return axios.delete('wdr/del/' + id) +} + +export const createSnapshot = (data: KeyValue) => { + return axios.get('wdr/createSnapshot', { + params: data + }) +} + +export const downloadWdrFile = (data: KeyValue) => { + return axios.get('wdr/downloadWdr', { + params: data + }) +} + +export const listSnapshot = (data: KeyValue) => { + return axios.get('wdr/listSnapshot', { + params: data + }) +} + +// backup +export const clusterBackup = (data: KeyValue) => { + return axios.post('backup/backup', data) +} +// backup page +export const backupPage = (data: KeyValue) => { + return axios.get('backup/page', { + params: data + }) +} +// backup recover +export const clusterRecover = (id: string, data: KeyValue) => { + return axios.post('backup/recover/' + id, data) +} +// backup delete +export const backupDel = (id: string) => { + return axios.delete('backup/del/' + id) +} + +// package manage +export const packageListAll = (data: KeyValue) => { + return axios.get('installPackageManager/list', { + params: data + }) +} + +export const packagePage = (data: KeyValue) => { + return axios.get('installPackageManager/page', { + params: data + }) +} + +export const packageDetailById = (id: string) => { + return axios.get('installPackageManager/detail/' + id) +} + +export const packageDel = (id: string) => { + return axios.delete('installPackageManager/' + id) +} + +export const addPackage = (data: FormData) => { + return axios.post('installPackageManager', data) +} + +export const editPackage = (data: FormData) => { + return axios.put(`installPackageManager`, data) +} + +export const getPackageCpuArch = (data: KeyValue) => { + return axios.get('installPackageManager/getCpuArch', { + params: data + }) +} + +export const analysisPkg = (pkgName: string, pkgType: string) => { + return axios.get('installPackageManager/analysisPkg', { + params: { pkgName: pkgName, pkgType: pkgType } + }) +} + +export const delPkgTar = (path: string, id?: string) => { + return axios.delete('installPackageManager/delPkgTar', { params: { path: path, id: id } }) +} + +export const getSysUploadPath = () => { + return axios.get('installPackageManager/sysUploadPath') +} + +export const hasPkgName = (name: string) => { + return axios.get('/installPackageManager/hasName', { params: { name: name } }) +} + +export const generateRuleYaml = (data: KeyValue) => { + return axios.post('olk/generateRuleYaml', data) +} + +export const installOlk = (data: KeyValue) => { + return axios.post('olk/install', data) +} + +export const removeOlk = (id: string) => { + return axios.delete('olk/remove/' + id) +} + +export const startOlk = (id: string, bid: string) => { + return axios.get('olk/start/' + id, { + params: { + bid: bid + } + }) +} + +export const stopOlk = (id: string, bid: string) => { + return axios.get('olk/stop/' + id, { + params: { + bid: bid + } + }) +} + +export const destroyOlk = (id: string, bid: string) => { + return axios.delete('olk/destroy/' + id, { + params: { + bid: bid + } + }) +} + +export const pageOlk = (query: any) => { + return axios.get('olk/page', { + params: query + }) +} + +export const listGucSetting = (query: KeyValue) => { + return axios.get('/opsCluster/listGucSetting', { + params: query + }) +} + +export const batchSetGucSetting = (data: KeyValue) => { + return axios.post('/opsCluster/batchConfigGucSetting', data) +} + +export const listEnterpriseCluster = () => { + return axios.get('/opsCluster/listEnterpriseCluster') +} + +export const listSupportOsName = () => { + return axios.get('/opsCluster/listSupportOsName') +} + +export const delPackage = (packageId: string) => { + return axios.delete(`/installPackageManager/${packageId}`) +} + +export const delPackageV2 = (data: KeyValue) => { + return axios.post(`/installPackageManager/v2/delete/package`, data) +} + +export const checkPackage = (data: KeyValue) => { + return axios.post(`/installPackageManager/v2/check/package`, data) +} + +export const checkPkg = (packageId: String) => { + return axios.post(`/installPackageManager/v2/check/pkg?packageId=${packageId}`) +} + +export const getVersionNum = () => { + return axios.get(`/installPackageManager/v2/list/version/number`) +} + +export const getPackageList = (data: KeyValue) => { + return axios.post(`/installPackageManager/v2/list/package`, data) +} + +export const getPackagePage = (data: KeyValue) => { + return axios.post(`/installPackageManager/v2/page/package`, data) +} + +export const batchPackageOnline = (data: FormData) => { + return axios.post(`/installPackageManager/v2/save/online`, data) +} + +export const batchPackageUpload = (name:KeyValue, os: KeyValue, cpuArch: KeyValue, packageVersion: KeyValue, packageVersionNum: KeyValue, packageUrl: KeyValue, file:any) => { + return axios.post(`/installPackageManager/v2/save/upload?name=${name}&os=${os}&cpuArch=${cpuArch}&packageVersion=${packageVersion}&packageVersionNum=${packageVersionNum}&packageUrl=${packageUrl}`, file) +} + +export const packageOnlineUpdate = (packageId:string, wsBusinessId:string) => { + return axios.post(`/installPackageManager/v2/update/online?packageId=${packageId}&wsBusinessId=${wsBusinessId}`) +} + +export const packageUploadUpdate = (params: KeyValue, data: KeyValue) => { + return axios.post(`/installPackageManager/v2/update/upload${params}`, data) +} + +export const checkNetStatus = () => { + return axios.get(`/installPackageManager/v2/check/online`) +} + +export const checkVersionNumber = (params: KeyValue) => { + return axios.get(`/installPackageManager/v2/check/version/number`, {params:params}) +} + +export const getHostIp = (params: KeyValue) => { + return axios.get(`/clusterTask/host/list`, {params}) +} + +export const getHostUser = (hostId: string) => { + return axios.get(`/clusterTask/hostUser/${hostId}`) +} + +export const getHostInfo = (hostIp: string) => { + return axios.get(`/clusterTask/host/${hostIp}`) +} + +export const createClusterTask = (data: KeyValue) => { + return axios.post(`/clusterTask/create`, data) +} + +export const checkDiskSpace = (data: KeyValue, hostId: string) => { + return axios.post(`/clusterTask/check/host/disk/space/${hostId}`, data) +} + +export const checkPortExist = (hostId: string, hostPort: number, clusterId: string) => { + return axios.get(`/clusterTask/hostPort/${hostId}/${hostPort}?taskId=${clusterId}`) +} + +export const checkClusternameExist = (clusterName: string, clusterId: string) => { + return axios.get(`/clusterTask/check/name?clusterName=${clusterName}&clusterId=${clusterId}`) +} + +export const checkClusterExist = (hostIp: string, hostUsername: number) => { + return axios.get(`/clusterTask/check/host?hostIp=${hostIp}&hostUsername=${hostUsername}`) +} + +export const batchClusterNodes = (clusterId: string) => { + return axios.get(`/clusterTask/detail/${clusterId}`) +} + +export const updateClusterTask = (data: KeyValue) => { + return axios.post(`/clusterTask/update`, data) +} + +export const clusterEnvCheck = (clusterId: string) => { + return axios.post(`/clusterTask/check/environment?taskId=${clusterId}`) +} + +export const envCheckResult = (clusterId: string) => { + return axios.get(`/clusterTask/get/environment?taskId=${clusterId}`) +} + +export const fetchAZList = () => { + return axios.get(`/clusterTask/list/az`) +} + +export const createClustertaskNode = (data: KeyValue) => { + return axios.post(`/cluster/task/node/create`, data) +} + +export const updateClustertaskNode = (data: KeyValue) => { + return axios.post(`/cluster/task/node/update`, data) +} + +export const deleteClustertaskNode = (clusterId: string, clusterNodeId: string, data: KeyValue) => { + return axios.post(`/cluster/task/node/delete?clusterId=${clusterId}&nodeId=${clusterNodeId}`, data) +} + +export const checkCluster = (clusterId: string) => { + return axios.post(`/clusterTask/check?clusterId=${clusterId}`) +} + +export const clusterTaskPage = (mutiSearchData: KeyValue, data: KeyValue) => { + return axios.post("/clusterTask/page", mutiSearchData, { + params: data + }) +} + +export const copyTask = (clusterId: KeyValue) => { + return axios.post(`/clusterTask/copy?taskId=${clusterId}`) +} + +export const batchDeleteTask = (clusterIds: any) => { + return axios.post(`/clusterTask/delete`, clusterIds) +} + +export const clusterLogDownload = (clusterId: string) => { + return axios.get(`/opsClusterLog/download/log?clusterId=${clusterId}`,{responseType: 'blob',headers: {'Content-Type':'application/json;application/octet-stream'}}) +} + +export const submitCluster = (clusterId: string) => { + return axios.post(`/clusterTask/confirm?taskId=${clusterId}`) +} + +export const executeTask = (clusterIds: any) => { + return axios.post(`/clusterTask/batch/install`, clusterIds) +} + +export const checkTaskStatus = (clusterIds: any) => { + return axios.post(`/clusterTask/batch/install/status`, clusterIds) +} + +export const reExecuteTask = (clusterId: number) => { + return axios.post(`/clusterTask/re/install?taskId=${clusterId}`) +} + +export const hostSSHByHostId = (hostId: string, data: KeyValue) => { + return axios.post('host/ssh/' + hostId, data) +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/api/resource/diskArray.ts b/ER-Model/plugins/base-ops/web-ui/src/api/resource/diskArray.ts new file mode 100644 index 000000000..2f8573ecc --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/api/resource/diskArray.ts @@ -0,0 +1,25 @@ +import axios from "axios"; + +export const apiGetDiskArrayList = () => { + return axios.get("/deviceManager/list"); +}; + +export const apiAddDiskArray = (data: any) => { + return axios.post("/deviceManager/add", data); +}; + +export const apiModifyDiskArray = (data: any) => { + return axios.post("/deviceManager/modify", data); +}; + +export const apiDeleteDiskArray = (id: string) => { + return axios.post(`/deviceManager/delete/${id}`); +}; + +export const apiCheckConnectivity = (data: any) => { + return axios.post("/deviceManager/connect", data); +}; + +export const apiCheckName = (data: any) => { + return axios.get(`/deviceManager/hasName?name=${data}`); +}; diff --git a/ER-Model/plugins/base-ops/web-ui/src/api/resource/dtCluster.ts b/ER-Model/plugins/base-ops/web-ui/src/api/resource/dtCluster.ts new file mode 100644 index 000000000..75d338297 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/api/resource/dtCluster.ts @@ -0,0 +1,35 @@ +import axios from "axios"; + +export const apiGetClusterAndDeviceManagerList = () => { + return axios.get("/disasterCluster/listClusterAndDeviceManager"); +}; + +export const apiInstallDtCluster = (data: any) => { + return axios.post("/disasterCluster/install", data); +}; + +export const apiGetDtCluster = () => { + return axios.get("/disasterCluster/listCluster"); +}; + +export const apiSwitchover = (data: any) => { + return axios.post("/disasterCluster/switchover", data); +}; + +export const apiRleaseDtCluster = (data: any) => { + return axios.post("/disasterCluster/delete", data); +}; + +export const apiMonitorDtCluster = (clusterId: string, businessId: string) => { + return axios.get(`/disasterCluster/monitor?clusterId=${clusterId}&businessId=${businessId}`); +}; + +export const apiGetHosts = (primaryClusterName: string, standbyClusterName: string) => { + return axios.get( + `/disasterCluster/getHosts?primaryClusterName=${primaryClusterName}&standbyClusterName=${standbyClusterName}` + ); +}; + +export const openSSH = (data: any) => { + return axios.post(`opsCluster/ssh`, data); +}; diff --git a/ER-Model/plugins/base-ops/web-ui/src/api/user.ts b/ER-Model/plugins/base-ops/web-ui/src/api/user.ts new file mode 100644 index 000000000..2d1d43764 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/api/user.ts @@ -0,0 +1,61 @@ +import axios from 'axios' + +export interface LoginData { + username: string; + password: string; +} + +export interface LoginRes { + token: string; +} + +export function logout () { + return axios.post('/logout') +} + +export function getUserInfo () { + return axios.get('/system/user/profile') +} + +export function updateUserInfo (payload: any) { + return axios.put('/system/user/profile', payload) +} + +export function getMenuList () { + const locale = localStorage.getItem('locale') || 'zh-CN' + return axios.get('/getRouters', { + headers: { + language: locale + } + }) +} + +export function getUserList (query: any) { + return axios.get('/system/user/list', { + params: query + }) +} + +export function createUser (payload: any) { + return axios.post('/system/user', payload) +} + +export function userDelete (userId: string) { + return axios.delete(`/system/user/${userId}`) +} + +export function changeStatus (payload: any) { + return axios.put('/system/user/changeStatus', payload) +} + +export function updateUser (payload: any) { + return axios.put('/system/user', payload) +} + +export function resetCode (payload: any) { + return axios.put('/system/user/resetPwd', payload) +} + +export function userRoleList (userId?: string) { + return axios.get(`/system/user/${userId}`) +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/index.ts b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/index.ts new file mode 100644 index 000000000..5261e71fc --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/index.ts @@ -0,0 +1,3 @@ +const svg = require.context('./svg', false, /\.svg$/) +const requireAll = (requireContext: any) => requireContext.keys().map(requireContext) +requireAll(svg) diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/check-one-blue.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/check-one-blue.svg new file mode 100644 index 000000000..b57938811 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/check-one-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/clouduploadsharp.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/clouduploadsharp.svg new file mode 100644 index 000000000..1f4c3958e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/clouduploadsharp.svg @@ -0,0 +1 @@ + Upload to Cloud \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/developing.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/developing.svg new file mode 100644 index 000000000..2e0153b6d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/developing.svg @@ -0,0 +1,227 @@ + + + 编组 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/download.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/download.svg new file mode 100644 index 000000000..4a5492ae8 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/download.svg @@ -0,0 +1 @@ + Download diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/file-search-fail.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/file-search-fail.svg new file mode 100644 index 000000000..c5eee901d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/file-search-fail.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/file-search-suc.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/file-search-suc.svg new file mode 100644 index 000000000..e57b64087 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/file-search-suc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/file-zip.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/file-zip.svg new file mode 100644 index 000000000..8dc08bbdf --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/file-zip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/hexagon-one-blue.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/hexagon-one-blue.svg new file mode 100644 index 000000000..b4f93cfed --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/hexagon-one-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/hexagon-one-gray.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/hexagon-one-gray.svg new file mode 100644 index 000000000..dc76abd03 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/hexagon-one-gray.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/hexagon-one-green.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/hexagon-one-green.svg new file mode 100644 index 000000000..a45d2933e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/hexagon-one-green.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/hexagon-one-red.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/hexagon-one-red.svg new file mode 100644 index 000000000..5f152250e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/hexagon-one-red.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/home.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/home.svg new file mode 100644 index 000000000..1dec32e41 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/home.svg @@ -0,0 +1,10 @@ + + + 首页-未 + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/logs.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/logs.svg new file mode 100644 index 000000000..e6139f31b --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/logs.svg @@ -0,0 +1,5 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-charu.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-charu.svg new file mode 100644 index 000000000..fd7a52a5c --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-charu.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-chaxun.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-chaxun.svg new file mode 100644 index 000000000..2c7e566d5 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-chaxun.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-dataSource.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-dataSource.svg new file mode 100644 index 000000000..73464e28b --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-dataSource.svg @@ -0,0 +1 @@ + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-fenzu.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-fenzu.svg new file mode 100644 index 000000000..701645e93 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-fenzu.svg @@ -0,0 +1,7 @@ + + + Created with Pixso. + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-gengxin.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-gengxin.svg new file mode 100644 index 000000000..4d2c828af --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-gengxin.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-juhe.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-juhe.svg new file mode 100644 index 000000000..cac9d99af --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-juhe.svg @@ -0,0 +1,7 @@ + + + Created with Pixso. + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-paixu.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-paixu.svg new file mode 100644 index 000000000..feb191e9b --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-paixu.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-qiaojie.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-qiaojie.svg new file mode 100644 index 000000000..84fe6fe8a --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-qiaojie.svg @@ -0,0 +1,7 @@ + + + Created with Pixso. + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-shanchu.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-shanchu.svg new file mode 100644 index 000000000..0a77df60a --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-shanchu.svg @@ -0,0 +1,15 @@ + + + Created with Pixso. + + + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-tiaojian.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-tiaojian.svg new file mode 100644 index 000000000..b304df7ea --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-tiaojian.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-waiting.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-waiting.svg new file mode 100644 index 000000000..02b2b2bd4 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-waiting.svg @@ -0,0 +1,227 @@ + + + 编组 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-xiantiao.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-xiantiao.svg new file mode 100644 index 000000000..52968e9de --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-xiantiao.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-yingshe.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-yingshe.svg new file mode 100644 index 000000000..5b8367426 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-yingshe.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-zidian.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-zidian.svg new file mode 100644 index 000000000..dce61b309 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling-zidian.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling.svg new file mode 100644 index 000000000..5d14b5081 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/modeling.svg @@ -0,0 +1,18 @@ + + + 业务-未 + + + + + + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/monitor.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/monitor.svg new file mode 100644 index 000000000..1fe330279 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/monitor.svg @@ -0,0 +1,12 @@ + + + 监控-未 + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/og-db.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/og-db.svg new file mode 100644 index 000000000..6ba81c034 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/og-db.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-busi-flow.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-busi-flow.svg new file mode 100644 index 000000000..528ff51d4 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-busi-flow.svg @@ -0,0 +1,15 @@ + + + 业务流 + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-cluster-ops.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-cluster-ops.svg new file mode 100644 index 000000000..32de2d1d6 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-cluster-ops.svg @@ -0,0 +1,15 @@ + + + 集群运维 + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-cluster.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-cluster.svg new file mode 100644 index 000000000..954d1fde6 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-cluster.svg @@ -0,0 +1,50 @@ + + + 集群 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-data-flow.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-data-flow.svg new file mode 100644 index 000000000..38b7f9993 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-data-flow.svg @@ -0,0 +1,15 @@ + + + 数据流 + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-data-model.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-data-model.svg new file mode 100644 index 000000000..ba2dc69de --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-data-model.svg @@ -0,0 +1,23 @@ + + + 数据模型 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-deploy.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-deploy.svg new file mode 100644 index 000000000..e6a3a01ff --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-deploy.svg @@ -0,0 +1,14 @@ + + + 步骤方式 + + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-empty.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-empty.svg new file mode 100644 index 000000000..7d0ec0299 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-empty.svg @@ -0,0 +1,158 @@ + + + 编组备份 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-error.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-error.svg new file mode 100644 index 000000000..2ef337565 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-error.svg @@ -0,0 +1,9 @@ + + + 错误 + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-has-install-plugin.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-has-install-plugin.svg new file mode 100644 index 000000000..de221f6ab --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-has-install-plugin.svg @@ -0,0 +1,14 @@ + + + 安装插件图标 + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-host.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-host.svg new file mode 100644 index 000000000..0a8ae8b31 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-host.svg @@ -0,0 +1,30 @@ + + + 主机 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-install-success.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-install-success.svg new file mode 100644 index 000000000..09e9d1b03 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-install-success.svg @@ -0,0 +1,112 @@ + + + 编组备份 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-instance.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-instance.svg new file mode 100644 index 000000000..c2e29fb97 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-instance.svg @@ -0,0 +1,29 @@ + + + 实例 + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-mini-version.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-mini-version.svg new file mode 100644 index 000000000..b497ad91e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-mini-version.svg @@ -0,0 +1,38 @@ + + + 极简版新 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-offline-install.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-offline-install.svg new file mode 100644 index 000000000..4f454b589 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-offline-install.svg @@ -0,0 +1,27 @@ + + + 离线-新 + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-one-check.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-one-check.svg new file mode 100644 index 000000000..18667f66d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-one-check.svg @@ -0,0 +1,10 @@ + + + 一键自检 + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-online-install.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-online-install.svg new file mode 100644 index 000000000..4b30fb2ff --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-online-install.svg @@ -0,0 +1,26 @@ + + + 在线-新 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-pass.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-pass.svg new file mode 100644 index 000000000..8f2376498 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-pass.svg @@ -0,0 +1,10 @@ + + + 通过16 + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-warning.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-warning.svg new file mode 100644 index 000000000..8e1ef6a22 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops-warning.svg @@ -0,0 +1,9 @@ + + + 警告16 + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops.svg new file mode 100644 index 000000000..e971c033c --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/ops.svg @@ -0,0 +1,12 @@ + + + 运维-未 + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/plugin.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/plugin.svg new file mode 100644 index 000000000..27f0e3dbb --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/plugin.svg @@ -0,0 +1,16 @@ + + + 插件-未 + + + + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/resource.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/resource.svg new file mode 100644 index 000000000..8fba22504 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/resource.svg @@ -0,0 +1,24 @@ + + + 资源中心-未 + + + + + + + + + + + + + + + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/security.svg b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/security.svg new file mode 100644 index 000000000..3ef3a6ac3 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/icons/svg/security.svg @@ -0,0 +1,10 @@ + + + 安全-未 + + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/bar.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/bar.png new file mode 100644 index 000000000..2e3f9ea4f Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/bar.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/graph.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/graph.png new file mode 100644 index 000000000..2e7a5f144 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/graph.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/heatmap.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/heatmap.png new file mode 100644 index 000000000..bca6a2966 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/heatmap.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/line.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/line.png new file mode 100644 index 000000000..0b381cb52 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/line.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/mix.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/mix.png new file mode 100644 index 000000000..c7a1fe8b1 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/mix.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/pie.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/pie.png new file mode 100644 index 000000000..2bce0bf99 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/pie.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/scatter.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/scatter.png new file mode 100644 index 000000000..2fcdf834e Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/chart/scatter.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/data-flow-header.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/data-flow-header.png new file mode 100644 index 000000000..a4f7c5b09 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/data-flow-header.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/bulkimport-done.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/bulkimport-done.png new file mode 100644 index 000000000..87c775b3e Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/bulkimport-done.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/bulkimport-fail.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/bulkimport-fail.png new file mode 100644 index 000000000..231ce665e Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/bulkimport-fail.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/bulkimport-import.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/bulkimport-import.png new file mode 100644 index 000000000..947b1e197 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/bulkimport-import.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/bulkimport-ing.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/bulkimport-ing.png new file mode 100644 index 000000000..c9e2162f1 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/bulkimport-ing.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/busi-flow.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/busi-flow.png new file mode 100644 index 000000000..6ab7000f5 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/busi-flow.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/cluster-ops.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/cluster-ops.png new file mode 100644 index 000000000..cdb14a63c Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/cluster-ops.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/cluster.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/cluster.png new file mode 100644 index 000000000..780963497 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/cluster.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/data-flow.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/data-flow.png new file mode 100644 index 000000000..9a4e522ce Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/data-flow.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/data-modeling.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/data-modeling.png new file mode 100644 index 000000000..000d4e6d8 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/data-modeling.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/deploy-way-active.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/deploy-way-active.png new file mode 100644 index 000000000..016c846de Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/deploy-way-active.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/deploy-way.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/deploy-way.png new file mode 100644 index 000000000..0eeaa021c Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/deploy-way.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/empty.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/empty.png new file mode 100644 index 000000000..7dfe04867 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/empty.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/env-monitor-active.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/env-monitor-active.png new file mode 100644 index 000000000..978c16b97 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/env-monitor-active.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/env-monitor.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/env-monitor.png new file mode 100644 index 000000000..5e409cd5a Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/env-monitor.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/exe-install-active.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/exe-install-active.png new file mode 100644 index 000000000..930bc5334 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/exe-install-active.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/exe-install.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/exe-install.png new file mode 100644 index 000000000..c98ca1e83 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/exe-install.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/host.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/host.png new file mode 100644 index 000000000..5167ff5bd Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/host.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-config-active.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-config-active.png new file mode 100644 index 000000000..c7aa8c1b3 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-config-active.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-config.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-config.png new file mode 100644 index 000000000..cc7555918 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-config.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-prompt-active.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-prompt-active.png new file mode 100644 index 000000000..8d74f70e4 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-prompt-active.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-prompt.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-prompt.png new file mode 100644 index 000000000..d4536fd04 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-prompt.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-success.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-success.png new file mode 100644 index 000000000..38eddaa4a Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/install-success.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/instance.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/instance.png new file mode 100644 index 000000000..eb6ed87fc Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/instance.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/mini-version.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/mini-version.png new file mode 100644 index 000000000..ceb9be9e6 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/mini-version.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/offline.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/offline.png new file mode 100644 index 000000000..119099556 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/offline.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/one-check.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/one-check.png new file mode 100644 index 000000000..72e91ae07 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/one-check.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/online.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/online.png new file mode 100644 index 000000000..34c3d546f Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/online.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/plugin.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/plugin.png new file mode 100644 index 000000000..2f2d04745 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/plugin.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/preview-close.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/preview-close.png new file mode 100644 index 000000000..a1c3de51b Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/preview-close.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/preview-open.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/preview-open.png new file mode 100644 index 000000000..3b045e160 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/preview-open.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/right-arrow.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/right-arrow.png new file mode 100644 index 000000000..ae74d6ca1 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/right-arrow.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/task-warning.png b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/task-warning.png new file mode 100644 index 000000000..081fb14c4 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/images/modeling/ops/task-warning.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/logo.png b/ER-Model/plugins/base-ops/web-ui/src/assets/logo.png new file mode 100644 index 000000000..d6da897a8 Binary files /dev/null and b/ER-Model/plugins/base-ops/web-ui/src/assets/logo.png differ diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/style/breakpoint.less b/ER-Model/plugins/base-ops/web-ui/src/assets/style/breakpoint.less new file mode 100644 index 000000000..584f21e5a --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/style/breakpoint.less @@ -0,0 +1,19 @@ +// ==============breakpoint============ + +// Extra small screen / phone +@screen-xs: 480px; + +// Small screen / tablet +@screen-sm: 576px; + +// Medium screen / desktop +@screen-md: 768px; + +// Large screen / wide desktop +@screen-lg: 992px; + +// Extra large screen / full hd +@screen-xl: 1200px; + +// Extra extra large screen / large desktop +@screen-xxl: 1600px; diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/style/global.less b/ER-Model/plugins/base-ops/web-ui/src/assets/style/global.less new file mode 100644 index 000000000..138802061 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/style/global.less @@ -0,0 +1,308 @@ +@import "standard"; + +* { + box-sizing: border-box; +} + +html, +body { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + font-size: 14px; + background-color: var(--color-bg-1); + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + // overflow: hidden; +} + +/**********custom specification***********/ +body { + --margin-base: 20px; // Margin + --margin-vertical: 15px; // Margins (Vertical) + + --padding-base: 20px; // padding + --padding-vertical: 15px; // Padding (vertical direction) +} + +// body style +.app-container { + .main-bd { + background: var(--color-bg-2); + border-radius: 2px; + } +} + +.label-color { + color: var(--color-text-2) +} + +.value-color { + color: var(--color-text-1) +} + +// z index +.z-0 { + z-index: 0; +} + +.z-1 { + z-index: 1; +} + +.z-10 { + z-index: 10; +} + +.z-20 { + z-index: 20; +} + +.z-30 { + z-index: 30; +} + +.z-40 { + z-index: 40; +} + +.z-50 { + z-index: 50; +} + +.z-auto { + z-index: auto; +} + +// common +.clearfix { + + &:before, + &:after { + content: " "; + display: table; + } + + &:after { + clear: both; + } +} + +.fl { + float: left; +} + +.fr { + float: right; +} + +// flex common +.flex-col { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.flex-col-start { + display: flex; + flex-direction: column; + align-items: flex-start; +} + +.flex-col-end { + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.flex-row { + display: flex; + align-items: center; +} + +.flex-row-center { + display: flex; + align-items: center; + justify-content: center; +} + +.flex-row-start { + display: flex; + align-items: center; + justify-content: flex-start; +} + +.flex-row-end { + display: flex; + align-items: center; + justify-content: flex-end; +} + +.flex-between { + display: flex; + justify-content: space-between; +} + +.echarts-tooltip-diy { + background: linear-gradient(304.17deg, + rgba(253, 254, 255, 0.6) -6.04%, + rgba(244, 247, 252, 0.6) 85.2%) !important; + border: none !important; + backdrop-filter: blur(10px) !important; + /* Note: backdrop-filter has minimal browser support */ + + border-radius: 6px !important; + + .content-panel { + display: flex; + justify-content: space-between; + padding: 0 9px; + background: rgba(255, 255, 255, 0.8); + width: 164px; + height: 32px; + line-height: 32px; + box-shadow: 6px 0px 20px rgba(34, 87, 188, 0.1); + border-radius: 4px; + margin-bottom: 4px; + } + + .tooltip-title { + margin: 0 0 10px 0; + } + + p { + margin: 0; + } + + .tooltip-title, + .tooltip-value { + font-size: 13px; + line-height: 15px; + display: flex; + align-items: center; + text-align: right; + color: #1d2129; + font-weight: bold; + } + + .tooltip-item-icon { + display: inline-block; + margin-right: 8px; + width: 10px; + height: 10px; + border-radius: 50%; + } +} + +.general-card { + border-radius: 4px; + border: none; + + &>.arco-card-header { + height: auto; + padding: 20px; + border: none; + } + + &>.arco-card-body { + padding: 0 20px 20px 20px; + } +} + +.split-line { + border-color: rgb(var(--gray-2)); +} + +.arco-table-cell { + .circle { + display: inline-block; + margin-right: 4px; + width: 6px; + height: 6px; + border-radius: 50%; + background-color: rgb(var(--blue-6)); + + &.pass { + background-color: rgb(var(--green-6)); + } + } +} + +.custom-winbox { + padding: 35px 0px 0px 0px; + z-index: 100 !important; +} + +.custom-winbox-mini { + padding: 35px 0px 0px 0px; + z-index: 100 !important; + bottom: 0px !important; + top: auto !important; +} + +// empty icon style +.empty-c { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + border-radius: 8px; + padding: 20px; + + .tip-content { + color: var(--color-neutral-4); + } +} + +.arco-message-list-top { + left: 0; +} + +.arco-btn-primary[type='button'] { + color: var(--color-bg-2); + + &:hover { + color: var(--color-bg-2); + } + + &.arco-btn-loading { + color: var(--color-bg-2); + } +} + +.arco-steps-item-process .arco-steps-icon { + color: var(--color-bg-2); +} + +.arco-steps-mode-arrow .arco-steps-item-process .arco-steps-item-title { + color: var(--color-bg-2); +} + +.arco-switch-checked .arco-switch-text { + color: var(--color-bg-2); +} + +body[arco-theme="dark"] .arco-link.arco-link-loading {} +.arco-link.arco-link-loading { + color: rgb(var(--link-6)) +} + +.arco-link.arco-link-disabled { + color: var(--color-danger-light-3) +} + +.arco-progress-line-bar { + background-color: rgb(var(--primary-5)) !important; +} + +.terminal-container{ + /* this is important */ + overflow: hidden; +} +.xterm .xterm-viewport { + /* see : https://github.com/xtermjs/xterm.js/issues/3564#issuecomment-1004417440 */ + width: initial !important; +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/style/ops/ops.less b/ER-Model/plugins/base-ops/web-ui/src/assets/style/ops/ops.less new file mode 100644 index 000000000..a700af9f9 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/style/ops/ops.less @@ -0,0 +1,55 @@ +// install page common style +// previous step next step switch panel style +.panel-c { + width: 100%; + height: 100%; +} + +.panel-header {} + +.panel-body { + padding: 20px; + overflow-y: auto; +} + +.panel-footer { + display: flex; + justify-content: center; +} + +// install select card style +.card-item-c { + border-radius: 8px; + border: 1px solid #C9CDD4; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + cursor: pointer; + // box-shadow: 0px 4px 20px 0px rgba(255, 255, 255, 0.5); + transition: all 0.3s ease-in-out; + + &:hover { + box-shadow: 0px 4px 20px 0px rgba(153, 153, 153, 0.6); + cursor: pointer; + } +} + +// select page select option select style +.center-item-active { + cursor: pointer; + padding: 8px; + border-radius: 8px; + border-color: rgb(var(--primary-6)) !important; + box-shadow: 0px 4px 20px 0px rgba(var(--primary-6), 0.4); +} + +.icon-size-s { + width: 60px; + height: 60px; +} + +.icon-size { + width: 100px; + height: 100px; +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/style/standard.less b/ER-Model/plugins/base-ops/web-ui/src/assets/style/standard.less new file mode 100644 index 000000000..59249a4a9 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/style/standard.less @@ -0,0 +1,108 @@ +@import "variables"; + +.mr-xs { + margin-right: @gap-xs; +} + +.mr-s { + margin-right: @gap-s; +} + +.mr { + margin-right: @gap; +} + +.mr-lg { + margin-right: @gap-lg; +} + +.mr-xlg { + margin-right: @gap-xlg; +} + +.mb-xs { + margin-bottom: @gap-xs; +} + +.mb-s { + margin-bottom: @gap-s; +} + +.mb { + margin-bottom: @gap; +} + +.mb-lg { + margin-bottom: @gap-lg; +} + +.mb-xlg { + margin-bottom: @gap-xlg; +} + +.icon-s { + width: 16px !important; + height: 16px !important; +} + +.icon { + width: 22px !important; + height: 22px !important; +} + +.icon-lg { + width: 70px !important; + height: 70px !important; +} + +.icon-xlg { + width: 90px !important; + height: 90px !important; +} + +.ft-s { + font-size: @font-s; +} + +// The current default font size is 14 +.ft-m { + font-size: @font-m; +} + +.ft-lg { + font-size: @font-lg; +} + +.ft-xlg { + font-size: @font-xlg; +} + +.ft-b { + font-weight: 600; +} + +.dlg-s { + .el-dialog { + width: 600px; + } +} + +.dlg { + .el-dialog { + width: 800px; + } +} + +.dlg-lg { + .el-dialog { + width: 1100px; + } +} + +.full-w { + width: 100% !important; +} + +.full-h { + height: 100% !important; +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/assets/style/variables.less b/ER-Model/plugins/base-ops/web-ui/src/assets/style/variables.less new file mode 100644 index 000000000..c896b0ba6 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/assets/style/variables.less @@ -0,0 +1,12 @@ +// margin size +@gap-xlg: 90px; +@gap-lg: 30px; +@gap: 20px; +@gap-s: 8px; +@gap-xs: 4px; + +// font size default 14px +@font-s: 12px; +@font-m: 16px; +@font-lg: 20px; +@font-xlg: 24px; diff --git a/ER-Model/plugins/base-ops/web-ui/src/components/downloadNotification/index.vue b/ER-Model/plugins/base-ops/web-ui/src/components/downloadNotification/index.vue new file mode 100644 index 000000000..cd3408c83 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/components/downloadNotification/index.vue @@ -0,0 +1,156 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/components/index.ts b/ER-Model/plugins/base-ops/web-ui/src/components/index.ts new file mode 100644 index 000000000..ca8d82b96 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/components/index.ts @@ -0,0 +1,31 @@ +import { App } from 'vue' +import { use } from 'echarts/core' +import { CanvasRenderer } from 'echarts/renderers' +import { BarChart, LineChart, PieChart, RadarChart } from 'echarts/charts' +import { + GridComponent, + TooltipComponent, + LegendComponent, + DataZoomComponent, + GraphicComponent +} from 'echarts/components' +import SvgIcon from './svg-icon/index.vue' + +use([ + CanvasRenderer, + BarChart, + LineChart, + PieChart, + RadarChart, + GridComponent, + TooltipComponent, + LegendComponent, + DataZoomComponent, + GraphicComponent +]) + +export default { + install (Vue: App) { + Vue.component('svg-icon', SvgIcon) + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/components/svg-icon/index.vue b/ER-Model/plugins/base-ops/web-ui/src/components/svg-icon/index.vue new file mode 100644 index 000000000..1669c086e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/components/svg-icon/index.vue @@ -0,0 +1,17 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/config/settings.json b/ER-Model/plugins/base-ops/web-ui/src/config/settings.json new file mode 100644 index 000000000..afc29eca7 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/config/settings.json @@ -0,0 +1,16 @@ +{ + "theme": "light", + "colorWeak": false, + "navbar": true, + "menu": true, + "hideMenu": false, + "menuCollapse": false, + "footer": true, + "themeColor": "#E41D1D", + "menuWidth": 220, + "globalSettings": false, + "device": "desktop", + "tabBar": true, + "menuFromServer": false, + "serverMenu": [] +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/hooks/chart-option.ts b/ER-Model/plugins/base-ops/web-ui/src/hooks/chart-option.ts new file mode 100644 index 000000000..9ca3cc399 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/hooks/chart-option.ts @@ -0,0 +1,25 @@ +import { computed } from 'vue' +import { EChartsOption } from 'echarts' +import { useAppStore } from '@/store' + +// for code hints +// import { SeriesOption } from 'echarts'; +// Because there are so many configuration items, this provides a relatively convenient code hint. +// When using vue, pay attention to the reactive issues. It is necessary to ensure that corresponding functions can be triggered, TypeScript does not report errors, and code writing is convenient. +interface optionsFn { + (isDark: boolean): EChartsOption; +} + +export default function useChartOption (sourceOption: optionsFn) { + const appStore = useAppStore() + const isDark = computed(() => { + return appStore.theme === 'dark' + }) + + const chartOption = computed(() => { + return sourceOption(isDark.value) + }) + return { + chartOption + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/hooks/loading.ts b/ER-Model/plugins/base-ops/web-ui/src/hooks/loading.ts new file mode 100644 index 000000000..63bf50fd9 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/hooks/loading.ts @@ -0,0 +1,16 @@ +import { ref } from 'vue' + +export default function useLoading (initValue = false) { + const loading = ref(initValue) + const setLoading = (value: boolean) => { + loading.value = value + } + const toggle = () => { + loading.value = !loading.value + } + return { + loading, + setLoading, + toggle + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/hooks/locale.ts b/ER-Model/plugins/base-ops/web-ui/src/hooks/locale.ts new file mode 100644 index 000000000..099975c1b --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/hooks/locale.ts @@ -0,0 +1,19 @@ +import { computed } from 'vue' +import { useI18n } from 'vue-i18n' +import { Message } from '@arco-design/web-vue' + +export default function useLocale () { + const i18 = useI18n() + const currentLocale = computed(() => { + return i18.locale.value + }) + const changeLocale = (value: string) => { + i18.locale.value = value + localStorage.setItem('locale', value) + // Message.success(value === 'zh-CN' ? '切换为简体中文' : 'Switch to English') + } + return { + currentLocale, + changeLocale + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/hooks/permission.ts b/ER-Model/plugins/base-ops/web-ui/src/hooks/permission.ts new file mode 100644 index 000000000..d7512405e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/hooks/permission.ts @@ -0,0 +1,33 @@ +import { RouteLocationNormalized, RouteRecordRaw } from 'vue-router' +import { useUserStore } from '@/store' + +export default function usePermission () { + const userStore = useUserStore() + return { + accessRouter (route: RouteLocationNormalized | RouteRecordRaw) { + return ( + !route.meta?.requiresAuth || + !route.meta?.roles || + route.meta?.roles?.includes('*') || + route.meta?.roles?.includes(userStore.role) + ) + }, + findFirstPermissionRoute (_routers: any, role = 'admin') { + const cloneRouters = [..._routers] + while (cloneRouters.length) { + const firstElement = cloneRouters.shift() + if ( + firstElement?.meta?.roles?.find((el: string[]) => { + return el.includes('*') || el.includes(role) + }) + ) + return { name: firstElement.name } + if (firstElement?.children) { + cloneRouters.push(...firstElement.children) + } + } + return null + } + // You can add any rules you want + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/hooks/request.ts b/ER-Model/plugins/base-ops/web-ui/src/hooks/request.ts new file mode 100644 index 000000000..2b6b35a06 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/hooks/request.ts @@ -0,0 +1,26 @@ +import { ref, UnwrapRef } from 'vue' +import { AxiosResponse } from 'axios' +import { HttpResponse } from '@/api/interceptor' +import useLoading from './loading' + +// use to fetch list +// Don't use async function. It doesn't work in async function. +// Use the bind function to add parameters +// example: useRequest(api.bind(null, {})) + +export default function useRequest ( + api: () => Promise>, + defaultValue = [] as unknown as T, + isLoading = true +) { + const { loading, setLoading } = useLoading(isLoading) + const response = ref(defaultValue) + api() + .then((res) => { + response.value = res.data as unknown as UnwrapRef + }) + .finally(() => { + setLoading(false) + }) + return { loading, response } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/hooks/responsive.ts b/ER-Model/plugins/base-ops/web-ui/src/hooks/responsive.ts new file mode 100644 index 000000000..c72e4a2fb --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/hooks/responsive.ts @@ -0,0 +1,32 @@ +import { onMounted, onBeforeMount, onBeforeUnmount } from 'vue' +import { useDebounceFn } from '@vueuse/core' +import { useAppStore } from '@/store' +import { addEventListen, removeEventListen } from '@/utils/event' + +const WIDTH = 992 // https://arco.design/vue/component/grid#responsivevalue + +function queryDevice () { + const rect = document.body.getBoundingClientRect() + return rect.width - 1 < WIDTH +} + +export default function useResponsive (immediate?: boolean) { + const appStore = useAppStore() + function resizeHandler () { + if (!document.hidden) { + const isMobile = queryDevice() + appStore.toggleDevice(isMobile ? 'mobile' : 'desktop') + appStore.toggleMenu(isMobile) + } + } + const debounceFn = useDebounceFn(resizeHandler, 100) + onMounted(() => { + if (immediate) debounceFn() + }) + onBeforeMount(() => { + addEventListen(window, 'resize', debounceFn) + }) + onBeforeUnmount(() => { + removeEventListen(window, 'resize', debounceFn) + }) +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/hooks/themes.ts b/ER-Model/plugins/base-ops/web-ui/src/hooks/themes.ts new file mode 100644 index 000000000..adafe0a39 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/hooks/themes.ts @@ -0,0 +1,12 @@ +import { computed } from 'vue' +import { useAppStore } from '@/store' + +export default function useThemes () { + const appStore = useAppStore() + const isDark = computed(() => { + return appStore.theme === 'dark' + }) + return { + isDark + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/hooks/useConfirm.ts b/ER-Model/plugins/base-ops/web-ui/src/hooks/useConfirm.ts new file mode 100644 index 000000000..fe5493d11 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/hooks/useConfirm.ts @@ -0,0 +1,36 @@ +import { ref } from 'vue' + + +const useConfirm = () => { + const nextConfirmVisible = ref(false) + const confirmMessage = ref('') + + let confirmPromiseResolve: ((value: boolean) => void) | null = null + const waitConfirm = async () => { + const confirmPromise = new Promise(resolve => { + confirmPromiseResolve = resolve + }) + try { + const confirmed = await confirmPromise + return confirmed + } catch (error) { + return false + } + } + + const resolveAction = (confirmed: boolean) => { + nextConfirmVisible.value = confirmed + if (confirmPromiseResolve) { + confirmPromiseResolve(confirmed) + } + } + + return { + nextConfirmVisible, + confirmMessage, + waitConfirm, + resolveAction + } +} + +export default useConfirm diff --git a/ER-Model/plugins/base-ops/web-ui/src/hooks/user.ts b/ER-Model/plugins/base-ops/web-ui/src/hooks/user.ts new file mode 100644 index 000000000..d7a2dca4d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/hooks/user.ts @@ -0,0 +1,24 @@ +import { useRouter } from 'vue-router' +import { Message } from '@arco-design/web-vue' + +import { useUserStore } from '@/store' + +export default function useUser () { + const router = useRouter() + const userStore = useUserStore() + const logout = async (logoutTo?: string) => { + await userStore.logout() + const currentRoute = router.currentRoute.value + Message.success('logout success') + router.push({ + name: logoutTo && typeof logoutTo === 'string' ? logoutTo : 'login', + query: { + ...router.currentRoute.value.query, + redirect: currentRoute.name as string + } + }) + } + return { + logout + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/hooks/visible.ts b/ER-Model/plugins/base-ops/web-ui/src/hooks/visible.ts new file mode 100644 index 000000000..8323af8e7 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/hooks/visible.ts @@ -0,0 +1,16 @@ +import { ref } from 'vue' + +export default function useVisible (initValue = false) { + const visible = ref(initValue) + const setVisible = (value: boolean) => { + visible.value = value + } + const toggle = () => { + visible.value = !visible.value + } + return { + visible, + setVisible, + toggle + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/layout/default-layout.vue b/ER-Model/plugins/base-ops/web-ui/src/layout/default-layout.vue new file mode 100644 index 000000000..866de0e9f --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/layout/default-layout.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/layout/parent-view/index.vue b/ER-Model/plugins/base-ops/web-ui/src/layout/parent-view/index.vue new file mode 100644 index 000000000..6c71efda3 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/layout/parent-view/index.vue @@ -0,0 +1,5 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/locale/en-US.ts b/ER-Model/plugins/base-ops/web-ui/src/locale/en-US.ts new file mode 100644 index 000000000..3050f8804 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/locale/en-US.ts @@ -0,0 +1,6 @@ + +import en from './locale/en.json' + +export default { + ...en +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/locale/index.ts b/ER-Model/plugins/base-ops/web-ui/src/locale/index.ts new file mode 100644 index 000000000..ba74dc2a5 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/locale/index.ts @@ -0,0 +1,22 @@ +import { createI18n } from 'vue-i18n' +import en from './en-US' +import cn from './zh-CN' + +export const LOCALE_OPTIONS = [ + { label: '简体中文', value: 'zh-CN' }, + { label: 'English', value: 'en-US' } +] +const defaultLocale = localStorage.getItem('locale') || 'zh-CN' + +const i18n = createI18n({ + locale: defaultLocale, + fallbackLocale: 'en-US', + globalInjection: true, + allowComposition: true, + messages: { + 'en-US': en, + 'zh-CN': cn + } +}) + +export default i18n diff --git a/ER-Model/plugins/base-ops/web-ui/src/locale/locale/en-US.ts b/ER-Model/plugins/base-ops/web-ui/src/locale/locale/en-US.ts new file mode 100644 index 000000000..ab2c31c93 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/locale/locale/en-US.ts @@ -0,0 +1,4 @@ + +export default { + 'common.test': 'test' +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/locale/locale/en.json b/ER-Model/plugins/base-ops/web-ui/src/locale/locale/en.json new file mode 100644 index 000000000..08dab7057 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/locale/locale/en.json @@ -0,0 +1,2197 @@ +{ + "customControl": { + "HostPwdDlg": { + "else1": "RootPassword", + "else2": "Please select host information", + "5mplfut5bj80": "Host", + "5mplfut5kuc0": "Please enter the password", + "5mplfut5lgo0": "Safety Inspection", + "5mplfut5lus0": "Please enter root password", + "user": "user", + "userPlaceholder": "Please select user", + "noDevice": "No device information available." + }, + "index": { + "else1": "No host information is available", + "else2": "Creating a host", + "else3": "Refresh", + "else4": "No console", + "else5": "Open console" + } + }, + "indicators": { + "index": { + "5mpli5dxtx80": "Under development, please wait" + } + }, + "components": { + "AuditLog": { + "else1": "Time range", + "5mplidlico80": "Query", + "5mplidlidfs0": "Time", + "5mplidlido80": "Type", + "5mplidlie1k0": "Result", + "5mplidlielo0": "User ID", + "5mplidlietk0": "User name", + "5mplidlif1o0": "database", + "5mplidlifdw0": "Client information", + "5mplidlifj80": "Object information", + "5mplidlifnc0": "Details", + "5mplidlifr40": "Node information", + "5mplidlifv40": "Thread ID", + "5mplidlig300": "Local port", + "5mplidlig7w0": "Remote port", + "5mplidligbw0": "Failed to obtain the audit log" + }, + "FileLog": { + "5mplk6504fg0": "Getting data, please wait", + "5mplk65055s0": "No data yet" + }, + "ChooseInstallWay": { + "5mpmwx7b1eo0": "Please select the installation mode", + "5mpmwx7b2580": "Has selected", + "5mpmwx7b2gg0": "Offline Installation", + "5mpmwx7b2p00": "Online Installation" + }, + "ChooseVersion": { + "5mpmxod8xno0": "Please select the version of openGauss you want to install", + "5mpmxod8yh00": "Has selected", + "5mpmxod8yr40": "Installation Mode", + "5mpmxod8yxs0": "MINI", + "5mpmxod8z3w0": "The main use of the minimalist version of the installation is mainly for universities and personal testing environment, compared with the enterprise installation process is simpler and faster。", + "5mpmxod8za00": "LITE", + "5mpmxod8zg00": "ENTERPRISE", + "else2": "sharding+openLookeng", + "5mpmxod8zlw0": "The installation of enterprise Edition is mainly used by enterprises or individuals with high requirements for database performance. The installation process is complex and the functions are more complete。", + "5mpmxod8zs80": "install", + "5mpmxod901g0": "import", + "5mpmxod901g1": "Batch import", + "5mpmxod907k0": "Selected version", + "else1": "openGauss lightweight edition is installed on each machine separately, because the lightweight version does not have OM, CM and other components, so the installation, uninstall, upgrade and other OM functions need to be manually operated on each node。", + "openLooKeng": "sharding+openLookeng", + "openLooKengRemark": "openLooKeng is an \"out of the box\" engine that supports in situ analysis of any data from any location, including geographically remote data sources. It provides a global view of all the data through the SQL 2003 interface.", + "else3": "Please note: Versions 5.0.0 and later differentiate between minimalist, lightweight, and Enterprise editions, while previous versions only differentiate between enterprise and non-enterprise editions. Verify that the version you select corresponds to the version to be imported" + }, + "ExeImport": { + "5mpmzg3zqu80": "Congratulations on the successful import", + "5mpmzg3zrrw0": "Keep the user name and password safe", + "5mpmzg3zs0o0": "Database user", + "5mpmzg3zs6w0": "Database password", + "5mpmzg3zseo0": "Back to Home Page", + "5mpmzg3zskw0": "Cluster Operation", + "5mpmzg3zsu80": "Import failure", + "5mpmzg3zt0g0": "Importing in progress", + "5mpmzg3ztak0": "Request exception occurs" + }, + "BatchImport":{ + "5mpmzg3zt1o0": "Drag the file to this area or", + "5mpmzg3zt1o1": "Choose file", + "5mpmzg3zesl0": "Only Excel format files are supported, and the size should not exceed 200MB.", + "5mpmertfggy0": "template", + "5mpmertfggy1": "Error report", + "5mpmzg3zesl1": "Download the upload template", + "5mpmzg3zesl2": "File uploading in progress", + "5mpmeddf3zl0": "Upload failed.The size should not exceed 200MB.Please reupload", + "5mpmeddf3zl1": "Upload failed. You need to upload a single file.Please reupload", + "5mpmeddf3zl2": "Upload failed. You need to upload a Excel file.Please reupload", + "5mpmeddf3zl3": ",Please modify the file and", + "5mpmeddf3zl4": "File parsing, please wait", + "5mpmeddf3zl5": "Do not refresh the page", + "5mpmeddf3zl6": "Data may be lost, are you sure you want to refresh the page?", + "5mpmeddf3zl7": "File parsing failed", + "5mpmzg3zesl3": "Reupload", + "5mpmzg3zesl4": "You can still replace file by dragging and dropping it into the area below", + "5mpmzg3zesl5": "Delete", + "5mpmzg3zesl6": "File upload successfully", + "5mpmqndzeok0": "Confirm", + "5mpmqndzeok1": "Cancel", + "5mpmqndzeok2": "File parsing", + "5mpmqndzeok3": "Import cluster", + "5mpmqndzeok4": "Import complete", + "5mpmqndzeok5": "{totalNum} record(s) imported in total", + "5mpmewrfzse0": "{num}", + "5mpmewrfzse1": " imported successfully,", + "5mpmewrfzse3": " failed to import", + "5mpmewrfzse4": "Download the error report", + "5mpmewrfzse5": "Reimport", + "5mpmewrfzse6": "Finish" + }, + "OfflineInstall": { + "5mpn1nway8c0": "Please select openGauss", + "5mpn1nwaywo0": "The installation package to use", + "5mpn1nwaz280": "The installation package has been selected", + "5mpn1nwaz600": "The installation package I want is not listed? Click To Upload", + "5mpn1nwaz980": "Package Directory", + "5mpn1nwazd40": "Please enter the installation package directory path", + "5mpn1nwazgw0": "Installation Package Path", + "5mpn1nwazks0": "Please enter the installation directory", + "5mpn1nwazok0": "MINI", + "5mpn1nwazs80": "LITE", + "5mpn1nwazvg0": "ENTERPRISE", + "else1": "If the DataKit is installed offline, upload the installation package to the server where the DataKit is located.", + "else2": "The path can only be a Linux path", + "else3": "The CPU architecture information of the installation package is being obtained. Please wait", + "5mpn1nwazvg2": "FilePath:", + "5mpn1nwazvg3": "PackageManagedName:" + }, + "OnlineInstall": { + "else1": "The installation package has been selected", + "5mpn3mp10hw0": "Directory where the installation package is saved", + "5mpn3mp111s0": "download", + "5mpn3mp117k0": "Downloaded", + "5mpn3mp11cg0": "Download completed", + "5mpn3mp11g00": "Download Progress", + "else2": "Obtaining the installation package", + "else3": "No installation package information is available", + "else4": "Add an installation package", + "5mpn3mp11g01": "file extension not correct, must be one of .tar.gz|.tar|.tar.bz2" + }, + "OneCheck": { + "clusterName": "Cluster Name", + "else1": "CheckHashIndex", + "else2": "CheckNodeGroupName", + "else3": "CheckTDDate", + "else4": "Detection completion time", + "else5": "Check Item", + "else6": "rootPassword", + "else7": "Please enter the root password", + "5mpiji1q2ng0": "One Check", + "5mpiji1q3wo0": "Select a cluster name", + "5mpiji1q4a00": "retest", + "5mpiji1q4k40": "detection", + "5mpiji1q4tg0": "One Check in progress", + "5mpiji1q57w0": "Cluster", + "5mpiji1q5r40": "CheckClusterState", + "5mpiji1q5zw0": "CheckDBParams", + "5mpiji1q6b00": "CheckDebugSwitch", + "5mpiji1q8q80": "CheckDirPermissions", + "5mpiji1q96c0": "CheckReadonlyMode", + "5mpiji1q9r00": "CheckEnvProfile", + "5mpiji1qa080": "CheckDilateSysTab", + "5mpiji1qa940": "CheckProStartTime", + "5mpiji1qahc0": "CheckMpprcFile", + "5mpiji1qas40": "Database", + "5mpiji1qbv80": "CheckCurConnCount", + "5mpiji1qc780": "CheckCursorNum", + "5mpiji1qcjs0": "CheckPgxcgroup", + "5mpiji1qczo0": "CheckSysadminUser", + "5mpiji1qdeo0": "CheckTableSpace", + "5mpiji1qdlk0": "CheckPgxcRedistb", + "5mpiji1qds80": "System", + "5mpiji1qdy40": "CheckMemInfo", + "5mpiji1qe8s0": "CheckCPU", + "5mpiji1qehc0": "CheckOSVer", + "5mpiji1qeoo0": "CheckKeyProAdj", + "5mpiji1qf280": "CheckMaxProcMemory", + "5mpiji1qf9k0": "CheckEncoding", + "5mpiji1qfgo0": "CheckFirewall", + "5mpiji1qfnc0": "CheckKernelVer", + "5mpiji1qfx00": "CheckMaxHandle", + "5mpiji1qg400": "CheckNTPD", + "5mpiji1qga80": "CheckSysParams", + "5mpiji1qgl40": "CheckTHP", + "5mpiji1qgt80": "CheckTimeZone", + "5mpiji1qh2k0": "CheckSshdService", + "5mpiji1qh8s0": "CheckSshdConfig", + "5mpiji1qhfs0": "CheckCrondService", + "5mpiji1qhn00": "CheckStack", + "5mpiji1qhto0": "CheckSysPortRange", + "5mpiji1qi2s0": "CheckHyperThread", + "5mpiji1qif40": "CheckBootItems", + "5mpiji1qioc0": "CheckFilehandle", + "5mpiji1qizk0": "CheckDropCache", + "5mpiji1qjag0": "Host", + "5mpiji1qjh80": "CheckBlockdev", + "5mpiji1qkn00": "CheckDiskFormat", + "5mpiji1qkyk0": "CheckSwapMemory", + "5mpiji1ql580": "CheckLogicalBlock", + "5mpiji1qldc0": "CheckIOrequestqueue", + "5mpiji1qlmc0": "CheckMaxAsyIOrequests", + "5mpiji1qltc0": "CheckSpaceUsage", + "5mpiji1qm0s0": "CheckInodeUsage", + "5mpiji1qmbw0": "CheckIOConfigure", + "5mpiji1qmj40": "Network", + "5mpiji1qmqg0": "CheckPing", + "5mpiji1qn1c0": "CheckNetWorkDrop", + "5mpiji1qn8g0": "CheckNICModel", + "5mpiji1qnf00": "CheckMTU", + "5mpiji1qnks0": "CheckRXTX", + "5mpiji1qnr00": "CheckMultiQueue", + "5mpiji1qnxs0": "CheckRouting", + "5mpiji1qo440": "Item", + "5mpiji1qoe00": "NoPass", + "5mpiji1qom40": "Error", + "5mpiji1qosc0": "Warning", + "5mpiji1qoyw0": "Pass", + "5mpiji1qpcc0": "Check category", + "5mpiji1qpns0": "Check Items", + "5mpiji1qpvg0": "Check Result", + "5mpiji1qq180": "View Return" + }, + "openLooKeng": { + "5mpiji1qpcb0": "name", + "5mpiji1qpcb1": "please input name", + "5mpiji1qpcb2": "remark", + "5mpiji1qpcb3": "please input remark", + "5mpiji1qpcb4": "DistributeDeploy", + "5mpiji1qpcb5": "please select deployment package", + "5mpiji1qpcb6": "After deployment, you can perform operation and maintenance operations on components using this name", + "5mpiji1qpcc0": "Basic Info", + "5mpiji1qpcc1": "deploy ip", + "5mpiji1qpbc1": "Distribute Deploy Info", + "5mpiji1qpcc2": "username", + "5mpiji1qpcc3": "root password", + "5mpiji1qpcc4": "please input root password", + "5mpiji1qpcc5": "install path", + "5mpiji1qpcc6": "please input install path", + "5mpiji1qpcc7": "Package Info", + "5mpiji1qpcc8": "openLooKeng Package", + "5mpiji1qpcc9": "please select openLooKeng package", + "5mpiji1qpcc10": "openLooKeng bind port", + "5mpiji1qpcc11": "please input openLooKeng bind port", + "5mpiji1qpcc12": "please input username", + "5mpiji1qpcc13": "please input password", + "5mpiji1qpcc14": "please input install path", + "5mpiji1qpcc15": "please input openLooKeng port", + "5mpiji1qpcc16": "please select openLooKeng package", + "5mpiji1qpcc17": "Add Config", + "5mpiji1qpcc18": "Config View", + "5mpiji1qpcc19": "Execute Install", + "5mpiji1qpcc21": "The installation path will be automatically created when it does not exist", + "5mpiji1qpcc22": "Sharding package", + "5mpiji1qpcc23": "please select ShardingProxy package", + "5mpiji1qpcc24": "Sharding bind port", + "5mpiji1qpcc25": "please input Sharding port", + "5mpiji1qpcc26": "Zookeeper package", + "5mpiji1qpcc27": "please select Zookeeper package", + "5mpiji1qpcc28": "Zookeeper bind port", + "5mpiji1qpcc29": "please input Zookeeper bind port", + "5mpiji1qpcc30": "sharding config", + "5mpiji1qpcc31": "table name", + "5mpiji1qpcc32": "please input table name,split with ,", + "5mpiji1qpcc33": "column", + "5mpiji1qpcc34": "please input, split with ,", + "5mpiji1qpcb35": "Basic Info", + "5mpiji1qpcc35": "ShardingProxy config", + "5mpiji1qpcc36": "openLooKeng config", + "5mpiji1qpcc37": "You can edit the sharding configuration file rule.yaml in the editor below", + "5mpiji1qpcc38": "Please enter the correct directory path", + "5mpiji1qpcc39": "Please select the deployment middleware installation package", + "5mpiji1qpcc40": "binding port", + "5mpiji1qpcc41": "please input Distribute Deploy binding port", + "5mpiji1qpcc42": "connection failed", + "5mpiji1qpcc43": "upload path", + "5mpiji1qpcc44": "please input upload path", + "5mpiji1qpcc45": "automatically created when the upload path does not exist", + "5mpiji1qpcc46": "Data source selection", + "5mpiji1qpcc47": "please select at least two data sources", + "5mpiji1qpcc48": "Cluster/Instance/Database", + "5mpiji1qpcc49": "installation package upload path", + "5mpiji1qpcc50": "please input installation package upload path", + "5mpiji1qpcc51": "Rebuild", + "5mpiji1qpcc52": "Advanced parameter configuration", + "5mpiji1qpcc53": "please input xmx", + "5mpiji1qpcc54": "please input maxMemory", + "5mpiji1qpcc55": "please input maxTotalMemory", + "5mpiji1qpcc56": "please input maxMemoryPer", + "5mpiji1qpcc57": "please input maxTotalMemoryPer", + "5mpiji1qpcc58": "Installation succeeded", + "5mpiji1qpcc59": "Return to Home Page", + "5mpiji1qpcc60": "Upload Package", + "5mpiji1qpcc61": "Install Zookeeper", + "5mpiji1qpcc62": "Install ShardingProxy", + "5mpiji1qpcc63": "Install openLookEng", + "5mpiji1qpcc64": "Start All Service", + "5mpiji1qpcc65": "DownloadLog", + "5mpiji1qpcc66": "Shell", + "5mpiji1qpcc67": "Retry", + "5mpiji1qpcc68": "You can go to the openLooKeng management page to manage the newly installed cluster", + "5mpiji1qpcc69": "openLooKeng Manage", + "5mpiji1qpcc70": "Deploy", + "5mpiji1qpcc71": "Please input deploy name", + "5mpiji1qpcc72": "Start", + "5mpiji1qpcc73": "Stop", + "5mpiji1qpcc74": "Are you sure you want to delete this row?", + "5mpiji1qpcc75": "Confirm", + "5mpiji1qpcc76": "Cancel", + "5mpiji1qpcb76": "Delete", + "5mpiji1qpcc77": "Are you sure you want to release this deployment? After the relevant release, it cannot be recovered", + "5mpiji1qpcc78": "Destroy", + "5mpiji1qpcc79": "Log Download", + "5mpiji1qpcc80": "deploy name", + "5mpiji1qpcc81": "table name", + "5mpiji1qpcc82": "column", + "5mpiji1qpcc83": "update time", + "5mpiji1qpcc84": "remark", + "5mpiji1qpcc85": "operation", + "5mpiji1qpcc86": "Distribute Deploy Install Info", + "5mpiji1qpcc87": "package", + "5mpiji1qpcc88": "host ip", + "5mpiji1qpcc89": "install username", + "5mpiji1qpcc90": "install path", + "5mpiji1qpcc91": "upload path", + "5mpiji1qpcc92": "port", + "5mpiji1qpcc93": "Zookeeper Install Info", + "5mpiji1qpcc94": "ShardingProxy Install Info", + "5mpiji1qpcc95": "openLooKeng Install Info" + }, + "HostTerminal": { + "else1": "IP", + "else2": "If the font is blurred under special screen resolution, please scale the page", + "5oxhoh5r6ls0": "Terminal" + } + }, + "logCenter": { + "index": { + "else1": "IP Address", + "5mpllm8hjao0": "Cluster name", + "5mpllm8hk7o0": "Please select the cluster name", + "5mpllm8hkk00": "Please select the host IP address", + "5mpllm8hkog0": "Search", + "5mpllm8hks40": "System log", + "5mpllm8hkwk0": "Operation log", + "5mpllm8hl000": "Audit Log", + "5mpllm8hl3w0": "Performance log", + "5mpllm8hl7w0": "Black box log" + } + }, + "monitor": { + "index": { + "5mplmn5z06k0": "Instance monitoring", + "5mplmn5z19w0": "Cluster name", + "5mplmn5z1lo0": "Please select the cluster name", + "5mplmn5z1qs0": "Host IP address", + "5mplmn5z1uc0": "Please select the host IP address", + "5mplmn5z1y80": "Memory usage", + "5mplmn5z2300": "Network status", + "5mplmn5z2a00": "lock", + "5mplmn5z2ms0": "Conversation", + "5mplmn5z30k0": "Connection number", + "5mplmn5z37w0": "TOP10 Sessions with the Largest Memory Usage", + "5mplmn5z3e90": "Please enable the enable_thread_pool parameter", + "5mplmn5z41w0": "The host lack dependency packages:" + } + }, + "operation": { + "task": { + "step1": { + "index": { + "serverConfig": "Server Configuration", + "IP": "Host IP", + "IPPlaceholder": "Please enter or select host IP", + "IPContent": "Primary and standby nodes must have consistent OS and architecture. Changing host IP may affect node configuration. Confirm change?", + "installUser": "Installation User", + "installUserPlaceholder": "Please select installation user", + "userContent": "Primary and standby nodes must use the same user. Changing user may affect node configuration. Confirm change?", + "packageConfig": "Package Configuration", + "system": "Operating System", + "cpuArch": "System Architecture", + "version": "openGauss Version", + "MINIMAL_LIST": "Minimal Edition", + "LITE": "Lite Edition", + "ENTERPRISE": "Enterprise Edition", + "MINIMAL_LISTContent": "The Minimal Edition is primarily for academic and personal testing environments, offering simpler and faster installation than enterprise versions.", + "LITEContent": "The Lite Edition requires manual installation on each machine as it lacks OM and CM components. Installation, uninstallation, and upgrades must be performed node-by-node.", + "ENTERPRISEContent": "The Enterprise Edition targets businesses and performance-demanding users, featuring a more complex installation process and comprehensive functionality.", + "versionNum": "openGauss Version Number", + "versionPlaceholder": "Enter version number", + "selectedPackage": "Selected Package", + "selecPack": "Selected Package", + "morePackage": "Package not listed?", + "packageManagement": "Package Management", + "upload": "Offline Upload", + "download": "Online Download", + "clusterConfig": "Cluster Configuration", + "clusterName": "Cluster Identifier", + "clusterNamePlaceholder": "Enter cluster identifier", + "installPath": "Installation Directory", + "installPathPlaceholder": "Enter installation directory", + "installPathContent": "Installation directory refers to openGauss root path (e.g., /opt/openGauss)", + "installPackagePath": "Software Package Path", + "installPackagePathplaceholder": "Enter software package path", + "installPackagePathcontent": "Path where openGauss installation package files are stored", + "port": "Port Number", + "dbPwd": "Database Password", + "dbPwdContent": "Credential for database login. Please use a high-security password.", + "deployType": "Deployment Type", + "SINGLE_NODE": "Single Node", + "CLUSTER": "Multi-node", + "checkflagCM": "Install CM?", + "checkflagEnvSeqar": "Environment Separation?", + "envPath": "Environment Separation Path", + "envPathPlaceholder": "Enter environment separation path", + "placeholder": "Please enter", + "proConfig": "Advanced Parameters", + "logPath": "Log Directory", + "tmpPath": "Temp Directory", + "omToolsPath": "Database Tools Directory", + "corePath": "Database Core Directory", + "logPathPlaceholder": "Enter log directory", + "tmpPathPlaceholder": "Enter temp directory", + "omToolsPathPlaceholder": "Enter database tools directory", + "corePathPlaceholder": "Enter database core directory", + "nodeConfig": "Node Configuration", + "addColumn": "Add Node", + "clusterType": "Node Type", + "clusterMaster": "Primary Node", + "clusterOthers": "Standby Node", + "dataPath": "Data Path", + "azOwner": "AZ Owner", + "azPriority": "AZ Priority", + "isCMMaster": "Is CM Primary?", + "cmMasterContent": "Only one node can be CM Primary. Changing this may affect other nodes. Confirm change?", + "yes": "Yes", + "no": "No", + "confirm": "Confirm", + "cancel": "Cancel", + "cmDataPath": "CM Data Path", + "cmPort": "CM Port", + "cmPortContent": "All CM nodes must use the same port. Changing this may affect other nodes. Confirm change?", + "option": "Operation", + "edit": "Edit", + "delete": "Delete", + "deleteContent": "Confirm deletion?", + "save": "Save", + "serverDataError": "Incomplete server data. Please select another IP.", + "packageExistError": "Package not found. Please reselect.", + "packageCheckError": "Package verification failed. May affect the process.", + "userNullError": "Select installation user to generate nodes.", + "ipSameError": "IP same as host IP. Please reselect.", + "ipNullError": "IP cannot be empty.", + "ipUserError": "User not found on this IP. Reselect IP or add user.", + "saveError": "Invalid items exist. Cannot save.", + "sameNameError": "Duplicate names exist. Cannot save.", + "cmMasterError": "No CM Primary node exists. Cannot change to non-primary.", + "websocketErrorMsg": "WebSocket unresponsive - closing download progress panel", + "onlineDownload": "Online Download", + "ipNoticeError": "IP Down", + "portOccupied": "Port is occupied, unable to save", + "notificationText": "Match the following installation package based on the configuration and download it:", + "passwdWarning": "For security reasons, passwords cannot be displayed. Please re-enter your password to make changes.", + "visible": "visible", + "hide": "hide" + } + }, + "step2s5cf2y0": "Environmental monitoring in progress...", + "step2s5cf2y1": "Hardware environment monitoring results", + "step2s5cf2y2": "Operating System", + "step2s5cf2y3": "Please check if the operating system is", + "step2s5cf2y4": "Operating system meets requirements", + "step2s5cf2y5": "Operating system version", + "step2s5cf2y6": "Please check if the operating system version meets requirements", + "step2s5cf2y7": "Operating system version meets requirements", + "step2s5cf2y8": "Available memory", + "step2s5cf2y9": "Recommended memory: 32GB or more", + "step2s5cf210": "Available memory meets requirements", + "step2s5cf211": "CPU cores", + "step2s5cf212": "CPU cores do not meet system requirements", + "step2s5cf213": "CPU cores meet system requirements", + "step2s5cf214": "CPU frequency", + "step2s5cf215": "Minimum CPU frequency: 2.0GHz", + "step2s5cf216": "CPU frequency meets requirements", + "step2s5cf217": "Available disk space", + "step2s5cf218": "Available disk space does not meet system requirements", + "step2s5cf219": "Available disk space meets system requirements", + "step2s5cf220": "Software environment monitoring results", + "step2s5cf221": "Software dependencies", + "step2s5cf222": "Missing dependencies:", + "step2s5cf223": "Meets requirements", + "step2s5cf224": "Firewall", + "step2s5cf225": "Firewall is not enabled", + "step2s5cf226": "Firewall is enabled", + "step2s5cf227": "Installing user", + "step2s5cf228": "User is not allowed to install", + "step2s5cf229": "User is allowed to install", + "step2s5cf230": "Others", + "step2s5cf231": "Does not meet requirements:", + "step2s5cf232": "All other conditions meet requirements", + "step2s5cf233": "Re-monitor", + "step2s5cf234": "Failed to obtain IP address" + }, + "ClusterBackupDlg": { + "5mplmzbrmkg0": "Backup", + "5mplmzbrntg0": "Cluster name", + "5mplmzbro8g0": "Backup path", + "5mplmzbroj00": "Please enter the backup path", + "5mplmzbrosg0": "Remarks", + "5mplmzbrp1s0": "Please enter remarks", + "5mplmzbrpas0": "Cluster backup" + }, + "DailyOps": { + "sl3u5s5cf2y0": "Cluster Manager", + "sl3u5s5cf2y1": "Usage Guide", + "sl3u5s5cf2y2": "Create a Parallel Installation Task", + "sl3u5s5cf2y3": "Install a Cluster", + "sl3u5s5cf2y4": "CPU Utilization TOP5", + "sl3u5s5cf2y5": "Memory Utilization TOP5", + "sl3u5s5cf2y6": "Session TOP5", + "sl3u5s5cf2y7": "Cluster List", + "sl3u5s5cf2y8": "Parallel Install Tasks", + "sl3u5s5cf2y9": "Task List", + "sl3u5s5cf210": "Draft Box", + "sl3u5s5cf211": "Batch Execution", + "sl3u5s5cf212": "Delete", + "sl3u5s5cf299": "Please enter cluster name to search", + "sl3u5s5cf213": "Please Select Appropriate Tags", + "sl3u5s5cf214": "Pending", + "sl3u5s5cf215": "Execution Waiting", + "sl3u5s5cf216": "Executing", + "sl3u5s5cf217": "Execution Successful", + "sl3u5s5cf218": "Execution Failed", + "sl3u5s5cf219": "Are you sure you want to execute?", + "sl3u5s5cf220": "One-click Execution", + "sl3u5s5cf221": "Re-execute", + "sl3u5s5cf222": "Copy", + "sl3u5s5cf223": "More", + "sl3u5s5cf224": "Execution Log Download", + "sl3u5s5cf225": "Edit", + "sl3u5s5cf226": "Publish", + "sl3u5s5cf227": "Environment Detection", + "sl3u5s5cf228": "Server Information", + "sl3u5s5cf229": "Server IP", + "sl3u5s5cf230": "Server OS", + "sl3u5s5cf231": "CPU Architecture", + "sl3u5s5cf232": "openGauss Version", + "sl3u5s5cf233": "Number of Cluster Nodes", + "sl3u5s5cf234": "Cluster Port Number", + "sl3u5s5cf235": "Environment Variable Address", + "sl3u5s5cf236": "Install CM?", + "sl3u5s5cf237": "Environment Isolation?", + "sl3u5s5cf238": "Environment Isolation Path", + "sl3u5s5cf239": "Node Type", + "sl3u5s5cf240": "AZ Priority", + "sl3u5s5cf241": "Are you sure you want to execute the following task?", + "sl3u5s5cf242": "Executing the task will install the database on the target host, please proceed with caution.", + "sl3u5s5cf243": "Execution Confirmation", + "sl3u5s5cf244": "Task ID", + "sl3u5s5cf245": "Task Status", + "sl3u5s5cf246": "Server IP", + "sl3u5s5cf247": "openGauss Version", + "sl3u5s5cf248": "Task Creation Time", + "sl3u5s5cf249": "Task Update Time", + "sl3u5s5cf250": "Only one child tag can be selected for the same parent tag.", + "sl3u5s5cf251": "There are multiple items under the tag.", + "sl3u5s5cf252": "Please select at least one row to delete.", + "sl3u5s5cf253": "Please select at least one row to execute.", + "sl3u5s5cf254": "Maximum Value (%)", + "sl3u5s5cf255": "Utilization Rate (%)", + "else1": "core", + "else2": "Instance", + "else3": "count", + "else4": "Del Cluster", + "else5": "This delete only deletes the platform data, sure to delete?", + "else6": "Refresh", + "else7": "Pending", + "else8": "Unknown", + "else9": "Down", + "else10": "Abnormal", + "else11": "Manually stopped", + "else12": "Database user", + "else13": "Database port", + "else14": "Install Path", + "else15": "Data Path", + "else16": "Install User", + "else17": "ENV File Path", + "5mplp1xbxl40": "Cluster name", + "5mplp1xbyi40": "Self-check warning", + "lastCheckAt": "Last self-check time", + "5mplp1xbyqc0": "One-key self-test", + "5mplp1xbyxw0": "Current status", + "5mplp1xbz600": "available", + "5mplp1xbzew0": "Number of nodes", + "5mplp1xbzmw0": "One master", + "5mplp1xbztc0": "Standby", + "5mplp1xc0000": "Single node", + "5mplp1xc0640": "You can do the following", + "5mplp1xc0c00": "Start", + "5mplp1xc0i80": "Stop", + "5mplp1xc0o40": "Restart", + "5mplp1xc0u40": "Backup", + "5mplp1xc0zo0": "Are you sure you want to delete?", + "5mplp1xc1580": "Confirm", + "5mplp1xc1b00": "Cancel", + "5mplp1xc1gs0": "Uninstall", + "5mplp1xc1ms0": "Forced uninstall", + "5mplp1xc1t80": "IP(Intranet/extranet)", + "5mplp1xc20k0": "Operation", + "5mplp1xc25o0": "Start all instances", + "5mplp1xc2b40": "Stop all instances", + "5mplp1xc2gw0": "Intranet IP address", + "5mplp1xc2ls0": "External IP address", + "5mplp1xc2sw0": "Host name", + "5mplp1xc2xs0": "Host configuration", + "5mplp1xc32g0": "Memory", + "5mplp1xc3740": "Session", + "5mplp1xc3bw0": "Connection number", + "5mplp1xc3hs0": "lock", + "5mplp1xc3no0": "Availability zone", + "5mplp1xc3ss0": "No cluster information currently", + "5mplp1xc3xg0": "Go install", + "5mplp1xc4200": "Getting cluster information, please wait", + "5mplp1xc46o0": "Minimalist version", + "5mplp1xc4b40": "Lightweight Edition", + "5mplp1xc4fg0": "Enterprise Edition", + "5mplp1xc4jw0": "main", + "5mplp1xc4oc0": "Cascading", + "5mplp1xc4sw0": "Instance switch operation", + "5mplp1xc4x80": "Unavailable", + "5mplp1xc51s0": "Undetected", + "5mplp1xc56k0": "Active/standby switchover", + "5mplp1xc5cg0": "Generate configuration", + "nodeBuild": "Node rebuild", + "nodeField1": "Have a fault", + "nodeState1": "available", + "nodeState2": "Need repair", + "nodeState3": "Starting", + "nodeState4": "Waiting to upgrade", + "nodeState5": "upgrading", + "nodeState6": "In demotion", + "nodeState7": "Need to rebuild", + "nodeState8": "Catch-up master", + "nodeState9": "collapse", + "nodeState10": "unknown", + "guc5cg0": "guc setting", + "guc5cg1": "name:", + "guc5cg2": "please input param name", + "guc5cg3": "search", + "guc5cg4": "ResetDefaultToAll", + "guc5cg5": "RestoreToAll", + "guc5cg6": "ApplyToAllInstance", + "guc5cg7": "Save", + "guc5cg8": "Name", + "guc5cg9": "Value", + "guc5ch0": "Description", + "guc5ch1": "Remark", + "guc5ch2": "Operation", + "guc5ch3": "SetToDefault", + "guc5ch4": "Restore", + "guc5ch5": "setting guc parameters and restarting cluster", + "guc5ch6": "setting guc parameters", + "guc5ch7": "Confirm Cluster Restart", + "guc5ch8": "You have modified the guc parameters of the postmaster type. You need to restart the cluster to take effect. Do you want to set the guc parameters now and restart?" + }, + "HostPwdDlg": { + "pwdValid": "Password check", + "IpAddress": "IP address", + "rootPwd": "rootPassword", + "rootPwdPlaceholder": "Please enter the root password", + "else1": "The value cannot be pure space" + } + }, + "slowSql": { + "index": { + "else1": "DB Name", + "else2": "Node Name", + "5mplw69rv000": "Cluster name", + "5mplw69rvmo0": "Please select the cluster name", + "5mplw69rvt40": "Host IP address", + "5mplw69rvz80": "Please select the host IP address", + "5mplw69rw300": "Start Time", + "5mplw69rw900": "Query", + "5mplw69rwcw0": "Finish Time", + "5mplw69rwgs0": "Slow Sql Threshold", + "5mplw69rwks0": "Query", + "5mplw69rwog0": "Query Plan" + } + }, + "snapshot": { + "index": { + "5mplywcvqh80": "Creating a snapshot...", + "5mplywcvr080": "Create Snapshot", + "5mplywcvr7c0": "Cluster name", + "5mplywcvrb80": "Please select the cluster name", + "5mplywcvres0": "Host IP", + "5mplywcvrio0": "Please select the host IP address", + "5mplywcvrmg0": "Search", + "5mplywcvrq80": "Snapshot ID", + "5mplywcvru80": "Capture Time", + "snapshotManage": "Snapshot Manage" + } + }, + "wdr": { + "GenerateWdrDlg": { + "else1": "The start snapshot cannot be later than or equal to the end snapshot", + "else2": "The end snapshot cannot be earlier than or equal to the start snapshot", + "5mpm0eufx3g0": "Generate", + "5mpm0eufxww0": "Cluster name", + "5mpm0eufy340": "Please select the cluster", + "5mpm0eufy7k0": "Host IP address", + "5mpm0eufyb40": "Please select the host IP address", + "5mpm0eufyew0": "Report scope", + "5mpm0eufyig0": "Please select report scope", + "5mpm0eufyo80": "Report type", + "5mpm0eufyz00": "Please select the report type", + "5mpm0eufzbk0": "Start snapshot", + "5mpm0eufzg00": "Please select Start snapshot", + "5mpm0eufzk80": "End snapshot", + "5mpm0eufzo40": "Please select End snapshot", + "5mpm0eufzrs0": "Generate WDR", + "5mpm0eufzv40": "cluster", + "5mpm0eufzyg0": "node", + "5mpm0eug0300": "Details", + "5mpm0eug0880": "Summary", + "5mpm0eug0bc0": "All" + }, + "index": { + "snapshotManage": "Snapshot Manage", + "5mpm1cuaq540": "Cluster name", + "5mpm1cuaqvk0": "Please select the cluster name", + "5mpm1cuar0w0": "Production time", + "5mpm1cuar500": "Report Scope", + "5mpm1cuar9s0": "Please select report scope", + "5mpm1cuardo0": "Report type", + "5mpm1cuarh00": "Please select report type", + "5mpm1cuarks0": "Query", + "5mpm1cuaro40": "Reset", + "5mpm1cuarrk0": "Generate WDR", + "5mpm1cuarv40": "View", + "5mpm1cuaryo0": "Download", + "5mpm1cuas2s0": "Are you sure you want to delete?", + "5mpm1cuas6c0": "Confirm", + "5mpm1cuasa40": "Cancel", + "5mpm1cuasdc0": "Delete", + "5mpm1cuasgo0": "cluster", + "5mpm1cuask80": "node", + "5mpm1cuasng0": "Details", + "5mpm1cuasr40": "Summary", + "5mpm1cuasvs0": "all", + "5mpm1cuasys0": "Report generation time", + "5mpm1cuat200": "Report name", + "5mpm1cuat4o0": "Operation" + } + }, + "backup": { + "index": { + "else1": "Cluster Name", + "else2": "Are you sure you want to recover?", + "5mpm2oya7bg0": "Please select the cluster name", + "5mpm2oya7y80": "Search", + "5mpm2oya83s0": "restore", + "5mpm2oya87g0": "Are you sure you want to delete?", + "5mpm2oya8b00": "Confirm", + "5mpm2oya8ek0": "cancel", + "5mpm2oya8i80": "Delete", + "5mpm2oya8lk0": "Cluster identification", + "5mpm2oya8r80": "Backup identification", + "else3": "Backup remark", + "5mpm2oya8uo0": "in the machine", + "5mpm2oya8y00": "Backup path", + "5mpm2oya9280": "Backup time", + "5mpm2oya95o0": "Operation" + } + }, + "enterprise": { + "ClusterConfig": { + "5mpm3ku3go40": "Cluster configuration", + "5mpm3ku3hcg0": "Cluster ID", + "5mpm3ku3hik0": "Please enter the cluster ID", + "5mpm3ku3hn40": "Cluster name", + "5mpm3ku3hr00": "Please enter the cluster name", + "5mpm3ku3hv40": "Installation directory", + "5mpm3ku3i340": "Please enter the installation directory", + "5mpm3ku3i6s0": "Log directory", + "5mpm3ku3iag0": "Please enter the log directory", + "5mpm3ku3ie40": "temporary file directory", + "5mpm3ku3ihg0": "Please enter temporary file directory", + "5mpm3ku3il80": "Database tool directory", + "5mpm3ku3iok0": "Please enter the Database tool directory", + "5mpm3ku3is00": "Database core directory", + "5mpm3ku3ivk0": "Please enter the database core directory", + "5mpm3ku3iz80": "port number", + "5mpm3ku3j300": "Please enter the port number", + "5mpm3ku3j6k0": "Database user name", + "5mpm3ku3j9s0": "Please enter the database username", + "5mpm3ku3jdg0": "Database password", + "5mpm3ku3ji00": "Please enter the database password", + "5mpm3ku3jmn0": "DCF port", + "5mpm3ku3jnl0": "Please enter the port number of DCF", + "5mpm3ku3jkw0": "Enable database node self-selection at runtime", + "5mpm3ku3jo00": "The cluster ID already exists, please re-enter", + "5mpm3ku3jqo0": "Determine whether there is an error in the cluster ID", + "5mpm3ku3jpo0": "Sharing Storage Configuration", + "5mpm3ku3jso0": "In Cluster installing, host Ip should be different", + "5mpm3ku3jto0": "In Cluster installing, installed user should be the same", + "5mpm3ku3joo0": "Installation Package Path must be different with Installation directory", + "5mpm3ku3jxo0": "The DCF port and the database port cannot be the same", + "5mpm3ku3jro0": "The length of this field cannot exceed 20 characters", + "5mpm3ku3jwo0": "In Cluster installing, CM port should be the same", + "5mpm3ku3juo0": "In Cluster installing, database port and CM port must be different", + "5mpm3ku3juw0": "Cluster id contains illegal character", + "5mpm3ku3juy0": "This field cannot contain only numbers", + "5mpm3ku3jux0": "The port value is invalid, please input port in range 1024~65529", + "5mpm3ku3jyx0": "The CM port and the database port cannot be the same", + "5mpm3ku3jzx0": "The CM port and the DCF port cannot be the same", + "5mpm3ku3jvx0": "The path format is incorrect", + "5mpm3ku3jvx5": "The installation path cannot intersect with the software package path. Please set up a separate folder.", + "5mpm3ku3jvx6": "The installation path cannot intersect with the advanced parameter configuration path. Please set up a separate app folder.", + "else1": "Please enter numbers, letters or underscores", + "else2": "The value cannot be pure space", + "else3": "Master", + "else4": "Slave", + "test0": "Db kernel Arch", + "test00": "Please select Db kernel Arch", + "test1": "Please enter the dss home directory", + "test2": "Dss home directory", + "test3": "Please enter the dss vg name", + "test4": "Dss vg name", + "test5": "Please select the dss data lun path", + "test6": "Dss data lun path", + "test7": "Please enter the xlog vg name", + "test8": "Xlog vg name", + "test9": "Please select the xlog lun path", + "test10": "Xlog lun path", + "test11": "Please select the cm sharing lun path", + "test12": "CM sharing lun path", + "test13": "Please select the cm voting lun path", + "test14": "CM voting lun path", + "test15": "Please select the interconnect type", + "test16": "Interconnect type", + "test17": "Please enter the radm working config", + "test18": "RDMA working config", + "collapse1": "sharing storage configuration", + "option1": "Master Slave Arch", + "option2": "Sharing Storage Arch", + "option3": "Single-Master Architecture", + "passwdWarning": "For security reasons, passwords cannot be displayed." + }, + "EnterpriseInstall": { + "else2": "prompt", + "else3": "execute", + "5mpm4jewxn00": "Cluster configuration", + "5mpm4jewye00": "Node configuration", + "5mpm4jewykk0": "Environmental Monitoring", + "5mpm4jewyp40": "Install", + "5mpm4jewysw0": "Import", + "error1": "Dss home is empty", + "error2": "Dss volume name is empty", + "error3": "Dss volume path is empty", + "error4": "Xlog volume name is empty", + "error5": "Xlog volume path is empty", + "error6": "CM sharing volume path is empty", + "error7": "CM voting volume path is empty", + "error8": "LUN path can not be the same", + "error9": "Xlog volume name and xlog volume path is not corresponding", + "error10": "The number of xlog volume names must be equal to 1 or equal to the number of nodes", + "error11": "Xlog volume name and Dss volume name must be different", + "error12": "When there are multiple xlog volume names, the xlog volume names cannot be the same", + "error13": "Dss home must be different with database install path" + + }, + "EnvMonitor": { + "else1": "One environment test failed, it is recommended to process and try again", + "else2": "node ip address", + "5mpm5p9xees0": "Testing in progress", + "5mpm5p9xf4w0": "You have", + "5mpm5p9xfb80": "Retest", + "5mpm5p9xff40": "Hardware Environment Monitoring", + "5mpm5p9xfk80": "Software environment test", + "5mpm5p9xfo40": "Detected a problem", + "5mpm5p9xfrs0": "Detection failure, failure reason", + "5mpm5p9xfv80": "Under test", + "5mpm5p9xfzo0": "Master", + "5mpm5p9xg3o0": "slave", + "5mpm5p9xg700": "Cascading" + }, + "ExeInstall": { + "else1": "DataBaseUser", + "else2": "DataBasePassword", + "5mpm9j359hw0": "Congratulations on your successful installation", + "5mpm9j35a4o0": "Keep the user name and password safe", + "5mpm9j35aa80": "Back to Home Page", + "5mpm9j35aec0": "Cluster Operation", + "5mpm9j35ahw0": "The installation failed. You can try again after resolving the problem", + "5mpm9j35alg0": "Do not perform other operations during the installation,", + "5mpm9j35ap00": "Otherwise, the installation will fail", + "5mpm9j35ats0": "retry" + }, + "InstallPrompt": { + "else1": "BelongAZ", + "else2": "Install Path", + "else3": "port", + "else4": "XLOG dir", + "else5": "CM port", + "else6": "CM data path", + "5mpmb9e6puk0": "Cluster Info", + "5mpmb9e6qmg0": "nodeIP", + "5mpmb9e6qsc0": "hostIp", + "5mpmb9e6qww0": "dataDir", + "5mpmb9e6r0k0": "isInstall", + "5mpmb9e6r4g0": "yes", + "5mpmb9e6r800": "no", + "5mpmb9e6rcw0": "master", + "5mpmb9e6rj80": "standby", + "5mpmb9e6rpo0": "cascade", + "5mpmb9e6rpp0": "Sharing Storage Info" + }, + "SharingStorageConfig": { + "else1": "Dss home directory", + "else2": "Dss vg name", + "else3": "Dss data lun path", + "else4": "Xlog vg name", + "else5": "Xlog lun path", + "else6": "CM sharing lun path", + "else7": "CM voting lun path", + "else8": "Interconnect type", + "else9": "RDMA working config" + }, + "NodeConfig": { + "else1": "xlogPath", + "else2": "rootPassword", + "else3": "isInstallCM", + "else4": "CMDataPath", + "else5": "CMPort", + "else6": "Please enter the az priority", + "else7": "az priority", + "else8": "Password error", + "else9": "Password check exception", + "else10": "The instance corresponding to port 5432 is not detected", + "else11": "The port is occupied", + "else12": "The instance in the data directory is not detected", + "else13": "Data directory detected is not empty", + "else14": "The installation package information in the installation package directory is not detected", + "else15": "The installation package directory is not empty", + "else16": "The installation package directory is not empty, Please click the previous step to modify", + "else17": "Environment separation file not detected, please click the previous step to modify", + "else18": "Environment separation file not detected", + "else19": "DSS Data Lun is not exist", + "else20": "Xlog Lun is not exist", + "else21": "CM Shring Lun is not exist", + "else22": "CM Voting Lun is not exist", + "else23": "DSS Home path is not empty", + "5mpme7w69yc0": "nodeNumber", + "5mpme7w6aj40": "BelongAZ", + "5mpme7w6ap00": "Select an AZ", + "5mpme7w6aw80": "Node Config", + "5mpme7w6azo0": "hostIP", + "5mpme7w6b3k0": "Select the host IP address", + "5mpme7w6b700": "Please enter the root password", + "5mpme7w6bak0": "installUser", + "5mpme7w6be40": "isMasterNodeCM", + "5mpme7w6bhg0": "Please enter the CM data path", + "5mpme7w6bko0": "Please enter the CM PORT", + "5mpme7w6boc0": "dataNode", + "5mpme7w6brs0": "dataPath", + "5mpme7w6bv40": "Please enter the data path", + "5mpme7w6byo0": "Please enter the xlog path", + "5mpme7w6c1w0": "Please select the host", + "5mpme7w6c5g0": "Please select the installation user", + "5mpme7w6c8s0": "Please fill in the CM data path", + "5mpme7w6cc40": "Please fill in the data path", + "5mpme7w6cfw0": "Please fill in the xlog path", + "5mpme7w6ckk0": "master", + "5mpme7w6cng0": "standby", + "5mpme7w6cqk0": "cascade", + "noRootTip": "The selected host needs to have a root user." + } + }, + "lightweight": { + "EnvMonitor": { + "else1": "One environment test failed, it is recommended to process and try again", + "5mpmgwswi880": "Testing in progress", + "5mpmgwswj8g0": "You have", + "5mpmgwswjlc0": "Node ip address", + "5mpmgwswjuk0": "Retest", + "5mpmgwswk640": "Hardware environment Monitoring", + "5mpmgwswkb40": "Software environment Detection", + "5mpmgwswkeo0": "A problem has been detected", + "5mpmgwswki80": "Detection failure, failure reason", + "5mpmgwswkls0": "Under testing", + "5mpmgwswkpc0": "Master", + "5mpmgwswkso0": "slave", + "5mpmgwswkwg0": "Cascading" + }, + "ExeInstall": { + "else1": "Environmental preparation", + "else2": "Installation package preparation", + "else3": "Perform the installation", + "else4": "Post-installation processing", + "else5": "Master Environmental preparation", + "else6": "Master Installation package preparation", + "else7": "Master Perform the installation", + "else8": "Slave Environmental preparation", + "else9": "Slave Installation package preparation", + "else10": "Slave Perform the installation", + "5mpmjd1mam40": "Congratulations on successful installation", + "5mpmjd1mbdc0": "Please keep the user name and password properly", + "5mpmjd1mbo00": "Database user", + "5mpmjd1mbxg0": "Database password", + "5mpmjd1mc580": "Back to home page", + "5mpmjd1mce00": "Cluster operation and maintenance", + "5mpmjd1mcm40": "Installation failed. You can try again after resolving the problem", + "5mpmjd1mcu40": "Do not perform other operations during the installation", + "5mpmjd1md2o0": "Otherwise the installation will fail", + "5mpmjd1mdf40": "Retry" + }, + "InstallConfig": { + "else1": "root password", + "else2": "The software package path indicates the directory where the compressed openGauss official installation package is stored", + "5mpmkfqy71w0": "master", + "5mpmkfqy8400": "slave", + "5mpmkfqy8es0": "Node configuration", + "5mpmkfqy8nc0": "Cluster ID", + "5mpmkfqy8r00": "Please enter the cluster ID", + "5mpmkfqy8vk0": "Host IP address", + "5mpmkfqy91w0": "Please select the host IP address", + "5mpmkfqy9h80": "Please enter the root password", + "5mpmkfqy9rw0": "Install user", + "5mpmkfqy9yo0": "Please select the installation user", + "5mpmkfqya4o0": "Installation directory", + "5mpmkfqyaas0": "Please enter the installation directory", + "5mpmkfqyah00": "Data directory", + "5mpmkfqyan00": "Please enter the data directory", + "5mpmkfqyasw0": "port number", + "5mpmkfqyay80": "Please enter the port number", + "5mpmkfqyb4c0": "Database user name", + "5mpmkfqyb9w0": "Please enter the database user name", + "5mpmkfqybf80": "Database password", + "5mpmkfqybo00": "Please enter the database password", + "5mpmkfqybw00": "Please select the host", + "5mpmkfqyc0w0": "The cluster ID already exists, please re-enter", + "5mpmkfqyc5w0": "Determine whether there is an error in the cluster ID", + "5mpmkfqycac0": "Failed to obtain host list data" + }, + "InstallPrompt": { + "5mpmly4bw340": "Node ip address", + "5mpmly4bwww0": "Host IP address", + "5mpmly4bx7g0": "Installation directory", + "5mpmly4bxfg0": "Data directory", + "5mpmly4bxlo0": "port number", + "5mpmly4bxtg0": "Lord", + "5mpmly4by1k0": "slave", + "5mpmly4by980": "Cascade" + }, + "LightWeightInstall": { + "else1": "Configuration", + "else2": "prompt", + "else3": "Execute", + "5mpmmqzpre40": "Deployment Mode", + "5mpmmqzprz80": "Install", + "5mpmmqzps6w0": "Import", + "5mpmmqzpsao0": "Environmental Monitoring" + } + }, + "simple": { + "DeployWay": { + "5mpmphlozp00": "single node", + "5mpmphlp0eo0": "Master", + "5mpmphlp0k00": "slave", + "5mpmphlp0nw0": "One master and one slave" + }, + "EnvMonitor": { + "else1": "One environment test failed, it is recommended to process and try again", + "5mpmqpcxjpc0": "testing in progress", + "5mpmqpcxke40": "You have", + "5mpmqpcxkq80": "Node ip address", + "5mpmqpcxkzc0": "Retest", + "5mpmqpcxlas0": "Hardware environment monitoring", + "5mpmqpcxlk40": "Software environment Detection", + "5mpmqpcxlxc0": "A problem has been detected", + "5mpmqpcxm6o0": "Detection failure, failure reason", + "5mpmqpcxmfs0": "Being detected", + "5mpmqpcxmog0": "master", + "5mpmqpcxmwg0": "slave", + "5mpmqpcxn5o0": "Cascading", + "confirm": "Confirm", + "errorConfirm": "{noPassNum} environment test failed, it is recommended to process and try again. Click confirm button if you still want to continue.", + "failConfirm": "Failed to test environment, it is recommended to check and try again. Click confirm button if you still want to continue." + }, + "ExeInstall": { + "else1": "Environmental preparation", + "else2": "Installation package preparation", + "else3": "Perform the installation", + "else4": "Post-installation processing", + "5mpmsp16nuo0": "Congratulations on your successful installation", + "5mpmsp16oy40": "Please keep the user name and password properly", + "5mpmsp16pb40": "Database user", + "5mpmsp16pig0": "Database password", + "5mpmsp16pp80": "Back to home page", + "5mpmsp16pxs0": "Cluster Operation and Maintenance", + "5mpmsp16q5o0": "Installation failed, you can try again after resolving the related issues", + "5mpmsp16qhc0": "Do not perform other operations during installation", + "5mpmsp16qr80": "Otherwise, the installation will fail", + "5mpmsp16qzc0": "Retry" + }, + "InstallConfig": { + "else1": "rootPassword", + "else2": "Please enter the correct port number", + "else3": "Empty data in the installation directory is detected", + "else4": "data in the installation directory is not empty", + "else5": "At least 8 characters, including at least three of the upper case letters (A-Z), lower case letters (a-z), digits (0-9), and non-alphanumeric characters", + "else6": "InstallPackage path", + "else7": "Please enter the installPackage path", + "else8": "The installation package path is not empty", + "else9": "Environment separation file path", + "else10": "Please enter the environment separation file path", + "else11": "Environmental separation or not", + "else12": "Installation directory refers to the root directory for installing openGauss, such as /opt/openGauss", + "else13": "Important: Be sure to confirm that you have followed the reference link (https://docs-opengauss.osinfra.cn/zh/docs/latest/docs/BriefTutorial/%E8%B5%84%E6%BA%90%E6%B1%A0%E5%8C%96.html) to complete all the configuration work, otherwise the installation will fail.", + "else14": "Disabling environment separation may cause environment conflicts when deploying multiple clusters", + "5mpmu0lapic0": "Node configuration", + "5mpmu0laqc80": "Cluster ID", + "5mpmu0laqiw0": "Please enter the cluster ID", + "5mpmu0laqow0": "Host IP address", + "5mpmu0laqss0": "Please select the host IP address.If there is no host information, please go to the Resources -> Server interface to add the host information.", + "5mpmu0laqwo0": "Please enter the root password", + "5mpmu0lar0c0": "Install user", + "5mpmu0lar480": "Installation directory", + "5mpmu0lar800": "Please enter the installation directory", + "5mpmu0laran0": "Edit the data path", + "5mpmu0larbb0": "If the data path is not edited, it defaults to being concatenated from the installation path.", + "5mpmu0larsl0": "Example of data path:/opt/openGauss/data/single_node", + "5mpmu0larbk0": "Data directory", + "5mpmu0larew0": "Please enter the data directory", + "5mpmu0larj40": "port number", + "5mpmu0larmo0": "Please enter the port number", + "5mpmu0larq40": "Database username", + "5mpmu0larto0": "Please enter the database user name", + "5mpmu0larx00": "Database password", + "5mpmu0las0k0": "Please enter the database password", + "5mpmu0las4g0": "The cluster ID already exists, please re-enter", + "5mpmu0las800": "Determine whether there is an error in the cluster ID" + }, + "InstallPrompt": { + "5mpmunt3eco0": "master", + "5mpmunt3ezw0": "Node ip", + "5mpmunt3f5k0": "Host IP address", + "5mpmunt3f9s0": "Installation directory", + "5mpmunt3fdc0": "Data directory", + "5mpmunt3fjc0": "port number" + }, + "SimpleInstall": { + "else0": "Deployment Mode", + "else1": "Configuration", + "else2": "prompt", + "else3": "Execute ", + "5mpmv5d5bmo0": "Install", + "5mpmv5d5cyk0": "Import", + "5mpmv5d5dak0": "Environmental Monitoring" + } + }, + "install": { + "Install": { + "5mpn60ejqkg0": "Previous step", + "5mpn60ejriw0": "Next step" + } + }, + "dtCluster": { + "index": "Disaster Tolerance Cluster Installation", + "exeInstall": "Execute Install", + "installConstraints": "Installation Constraints", + "installConstraint1": "A single cluster used to build a resource pooled disaster recovery cluster needs to meet the following constraints:", + "installConstraint2": "1) Each single cluster needs to be associated with different Dorado magnetic arrays, and the magnetic arrays need to be able to access each other.", + "installConstraint3": "2) Each single cluster can only have one xlog volume, and it is necessary to establish synchronous replication relationships between xlog disks on multiple clusters. The above operations need to be completed on DeviceManager.", + "installConstraint4": "3) The capacity of xlog disks with synchronization relationships should be the same.", + "dtClusterName": "Disaster Tolerance Cluster Name", + "dtClusterType": "Disaster Tolerance Cluster Type", + "resourcePoolingDt": "Resource Pooling Disaster Tolerance", + "tranditionalDt": "Tranditional Disaster Tolerance", + "replicationType": "Replication Type", + "storageReplication": "Storage Replication", + "networkReplication": "Network Replication", + "cluster": "Cluster", + "diskArray": "Disk Array", + "selectCluster": "Please select a cluster", + "selectDiskArray": "Please select a disk array", + "clusterInstallation": "Cluster Installation", + "diskArrayManagement": "Disk Array Management", + "inputDtClusterName": "Please input disaster tolerance cluster name", + "selectDtClusterType": "Please select a disaster tolerance cluster type", + "selectReplicationType": "Please Select a replication type", + "refresh": "Refresh", + "nextStep": "Next Step", + "lastStep": "Last Step", + "installing": "Installing, do not perform other operations during the installation, the installation will fail otherwise", + "installFailure": "Installation failed, you can try again after resolving the related issues", + "installSuccess": "Congratulations on successful installation", + "goHome": "Back to home page", + "downloadLog": "Download Log", + "retry": "Retry", + "dtClusterManagement": "Disaster Tolerance Cluster Management", + "operations": "You can do the following", + "switchover": "Primary/standby switchover", + "releaseDt": "Release Disaster Tolerance", + "clusterName": "Cluster Name", + "currentStatus": "Current Status", + "available": "Available", + "unavailable": "Unavailable", + "degraded": "Degraded", + "fullBackup": "Full backup", + "backupFail": "Backup failed", + "archive": "Archive", + "archiveFail": "Archive failed", + "switchovering": "Switchover", + "restore": "Restore", + "restoreFail": "Restore failed", + "recovery": "Recovery", + "recoveryFail": "Recovery failed", + "promote": "Promote", + "promoteFail": "Promote failed", + "nodeNum": "Number of nodes", + "singleNode": "Single Node", + "primary": "Primary", + "standby": "Standby", + "dbUser": "Database User", + "dbPort": "Database Port", + "envVarPath": "Environment Variable Path", + "primaryNodeIp": "Primary Node IP", + "standbyNodeIp": "Standby Node IP", + "unknown": "Unknown", + "illegalCharacters": "Please delete illegal characters", + "empty": "No disaster tolerance cluster information currently", + "noSameCluster": "Please select different cluster for pimary and standby", + "noSameDiskArray": "Please select different disk array for pimary and standby" + }, + "diskArray" : { + "index": "Disk Array", + "create": "Create", + "update": "Update", + "diskArrayName": "Disk Array Name", + "hostIp": "Host IP", + "port": "Port", + "username": "Username", + "password": "Password", + "pairId": "Pair Id", + "operations": "Operations", + "modify": "Modify", + "delete": "Delete", + "cancel": "Cancel", + "ok": "OK", + "confirmDelete": "Do you confirm to delete this disk array?", + "connectivityTest": "Connectivity Test", + "available": "Available", + "unavailable": "Unavailable", + "inputName": "Please input disk array name", + "inputHostIp": "Please input host IP", + "inputPort": "Plekase input port", + "inputUsername": "Please input username", + "inputPassword": "Please input password", + "inputPairId": "Please input pair id", + "illegalCharacters": "Please delete illegal characters", + "duplicatedName": "The name already exists, please modify it", + "wrongHostIp": "Please input right IP address", + "wrongPort": "Please input right port", + "currentStatus": "Current Status", + "success": "Success" + }, + "migrate": { + "index": { + "5mpn6g8g9680": "Under development, please wait" + } + }, + "simpleInstall": { + "index": { + "else1": "Minimalist Edition", + "else2": "rootPassword", + "else3": "Minimalist single point has been installed", + "else4": "You can choose another version to install", + "else5": "Version", + "else6": "Select the database version", + "5mpn813gtvs0": "Installing a minimalist version of openGauss", + "5mpn813guf00": "Host IP", + "5mpn813gukw0": "Please select the host IP address", + "user": "user", + "userPlaceholder": "Please select user", + "5mpn813gupc0": "Please enter the root password", + "5mpn813gut00": "One-click installation", + "5mpn813guxc0": "Installing", + "5mpn813gv880": "You are installing openGauss Minimalist", + "5mpn813gvc40": "to", + "5mpn813gvfw0": "Installation failed, you can try again after resolving the problem", + "5mpn813gvjk0": "Host information", + "5mpn813gvmw0": "Retry", + "5mpn813gvqo0": "Congratulations on your successful installation", + "5mpn813gvu40": "Please keep the user name and password properly", + "5mpn813gvxg0": "Database user", + "5mpn813gw100": "Database password", + "5mpn813gw4c0": "Back to home page", + "5mpn813gw7w0": "Cluster operation and maintenance", + "5mpn813gwbk0": "Please select the host.If there is no host information, please go to the Resources -> Server interface to add the host information." + } + }, + "upgrade": { + "index": { + "5uunxx6pdzg0": "Upgrade configuration", + "5uunxx6pf5w0": "Env check", + "5uunxx6pfic0": "Execute upgrade", + "5uunxx6pftc0": "back", + "5uunxx6pg380": "next" + }, + "UpgradeConifg": { + "5uuo00svvrc0": "Select the enterprise cluster that you want to upgrade", + "5uuo00svwnk0": "Please enter a cluster name", + "5uuo00svwu80": "Searching for available Enterprise cluster", + "5uuo00svwy40": "Please enter the root password", + "5uuo00svx1g0": "Enterprise cluster not retrieved", + "5uuo00svx580": "Cluster installation", + "5uuo00svx8k0": "Collecting the upgrade version", + "5uuo00svxc00": "Upgrade target", + "5uuo00svxfo0": "Single node", + "5uuo00svxlo0": "Select at least one enterprise cluster to upgrade", + "5uuo00svxpc0": "Please select a target version to upgrade", + "else1": "ClusterName", + "else2": "NodeNumber", + "else3": "XmlPath", + "else4": "InstallOS", + "else5": "MasterRootPwd", + "else6": "One Master", + "else7": "slave", + "5uuo00svx1g1": "upgrade package not found", + "5uuo00svx1g2": "upgrade package" + }, + "EnvCheck": { + "5uuo4bwfpzg0": "Upgrade to", + "5uuo4bwfr6k0": "retest", + "else1": "Cluster name", + "else2": "Hard disk usage", + "5uuo4bwfr6k1": "cluster {clusterId} master node {ip} has not enough disk space,Unable to upgrade" + }, + "ExeUpgrade": { + "5uuo8zf4cfw0": "Expand all", + "5uuo8zf4dn40": "Put away all", + "5uuo8zf4dxo0": "Batch submit upgrade", + "5uuo8zf4e4w0": "Batch rollback", + "5uuo8zf4ebw0": "Upgrade to", + "5uuo8zf4ej80": "Upgrading", + "5uuo8zf4eq40": "Rolling back", + "5uuo8zf4eww0": "submitting", + "5uuo8zf4f480": "Download log", + "5uuo8zf4fao0": "Start upgrade", + "5uuo8zf4fmg0": "Submit upgrade", + "5uuo8zf4ftc0": "Reset", + "5uuo8zf4fzg0": "Upgrage rollback", + "5uuo8zf4g600": "Prepare Package", + "5uuo8zf4gc40": "Open GUC Parameter", + "5uuo8zf4gi00": "Check cluster status", + "5uuo8zf4gs80": "Execute PreInstall", + "5uuo8zf4gz40": "Execute Upgrade", + "5uuo8zf4h580": "Upgrade verification", + "5uuo8zf4hbk0": "Please click the Start Upgrade button to upgrade again", + "5uuo8zf4hk40": "Performing", + "5uuo8zf4hq00": "The upgrade progress is 100% to be submitted", + "5uuo8zf4hvk0": "Upgrade completed", + "5uuo8zf4i0s0": "Upgrade failure", + "else1": "Cluster name" + }, + "ClusterControlDlg": { + "5uuobb2rd640": "Control console" + } + }, + "packageManage": { + "index": { + "else1": "CPU Arch", + "else2": "All", + "else3": "Please select a version name", + "5myq5c8yz7c0": "Add Package", + "5myq5c8z8540": "search...", + "5myq5c8zmbk0": "Update", + "5myq5c8zms40": "Are you sure you want to delete it?", + "5myq5c8zn100": "Confirm", + "5myq5c8zn7k0": "Cancel", + "5myq5c8znew0": "Delete", + "5myq5c8zpu85": "Name", + "5myq5c8zpu83": "Package Type", + "5myq5c8znkk0": "Owning System", + "5myq5c8zns00": "Version Name", + "5myq5c8zp680": "Version Number", + "5myq5c8zpu80": "Download Url", + "5myq5c8zpu82": "Package Path", + "5myq5c8zpu84": "Remark", + "5myq5c8zq380": "Operation", + "5myq5c8zu5w0": "Enterprise", + "5myq5c8zuxc0": "Lite", + "5myq5c8zw680": "Mini", + "5myq5c8zw681": "Back" + }, + "AddPackageDlg": { + "else1": "CPU Arch", + "else2": "Please fill in the correct address", + "5myq6nnea2w0": "Owning System", + "5myq6nneap40": "Select the owning system", + "5myq6nneap41": "Select the Package Type", + "5myq6nneaw40": "Select the CPU Arch", + "5myq6nneb180": "Version Name", + "5myq6nneb5w0": "Select the Version Name", + "5myq6nnebag0": "Version Number", + "5myq6nnebew0": "Select the Version Number", + "5myq6nnebis0": "Download Address", + "5myq6nnebn40": "Input the Online Download Address", + "5myq6nnebn41": "Offline Package", + "5myq6nnebn42": "Upload the Offline Package", + "5myq6nnebn43": "Remark", + "5myq6nnebn44": "Please input remark", + "5myq6nnebrc0": "Add Package", + "5myq6nnebwo0": "Update Package", + "5myq6nnec400": "Enterprise", + "5myq6nnec8c0": "Lite", + "5myq6nnecc40": "Mini", + "5myq6nnecc41": "Confirm Close", + "5myq6nnecc42": "You have uploaded a file. If you cancel at this time, the uploaded file cannot be deleted. You can delete the file first and then close it", + "5myq6nnecc43": "Please enter online address or add offline installation package", + "5myq6nnecc44": "Please enter online address", + "5myq6nnecc45": "Confirm Deletion", + "5myq6nnecc46": "?Once deleted, the installation package file cannot be recovered", + "5myq6nnecc47": "You can upload any file as an installation package", + "5myq6nnecc48": "Current upload path is: ", + "5myq6nnecc49": ", You can change this setting in System Setting", + "5myq6nnecc50": "Please select a install package to upload", + "5myq5c8zpu93": "Name", + "5myq5c8zpu94": "Package name duplicated", + "5myq5c8zpu95": "please input package name" + } + }, + "modeling": { + "dy_common": { + "saveSuccess": "Saved successfully!", + "noResult": "No Result", + "saveFailed": "Save failed!", + "sqlyunxing": "SQL statement", + "saveAndClose": "Save configuration and close", + "AutomaticallySaved": "Automatically Saved", + "AutomaticallySaving": "Automatically Saving", + "page_empty": "Under development, stay tuned", + "xConfig": "X axis configuration", + "yConfig": "Y axis configuration", + "xFB": "X-axis distribution", + "xConfigMessage": "Please select the X-axis configuration", + "yConfigMessage": "Please select the Y-axis configuration", + "warning": "Warning", + "warningBatchDelete1": "Are you sure you want to delete these ", + "warningBatchDelete2": "streams", + "batchDelete": "Batch delete", + "deleteText": "Delete", + "deleteNotice": "Are you sure you want to delete?", + "paste": "paste", + "isNo": "is no", + "heatmapConfig": { + "notice1": "Please select the statistical indicator field!", + "notice2": "Please select the X-axis distribution field!", + "notice3": "Please select the Y-axis configuration field!" + }, + "chartTips1": "Statistical Dimensions: On the basis of the fields selected on the X-axis, grouping of multiple dimensions can be configured here again. Each dimension can be set to only take the first N items to avoid too many items and the display effect is not good For example: After selecting the brand field (brand_type) on the X-axis, but want to see the difference in the sales of gifts and non-gifts in different brands, you can add an item in the statistical dimension and select whether the gift field (present).\n \nCustomized dimension: You can classify all the enumeration values of a certain field through custom configuration, and form a new statistical dimension. The method of using this statistical dimension is the same as that of regular fields. After saving, it can be used in any graph in this operator.", + "chartNotice1": "Please select the first", + "chartNotice2": "The type of statistical dimension!", + "chartNotice3": "Please select [Article", + "chartNotice4": "Number of subcharts]", + "geoMapLabelColor0": "GeoMapLabelColor0", + "geoMapLabelColor1": "Thermal low value", + "geoMapLabelColor2": "Median thermal value", + "geoMapLabelColor3": "Heat High Value", + "visualEdit": { + "mapTitle1": "Map parameters (read-only)", + "showNum": "Whether to display the value", + "showNumYes": "Yes", + "showNumNo": "No", + "snapshotBtnText": "Go to snapshot list", + "snapshotSuccessNotice": "You can click the visual report above the workspace to view historical snapshots" + }, + "detail": { + "copy": "Copy", + "dataSourceDesc": "1. The data source operator has only one drop-down box configuration item, which is used to specify the configured cluster/node/database/schema information in the integrated platform.\n\n2. The data source operator can point to multiple A basic operator, and can only point to the basic operator, all the basic operators connected to it will execute SQL in the database configured by the data source operator when performing operations such as adding, deleting, modifying, etc.\n\n3. The data source operator is not a necessary operator. If a basic operator does not have a corresponding data source operator connected to it, the default data source configured when creating a new data flow will be used for SQL execution.\n\n4. If you modify a The data source operator that has been connected may cause all subsequent operators connected to it to be unavailable. For example, after modifying the data source, the table name and fields in the previous database do not exist in the new database. Therefore, after modifying When the data source operator is used, the user will be prompted for a second confirmation, and the user is required to check whether the configuration of the subsequent operator is available.", + "queryDesc": "1. The configuration of the query operator includes a mandatory drop-down box - table name, from which the user can select all the table names contained in the data source.\n\n2. The query operator can be used by clicking The plus sign configures the extraction field, the effect is equivalent to `SELECT goods_name , brand_type , goods_model from xxx_list`, if no extraction field is configured, all fields will be extracted by default, the effect is equivalent to `SELECT * from xxx_list`. 3. Below the query operator, you can Multiple combination operators can be connected, but other basic operators cannot be connected. In the subsequent description, [the sequence of basic operators is the query operator] refers to a series of operator combinations that start with the query operator as a certain operator flow. ", + "insertDesc": "1. The configuration of the insert operator includes a mandatory drop-down box - table name, from which the user can select all the table names contained in the data source.\n\n2. The insert operator can be clicked【 Add one] button to add multiple insertion data.\n\n3. Multiple combination operators can be connected under the insertion operator, but other basic operators cannot be connected. In the subsequent description [the basic operator is the sequence of the insertion operator ] That is, a series of operator combinations with the insertion operator as the beginning of an operator stream.", + "updateDesc": "1. The configuration of the update operator includes a mandatory drop-down box - table name, from which the user can select all the table names contained in the data source.\n\n2. The update operator can be clicked The plus sign configures one or more update fields. There are two update methods. The first is a custom statement by default, which updates the value of the specified field to the content in the input box. The other is the field mode, which can use other fields in the table Field to perform update operations, by selecting the drop-down box (original value, splicing) behind the post-field, the original value of the post-field can be overwritten or spliced to update the target field.\n\n3. The update operator generally needs matching conditions Operators, if the update operator is not connected to any conditional operator, the entire table will be updated.\n\n4. Multiple combination operators can be connected under the update operator, but other basic operators cannot be connected. Follow-up In the description [the basic operator is a sequence of update operators], it refers to a series of operator combinations with the update operator as the beginning of a certain operator flow.", + "deleteDesc": "1. The configuration of the delete operator includes a mandatory drop-down box - table name, from which the user can select all the table names contained in the data source.\n\n2. Deleting operators generally require matching conditions The operator is used. If the update operator is not connected to any conditional operator, the entire table data will be deleted.\n\n3. Multiple combination operators can be connected under the delete operator, but other basic operators cannot be connected. Follow-up In the description [the basic operator is a sequence of delete operators], it refers to a series of operator combinations starting with a delete operator.", + "conditionDesc": "1. The configuration of the condition operator must contain at least one or condition, and each or condition must contain at least one or more and conditions.\n\n 2. Each condition consists of 3 parts, field, Condition type and value. Among them, the condition type and value are manually selected or entered by the user, and the field will obtain the fields of all tables from the operators connected upstream and downstream of the condition operator. For example: in query operator-condition operator-bridge operator In such a connection sequence, the query table selected by the query operator is USER, and the bridging operator bridges the ROLE table in, then in the conditional operator, the fields that can be selected will include all the fields of the USER table and the ROLE table at the same time.\n\n3. Multiple conditional operators can be connected at the same time, and they will take effect at the same time.", + "joinDesc": "1. The configuration of the bridge operator includes the name of the table that needs to be bridged. The user can select all the table names contained in the data source from the drop-down box.\n\n2. The bridge operator needs to select the bridge type, the bridge type Including inner join, left outer join, right outer join, full outer join, and bridging conditions. Corresponding to `join, left join, right join, full join` four syntaxes.\n\n3. The selection of bridging conditions consists of 3 drop-downs The first is the table field selected in the basic operator, the second is the condition type, generally select equal to, and the third is the field in the bridging table. As shown in the figure, the following join statement will be obtained : `left join goods_type on ( modeling_test_data_sales_list.brand_type = goods_type.type )` \n\n4. Multiple bridging operators can be connected at the same time, and they will take effect at the same time.\n\n5. After connecting bridging operators, other combinations will be made When an operator selects a configuration field, it can use the fields in the bridging table, such as the conditional operator. As can be seen in Figure 2, after using the bridging operator, the drop-down box (1 ) appears the table name (gray font) of the bridge table, and the (2) below the table name is the field owned by the bridge table.", + "sortDesc": "1. The configuration of the sorting operator must contain at least one or more field sorting settings.\n\n2. Each sorting condition consists of 2 parts, field and sorting type. The field will be selected from the operator sequence Select from all the included fields, and the sorting types are divided into three types, ascending order, descending order, and Chinese pinyin descending order.\n\n3. Among them, the Chinese pinyin descending order option requires the database to have corresponding format support. For details, please refer to the openGauss manual.\n\n4. Multiple sorting operators can be connected at the same time, and will take effect at the same time.", + "limitDesc": "1. The configuration of the limit operator consists of two input boxes, skipping the first N records, and only extracting the first M records.\n\n2. The syntax results obtained by configuring as shown in the figure are as follows: `select * from modeling_test_data_sales_list limit 1000 offset 100;`", + "groupDesc": "1. Both the grouping operator and the aggregation operator are combined operators. These two operators need to be used together, so they are explained together.\n\n2. The grouping operator needs to be configured with at least one or more Fields group data, that is, GROUP BY syntax. The configuration of each aggregation field includes three parts, field name, aggregation type, and alias.\n\n3. Use the aggregation operator after the grouping operator to group the data Other fields in the data perform aggregation function operations. Currently, three types of aggregation are supported, summation, maximum value, and average value, corresponding to the syntax `SUM(), MAX(), AVG()`\n\n4. Group calculation Sub and aggregate operators should be used in sequences whose base operator is the query operator.", + "polymerizationDesc": "1. Both the grouping operator and the aggregation operator are combination operators. These two operators need to be used together, so they are explained together.\n\n2. At least one or more grouping operators need to be configured Fields group data, that is, GROUP BY syntax. The configuration of each aggregation field includes three parts, field name, aggregation type, and alias.\n\n3. Use the aggregation operator after the grouping operator to group the data Other fields in the data perform aggregation function operations. Currently, three types of aggregation are supported, summation, maximum value, and average value, corresponding to the syntax `SUM(), MAX(), AVG()`\n\n4. Group calculation Sub and aggregate operators should be used in sequences whose base operator is the query operator.", + "mappingDesc": "1. The configuration of the mapping operator contains at least one or more mapping rules. Each rule consists of 3 parts, the original field, the mapping type (currently only as), and the mapping alias.\n\n2. The mapping operator The sub is only effective in the sequence where the basic operator is the query operator.\n\n3. The syntax result obtained from the above configuration is as follows: `select goods_name as g_name` ...", + "dictionaryDesc": "1. The configuration of the dictionary operator includes 4 drop-down box options, which are the dictionary table name, the original field, the matching field, and the escape field.\n\n2. For example, the dictionary table goods_type contains Each product name (name), product type (type), and the result of the main query only contains the product name (goods_name), in this case we want the product type to appear in the query results, not the product name , then we can use the dictionary operator to complete the dictionary escape function. In the dictionary operator, we first select the table name (goods_type) of the dictionary table, then select the original table field (goods_name) to be replaced, and then match In the field drop-down box, select the field (name) that corresponds to (goods_name) in the dictionary table. So far, the matching between the dictionary table and the original field has been completed. Finally, in the escape field, select the field that needs to be replaced from the dictionary table That’s it, here we select the product type (type).\n\n3. The escaping process of the dictionary operator is completed by the left join syntax. The complete statement obtained from the above configuration results is as follows: `select *,\"goods_type\".type as goods_name from modeling_test_data_sales_list left join \"goods_type\" on \"goods_type\".name = goods_name ;`" + }, + "panel": { + "datePlaceholder": "Please select a date", + "queryTip1": "If you don't choose to extract any fields, all fields will be extracted, which is equivalent to SELECT * FROM.", + "updateTip": "If the condition operator is not connected, the entire table data will be updated.", + "sortTip1": "Sorting operator field selection supports alias, and the alias field will be marked with [as]" + }, + "header": { + "gridText": "Toggle grid background" + } + }, + "table": { + "index": { + "5mpu30zsqys0": "Cut", + "5mpu30zsrj00": "Copy", + "5mpu30zsroc0": "Delete", + "5mpu30zsrrw0": "Rename", + "5mpu30zsrvc0": "Reset Properties", + "5mpu30zss2o0": "table" + } + }, + "components": { + "Fields": { + "5mpu393tc580": "Add field", + "5mpu393tcr00": "Insert Field", + "5mpu393td080": "Delete field", + "5mpu393td3o0": "Primary Key", + "5mpu393tdcs0": "Move up", + "5mpu393tdfo0": "Move down", + "5mpu393tdi40": "Name", + "5mpu393tdk80": "Type", + "5mpu393tdpo0": "Length", + "5mpu393tdsk0": "Decimal point", + "5mpu393tdus0": "not null", + "5mpu393tdyc0": "Virtual", + "5mpu393te100": "Comment" + }, + "Rename": { + "5mpu3fm9iw00": "Rename", + "5mpu3fm9jbw0": "Submit", + "5mpu3fm9jf40": "Cancel", + "5mpu3fm9jhc0": "table name" + }, + "VisualConfig": { + "5mpu5vnytdc0": "One axis data source", + "5mpu5vnytu40": "Please select a data source for one axis", + "5mpu5vnytxk0": "Two axis data source", + "5mpu5vnytzw0": "Please select the two-axis data source", + "5mpu5vnyu200": "Chart Type", + "5mpu5vnyu4g0": "Please select a chart type", + "5mpu5vnyu6k0": "Add configuration group", + "5mpu5vnyu8g0": "Warehouse Name", + "5mpu5vnyuak0": "Outbound Quantity", + "5mpu5vnyuck0": "Incoming Quantity", + "5mpu5vnyuek0": "Yearly Total", + "5mpu5vnyugo0": "Histogram", + "5mpu5vnyuig0": "Pie Chart" + }, + "CU": { + "5m6g1w13gjo0": "Submit", + "5m6g1w13gz40": "Cancel", + "5m6g1w13h2w0": "Data stream name", + "5m6g1w13h7c0": "Please enter a stream name", + "5m6g1w13ha80": "Access database by default", + "5m6g1w13hdo0": "Please select the default access database", + "5m6g1w13hgc0": "Label", + "5m6g1w13hiw0": "Please enter a label", + "5m6g1w13hlc0": "Query", + "5m6g1w13hno0": "Update", + "5m6g1w13hps0": "Insert", + "5m6g1w13hso0": "Delete", + "5m6g1w13hv40": "Add User", + "5m6g1w13hyo0": "Remarks", + "5m6g1w13i180": "Please enter a note", + "5m6g1w13i3g0": "Administrative Permissions", + "5m6g1w13i640": "Development Permission", + "5m6g1w13i8k0": "Guest Permissions", + "5m6g1w13iaw0": "New Data Stream", + "5m6g1w13id80": "Please enter the stream ID", + "5m6g1w13ihs0": "Please select behavior", + "5m6g1w13ik00": "Please enter a user", + "5m6g1w13ing0": "Modify data stream", + "submitok1": "Create successfully!", + "submitok2": "Modified successfully!" + }, + "CUUser": { + "5m78jvncojc0": "Add", + "5m78jvncp7o0": "Cancel", + "5m78jvncpc80": "Developer (testing, not communicating)", + "5m78jvncpfc0": "Tester (testing, not communicating)", + "5m78jvncpi40": "Add User", + "5mpu44y3ic40": "Add", + "5mpu44y3iso0": "Cancel", + "5mpu44y3ivw0": "Add User" + }, + "Header": { + "5m78qbczrv40": "Operator Menu", + "5m78qbczsmk0": "Save", + "5m78qbczsqw0": "Delete selected", + "5m78qbczsto0": "Undo", + "5m78qbczswg0": "Restore", + "5m78qbczszg0": "Zoom in", + "5m78qbczt2k0": "Zoom out", + "5m78qbczt4w0": "Centered", + "5m78qbczt7g0": "Import", + "5m78qbczt9s0": "Export", + "5m78qbcztdc0": "Export as JSON file", + "5m78qbcztg00": "Export as SVG", + "5m78qbcztio0": "Export as JPEG", + "5m78qbcztlk0": "Export as PNG", + "5m78qbcztok0": "View Code", + "5m78qbcztqs0": "Run", + "5m78qbcztws0": "Visualization", + "5m78qbcztzk0": "Export JSON file_", + "5m78qbczu200": "Export as SVG_$", + "5m78qbczu500": "Export as SVG_", + "5m78qbczu8c0": "Import failed, please check the file content!" + }, + "ScatterConfig": { + "5m7iiczn8k80": "Statistics", + "5m7iiczn92w0": "Unit", + "5m7iiczn97w0": "Geolocation Configuration", + "5m7iiczn9b80": "Coordinate Field", + "5m7iiczn9dw0": "Latitude and longitude offset", + "5m7iiczn9gk0": "Longitude", + "5m7iiczn9j80": "Latitude", + "5m7iiczn9m40": "Scaling", + "5m7iiczn9ow0": "Before highlight", + "5m7iiczn9rk0": "Article", + "5m7iiczn9u40": "Coordinate Type", + "5m7iiczn9wk0": "Upload map json", + "5m7iiczn9zg0": "Coordinate System", + "5m7iiczna1w0": "Are you sure you want to delete?", + "5m7iiczna4o0": "OK", + "5m7iiczna740": "Cancel", + "5m7iiczna9k0": "Delete", + "5m7iicznac00": "Mapping Relationship", + "5m7iicznaec0": "Latitude and longitude", + "5m7iicznags0": "Upload JSON map file", + "5m7iicznak00": "Upload", + "5m7iicznam80": "Map name", + "5m7iicznaok0": "Please enter a map name", + "5m7iicznaqs0": "File", + "5m7iicznask0": "Deleted successfully!", + "5m7iicznaug0": "Please drag and drop the file to upload", + "5m7iicznawk0": "Only geo map files in json format are allowed to be uploaded!", + "5m7iicznayc0": "Uploaded successfully!", + "5mjcl4pcs700": "Field", + "5mjcl4pct500": "Type", + "5mjcl4pctbc0": "Please select a coordinate field", + "5mjcl4pctfw0": "Please select a coordinate system", + "5mjcl4pctqg0": "Please select the statistical indicator field!", + "5mjcl4pcu280": "Please select the statistical indicator type!", + "5mjcl4pcu7c0": "Please select the coordinate field!", + "5mjcl4pcuec0": "Please enter the full latitude and longitude offset!", + "5mjcl4pcuk00": "Please select a coordinate system!" + }, + "PieConfig": { + "5m7ij2mkd0w0": "Statistics", + "5m7ij2mkdyw0": "Unit", + "5m7ij2mke7w0": "Statistics Dimension", + "5m7ij2mkeas0": "Custom Dimensions", + "5m7ij2mkeek0": "Take the front", + "5m7ij2mkejs0": "Display method", + "5m7ij2mkemg0": "Nested Dimensions", + "5m7ij2mkepc0": "Arrange", + "5m7ij2mkes80": "Impression Percentage", + "5m7ij2mkeuo0": "Yes", + "5m7ij2mkex40": "No", + "5mpu2f4wcy80": "field", + "5mpu2f4wdd40": "Type" + }, + "LineConfig": { + "5mpu292dkhc0": "field", + "5mpu292dky40": "Type", + "5m7ijyex0ts0": "According to dimension", + "5m7ijyex1h80": "Arrangement", + "5m7ijyex1m00": "By Time Dimension", + "5m7ijyex1oo0": "Specify time field", + "5m7ijyex1r80": "Time Granularity", + "5m7ijyex1ts0": "Comparison Method", + "5m7ijyex1x40": "Statistics", + "5m7ijyex2040": "Unit", + "5m7ijyex22w0": "Statistics Dimension", + "5m7ijyex2540": "Custom Dimension", + "5m7ijyex29k0": "Before taking", + "5m7ijyex2c00": "Please select the X-axis configuration!", + "5m7ijyex2e80": "Please select the statistical indicator field!", + "5m7ijyex2gg0": "Please select the statistical indicator type!", + "5m7ijyex2jc0": "Please select a statistical indicator unit!", + "5m7ijyex2lk0": "Imperfect configuration" + }, + "HeatmapConfig": { + "5m7ikcw34ic0": "Statistics", + "5m7ikcw35840": "Unit", + "5m7ikcw35e80": "Custom Dimension", + "5m7ikcw35hc0": "Fetch before", + "5m7ikcw35kc0": "Heat Range" + }, + "GraphConfig": { + "5m7ikjwuoig0": "Node Configuration", + "5m7ikjwuphs0": "Node ID", + "5m7ikjwupmw0": "Node Name", + "5m7ikjwupsg0": "Node X coordinate", + "5m7ikjwupv40": "Node Y coordinate", + "5m7ikjwupxw0": "Node size", + "5m7ikjwuq0w0": "Relationship Configuration", + "5m7ikjwuq3c0": "Exit node", + "5m7ikjwuq600": "Incoming node", + "5m7ikjwuq8k0": "Relationship value", + "5mjckw7can80": "Please select a node ID!", + "5mjckw850is0": "Please select a node name!", + "5mjckw8511c0": "Please select the out node!", + "5mjckw8516w0": "Please select the check-in point!", + "5mjckw851bo0": "Please select a relationship value!", + "notice1": "Please select node size", + "notice2": "Please select node X coordinate", + "notice3": "Please select node Y coordinate" + }, + "CustomDimensionDialog": { + "5m7iksonqnk0": "Custom Dimension", + "5m7iksonr9s0": "Save Report", + "5m7iksonrew0": "List of saved dimensions", + "5m7iksonri40": "Untitled", + "5m7iksonrkg0": "Delete", + "5m7iksonros0": "New dimension", + "5m7iksonrrg0": "Dimension name:", + "5m7iksonru00": "Please enter a dimension name", + "5m7iksonrwc0": "Field:", + "5m7iksonryw0": "Please select a field", + "5m7iksons100": "Dimension settings:", + "5m7iksons3g0": "Add category", + "5m7iksons5w0": "Please enter a category name", + "5m7iksons8g0": "Please select a statistics type", + "5m7iksonsak0": "contains", + "5m7iksonscs0": "Does not contain", + "5m7iksonsf40": "Please select a category value", + "5m7iksonshg0": "Save", + "5m7iksonsk00": "Deleted successfully!", + "5m7iksonsmc0": "Saved successfully!", + "5m7iksonspg0": "Category Name", + "5m7iksonsro0": "Statistic Type", + "5m7iksonsu00": "Category Value", + "5m7iksonsvs0": "Operation" + }, + "BarLineConfig": { + "5m7in04pdbs0": "According to dimension", + "5m7in04pe240": "Arrangement", + "5m7in04pe6c0": "By Time Dimension", + "5m7in04pe8s0": "Specify time field", + "5m7in04pebk0": "Time Granularity", + "5m7in04peec0": "Comparison method", + "5m7in04pegw0": "Chart type:", + "5m7in04pej80": "Line Chart", + "5m7in04pemg0": "Histogram", + "5m7in04peoo0": "Statistics", + "5m7in04per40": "Unit", + "5m7in04peu00": "Statistics Dimension", + "5m7in04pewg0": "Custom Dimensions", + "5m7in04pez40": "Take Front", + "5m7in04pf1c0": "Add secondary image", + "5m7in04pf3o0": "There should be at least one secondary picture!", + "5m7in04pf600": "Please select [Article", + "5m7in04pf8o0": "The chart type of a sub-picture]!", + "5m7in04pfas0": "Statistical indicator field of a secondary image]!", + "5m7in04pfdg0": "Statistical indicator type of sub-picture]!", + "5m7in04pfgs0": "Statistical indicator unit of submap]!", + "5m7in04pfis0": "Incomplete configuration" + }, + "BarConfig": { + "5m7insim1uc0": "According to dimension", + "5m7insim2qg0": "Arrangement", + "5m7insim2v40": "By Time Dimension", + "5m7insim2yc0": "Specify Time Field", + "5m7insim3280": "Time Granularity", + "5m7insim3500": "Comparison method", + "5m7insim37w0": "Statistics", + "5m7insim3ak0": "Unit", + "5m7insim3dc0": "Statistics Dimension", + "5m7insim3g00": "Custom Dimensions", + "5m7insim3ig0": "Get Front", + "5m7insim3lc0": "Please select the X-axis configuration!", + "5m7insim3o80": "Please select the statistical indicator field!", + "5m7insim3qo0": "Please select the statistical indicator type!", + "5m7insim3sw0": "Please select the statistical indicator unit!", + "5m7insim3vc0": "The configuration is not perfect" + }, + "Share": { + "5m7ipfzhwh40": "Share link", + "5m7ipfzhx540": "Copy link", + "5m7ipfzhx9g0": "Close", + "5m7ipfzhxd40": "Copy successful!" + }, + "CUSnapshot": { + "5m7iplopyjo0": "Snapshot Details" + }, + "CUReportDrag": { + "5m7ipr5lr5s0": "Edit Report", + "5m7ipr5lrw40": "Save Report", + "5m7ipr5ls0w0": "Basic configuration", + "5m7ipr5ls3k0": "Report Name", + "5m7ipr5ls800": "Please enter a report name", + "5m7ipr5lsao0": "Layout", + "5m7ipr5lsd40": "Please select the layout method", + "5m7ipr5lsfc0": "Free Arrangement", + "5m7ipr5lsho0": "Available Snapshots" + }, + "CUReport": { + "5m7iq3dyy200": "Edit Report", + "5m7iq3dyzf80": "Save Report", + "5m7iq3dyzls0": "Delete", + "5m7iq3dyzps0": "Basic configuration", + "5m7iq3dyzsg0": "Report Name", + "5m7iq3dyzvw0": "Please enter a report name", + "5m7iq3dyzyo0": "Layout", + "5m7iq3dz0140": "Please select the layout method", + "5m7iq3dz0400": "Free Arrangement", + "5m7iq3dz0780": "Available Snapshots", + "5m7iq3dz09k0": "Saved successfully!" + } + }, + "dataflow": { + "index": { + "5m77w0y57io0": "New stream", + "5m77w0y5b3k0": "Enter Stream ID/Name", + "5m77w0y5brg0": "Data stream list", + "5m77w0y5caw0": "Modify", + "5m77w0y5cgg0": "Properties", + "5m77w0y5d8k0": "Copy", + "5m77w0y5dec0": "Share", + "5m77w0y5dio0": "Export", + "5m77w0y5fq00": "Are you sure you want to delete?", + "5m77w0y5g400": "OK", + "5m77w0y5g9k0": "Cancel", + "5m77w0y5ghc0": "Delete", + "5m77w0y5in80": "Data Stream ID/Name", + "5m77w0y5ivk0": "Label", + "5m77w0y5jtg0": "Primary Type", + "5m77w0y5mb80": "Cluster Name", + "5m77w0y5mgc0": "Node ID", + "5m77w0y5n5w0": "Latest update time", + "5m77w0y5qjs0": "Last Updater", + "5m77w0y5qsw0": "Creation Time", + "5m77w0y5rm80": "Creator", + "5m77w0y5rqo0": "Remarks", + "5m77w0y5ruw0": "Operation", + "5m77w0y5rzk0": "Query", + "5m77w0y5s2s0": "Update", + "5m77w0y5s6o0": "Insert", + "5m77w0y5sak0": "Deleted successfully!" + } + }, + "share": { + "index": { + "5m78k584nkw0": "Integrated Platform", + "5m78k584o7w0": "The shared page of the target was not found", + "5m78k584ock0": "No required id" + } + }, + "detail": { + "index": { + "5m7apwiz75o0": "Initializing the artboard....", + "5m7apwiz8b00": "Please pass in the relevant ID in the configuration item" + } + }, + "left-sidebar": { + "index": { + "5m78udtsry80": "Search" + } + }, + "nodes": { + "BaseNode": { + "5m78v3ubttc0": "Cut", + "5m78v3ubuac0": "Copy", + "5m78v3ubuek0": "Delete" + } + }, + "utils": { + "index": { + "5m78yfizcxc0": "The configuration item data is incomplete, please complete it.", + "5m835tc0m9o0": "The target operator of a line cannot be the data source operator", + "5m835tc0n8k0": "The target operator of the line of the data source operator can only be the basic operator", + "5m835tc0ndo0": "The target and source counters of a line cannot both be basic counters", + "5m835tc0nh00": "Basic counters and composite counters can only have one line whose source counter is itself", + "5m835tc0njw0": "Basic counters and combined counters can only have one line whose target counter is itself" + } + }, + "antv": { + "dataSource": "Data Source Operator", + "query": "query operator", + "update": "Update operator", + "delete": "Delete operator", + "insert": "Insert operator", + "condition": "condition operator", + "join": "Bridge operator", + "sort": "sort operator", + "limit": "Limit operator", + "group": "Group operator", + "polymerization": "polymerization operator", + "mapping": "Mapping Operator", + "dictionary": "dictionary operator" + }, + "config-panel": { + "index": { + "5m7adga7to40": "No configuration item" + } + }, + "special": { + "DataSource": { + "5m7adso1d000": "Configuration Parameters", + "5m7adso1dv00": "Data Source", + "5m7adso1e040": "Cluster Name/Node ID/Schema", + "5m7adso1e340": "Please select a table name", + "5m7adso1e580": "If not selected, the default data source connection will be used", + "5m7adso1e7s0": "Confirm the modification", + "5m7adso1eag0": "Cancel modification", + "5m7adso1eeo0": "Warning!", + "5m7adso1ehg0": "After changing the data source, you need to reconfigure the configuration items of all operators related to the previous data source! Otherwise, it will not work!" + } + }, + "base": { + "QueryOperator": { + "5m7hhed6am80": "Configuration parameters", + "5m7hhed94ik0": "Table name", + "5m7hhed94qc0": "Please select a table name", + "5m7hhed94tg0": "Extract field", + "5m7hhed94w80": "Please select a field", + "5m7hhed950s0": "Input source", + "5m7hhed953o0": "Origin", + "5m7hhed95680": "alias", + "5m7hhed95b00": "Please select a source", + "5m7hhed95dw0": "Please enter an alias", + "5m7hhed95go0": "Output configuration", + "5m7hhed95jk0": "Output alias", + "5m7hhed95m00": "Custom Output", + "5m7hhed95og0": "Condition", + "5m7hhed95zg0": "After replacing the selected table, you need to reconfigure the configuration items of all operators related to the current operator! Otherwise, it will not work!", + "5m7hr5m93c80": "Confirm the modification", + "5m7hr5m943c0": "Cancel modification", + "5m7hr5m948k0": "Warning!", + "5m7hr5m94c00": "After replacing the selected table, you need to reconfigure the configuration items of all operators related to the current query operator! Otherwise, it will not work!" + }, + "DeleteOperator": { + "5m7hqy836z40": "Configuration parameters", + "5m7hqy838600": "Table name", + "5m7hqy838bg0": "Please select a table name", + "5m7hqy838eo0": "Extract field", + "5m7hqy838hc0": "Please select a field", + "5m7hqy838kw0": "Confirm the modification", + "5m7hqy838no0": "Cancel modification", + "5m7hqy838q40": "Warning!", + "5m7hqy838t40": "After replacing the selected table, you need to reconfigure the configuration items of all operators related to the currently deleted operator! Otherwise, it will not work!", + "5maolasd5hk0": "If the conditional operator is not connected, all data will be deleted" + }, + "InsertOperator": { + "5m7hqzngdeg0": "Configuration parameters", + "5m7hqzngebw0": "Table name", + "5m7hqzngeho0": "Please select a table name", + "5m7hqzngels0": "Insert Method", + "5m7hqzngeoo0": "Insert one by one", + "5m7hqzngeto0": "Batch insert from input", + "5m7hqzngewg0": "Field", + "5m7hqzngez40": "Please select a field", + "5m7hqzngf200": "Please enter a value", + "5m7hqzngf4k0": "Add one", + "5m7hqzngf7g0": "Enter Configuration", + "5m7hqzngfac0": "Receive Table", + "5m7hqzngfdc0": "Please select a table", + "5m7hqzngfgw0": "OK to modify", + "5m7hqzngfjg0": "Cancel modification", + "5m7hqzngfls0": "Warning!", + "5m7hqzngfoo0": "After replacing the selected table, you need to reconfigure the configuration items of all operators related to the current insertion operator! Otherwise, it will not work!" + }, + "UpdateOperator": { + "5m7hr2f43280": "Configuration parameters", + "5m7hr2f43ro0": "Table name", + "5m7hr2f43ws0": "Please select a table name", + "5m7hr2f43zo0": "Update field list", + "5m7hr2f442c0": "Please select the field to update", + "5m7hr2f44580": "Please select a value type", + "5m7hr2f44a40": "Please enter the value to be modified", + "5m7hr2f44h40": "Please select a field", + "5m7hr2f44o80": "Confirm the modification", + "5m7hr2f44qo0": "Cancel modification", + "5m7hr2f44tg0": "Warning!", + "5m7hr2f44vk0": "After replacing the selected table, you need to reconfigure the configuration items of all operators related to the current update operator! Otherwise, it will not work!", + "5mav1wd54g00": "Original value", + "5mav1wd9z9o0": "Custom statement", + "5mav1wd9zos0": "field", + "5mav1wd9ztw0": "Stitching" + } + }, + "combination": { + "BridgingOperator": { + "5m82a7hpfow0": "Property Configuration", + "5m82a7hpivk0": "table name", + "5m82a7i01wc0": "Please select a table", + "5m82a7i02a80": "Type", + "5m82a7i02f40": "Condition:", + "5m82a7i02kw0": "or", + "5m82a7i02p80": "And condition", + "5m82a7i02t40": "Please select a field", + "5m82a7i02xc0": "Condition", + "5m82a7i039g0": "Condition value or field", + "5m82a7i03lk0": "Inner Join", + "5m82a7i03qs0": "Take Intersection", + "5m82a7i03w00": "Left Outer Join", + "5m82a7i041g0": "Get all the data of the base table, if a row of the base table has no matching row in the join table, all select list columns of the join table in the associated result set row will be null", + "5m82a7i044w0": "Right outer join", + "5m82a7i047k0": "Get all the data in the join table, if a row of the join table has no matching row in the base table, all the select list columns of the base table in the associated result set row are null values", + "5m82a7i04bk0": "Full outer join", + "5m82a7i04ec0": "Get all the data of the base table and link table", + "5m82a7i04k00": "equal to", + "5m82a7i04nk0": "not equal to", + "5m82a7i04ro0": "less than", + "5m82a7i04ug0": "Less than or equal to", + "5m82a7i04x80": "greater than", + "5m82a7i04z80": "Greater than or equal to", + "5m82a7i052o0": "contains", + "5m82a7i054o0": "Does not contain", + "5m82a7i056w0": "is empty", + "5m82a7i058w0": "not empty" + }, + "ConditionOperator": { + "5m82aq4s7qo0": "Configuration parameters", + "5m82aq4s8cw0": "or", + "5m82aq4s8gw0": "and condition", + "5m82aq4s8js0": "Please select a field", + "5m82aq4s8m40": "Condition", + "5m82aq4s8ow0": "Enter...", + "5m82aq4s8rc0": "Add or condition", + "5m82aq4s8ts0": "Inner join", + "5m82aq4s8wc0": "equal to", + "5m82aq4s8yo0": "not equal to", + "5m82aq4s9180": "less than", + "5m82aq4s93k0": "Less than or equal to", + "5m82aq4s95s0": "greater than", + "5m82aq4s98c0": "Greater than or equal to", + "5m82aq4s9ao0": "contains", + "5m82aq4s9cw0": "Does not contain", + "5m82aq4s9fk0": "is empty", + "5m82aq4s9fa1": "In List", + "5m82aq4s9fa2": "Not In List", + "5m82aq4s9i00": "not empty", + "5m82aq4s9fk1": "Length Less Than", + "5m82aq4s9fk2": "Length Longer Than" + }, + "PolymerizationOperator": { + "5m82b2nqsa00": "Attribute Configuration", + "5m82b2nqt0k0": "Aggregated fields (need to be used with grouping operators)", + "5m82b2nqt4k0": "Please select a field", + "5m82b2nqt780": "Aggregation method", + "5m82b2nqt9s0": "alias", + "5m82b2nqtcl0": "Default", + "5m82b2nqtck0": "Max value", + "5m82b2nqtck1": "Min value", + "5m82b2nqtf40": "Summation", + "5m82b2nqths0": "Average", + "5m82b2nqths1": "Count", + "5m82b2nqths2": "Extra Year", + "5m82b2nqths3": "Extra Month" + }, + "SortOperator": { + "5m82bdo2r0w0": "Attribute Configuration", + "5m82bdo2rns0": "Sort Field", + "5m82bdo2rs00": "Please select a field", + "5m82bdo2rus0": "Condition", + "5m82bdo2rx80": "Ascending", + "5m82bdo2rzo0": "Descending", + "5m82bdo2s2c0": "Chinese Pinyin descending order" + }, + "DictionaryOperator": { + "5me6g9wp5p40": "Attribute Configuration", + "5me6g9wp6xk0": "Dictionary table", + "5me6g9wp74g0": "Please select a table name", + "5me6g9wp78o0": "Original field", + "5me6g9wp7cg0": "Please select a field", + "5me6g9wp7gw0": "Match field", + "5me6g9wp7lc0": "Escape Field" + }, + "GroupOperator": { + "5me6gg5ogk00": "Attribute Configuration", + "5me6gg5oh8c0": "Group Field", + "5me6gg5ohjk0": "Please select a field" + }, + "MappingOperator": { + "5me6giyzpck0": "Attribute Configuration", + "5me6giyzq4s0": "Field Mapping", + "5me6giyzqds0": "Please select a field", + "5me6giyzqmo0": "Condition", + "5me6giyzqq80": "Enter..." + }, + "RestrictionOperator": { + "5me6gnj6b8s0": "Attribute Configuration", + "5me6gnj6bpc0": "Skip before", + "5me6gnj6bts0": "Article data", + "5me6gnj6c3w0": "Extract only front" + } + }, + "visual-edit": { + "index": { + "5m7ihlv0lrk0": "Close popup", + "5m7ihlv0mig0": "Untitled", + "5m7ihlv0mmo0": "Saved config", + "5m7ihlv0mpc0": "Delete", + "5m7ihlv0ms00": "New chart", + "5m7ihlv0muo0": "Configuration parameters", + "5m7ihlv0mxo0": "Name", + "5m7ihlv0n0c0": "Please enter a report name", + "5m7ihlv0n2s0": "Chart Type", + "5m7ihlv0n5c0": "Please select a chart type", + "5m7ihlv0n7s0": "Preview", + "5m7ihlv0na80": "Save Configuration", + "5m7ihlv0ndo0": "Save Snapshot", + "5m7ihlv0ng80": "Select Chart Type", + "5m7ihlv0niw0": "Edit Visual Information", + "5m7ihlv0nl00": "Saved successfully!", + "5m7ihlv0nnw0": "Successfully saved the snapshot!", + "5m7ihlv0nqc0": "Deleted successfully!", + "5mpsromgvig0": "Please enter the chart type!", + "5mpsromgvxg0": "Please select a chart type!" + } + }, + "visual-list": { + "index": { + "5m7iox8oj6k0": "Visualization", + "5m7iox8ok400": "Snapshot History", + "5m7iox8oka00": "Delete snapshot", + "5m7iox8okdw0": "Report", + "5m7iox8okgs0": "Name Search", + "5m7iox8okmg0": "New report", + "5m7iox8okpk0": "Modify", + "5m7iox8oksk0": "Share", + "5m7iox8okv80": "Delete Report", + "5m7iox8okyc0": "Deleted successfully!", + "5m7iox8ol0w0": "Report Name", + "5m7iox8ol3k0": "Layout Type", + "5m7iox8ol680": "Creation Time", + "5m7iox8ol8o0": "Update Time", + "5m7iox8olb00": "Operation", + "5m7iox8oldc0": "Free Arrangement" + } + }, + "dialogs": { + "Run": { + "5m7iqlci0400": "Run result:", + "5m7iqlci0rk0": "Code preview running", + "5m7iqlci0vk0": "Running...", + "5m7iqlci0y80": "Code Preview", + "5m7iqlci10s0": "Code running" + } + }, + "hooks": { + "index": { + "5m83asj8uws0": "Special operator", + "5m83asj8vok0": "Data Source Operator", + "5m83asj8vsw0": "Basic operator", + "5m83asjg7480": "Query operator", + "5m83asjg7f40": "Update counter", + "5m83asjg7j80": "Field", + "5m83asjg7m80": "Stitching", + "5m83asjg7oo0": "Delete operator", + "5m83asjg7rk0": "Insert operator", + "5m83asjg7u80": "Combination operator", + "5m83asjg7x40": "Conditional operator", + "5m83asjg7zs0": "Bridge operator", + "5m83asjg8280": "Sort operator", + "5m83asjg84w0": "Limit bar operator", + "5m83asjg87c0": "Group operator", + "5m83asjg8ak0": "Aggregation Operator", + "5m83asjg8f40": "Mapping Operator", + "5m83asjg8i00": "dictionary operator" + }, + "visual-edit": { + "5m83coh1cb00": "Summation", + "5m83coh1d3k0": "Average", + "5m83coh1d7c0": "Maximum value", + "5m83coh1d9s0": "Minimum value", + "5m83coh1dcg0": "Median", + "5m83coh1df40": "Histogram", + "5m83coh1dhs0": "Line Chart", + "5m83coh1dk40": "mixed graph", + "5m83coh1dmk0": "Pie Chart", + "5m83coh1dqg0": "Heatmap", + "5m83coh1dso0": "Geographic coordinates", + "5m83coh1dvc0": "Relationship Diagram", + "5m83coh1dxs0": "Year-on-Year", + "5m83coh1e080": "Chain ratio", + "5m83coh1e2g0": "Year", + "5m83coh1e500": "Month", + "5m83coh1e7g0": "Day", + "5m83coh1e9o0": "hour" + } + } + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/locale/locale/zh-CN.ts b/ER-Model/plugins/base-ops/web-ui/src/locale/locale/zh-CN.ts new file mode 100644 index 000000000..ab2c31c93 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/locale/locale/zh-CN.ts @@ -0,0 +1,4 @@ + +export default { + 'common.test': 'test' +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/locale/locale/zh-cn.json b/ER-Model/plugins/base-ops/web-ui/src/locale/locale/zh-cn.json new file mode 100644 index 000000000..a30e60476 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/locale/locale/zh-cn.json @@ -0,0 +1,2199 @@ +{ + "customControl": { + "HostPwdDlg": { + "else1": "root密码", + "else2": "请选择主机信息", + "5mplfut5bj80": "主机信息", + "5mplfut5kuc0": "请输入密码", + "5mplfut5lgo0": "安全检验", + "5mplfut5lus0": "请输入root密码", + "user": "用户", + "userPlaceholder": "请选择用户", + "noDevice": "暂无设备信息" + }, + "index": { + "else1": "暂无主机信息", + "else2": "创建主机", + "else3": "刷新", + "else4": "暂无控制台", + "else5": "打开控制台" + } + }, + "indicators": { + "index": { + "5mpli5dxtx80": "正在开发中,敬请期待" + } + }, + "components": { + "AuditLog": { + "else1": "时间区间", + "5mplidlico80": "查询", + "5mplidlidfs0": "时间", + "5mplidlido80": "类型", + "5mplidlie1k0": "结果", + "5mplidlielo0": "用户ID", + "5mplidlietk0": "用户名称", + "5mplidlif1o0": "数据库", + "5mplidlifdw0": "客户端信息", + "5mplidlifj80": "对象信息", + "5mplidlifnc0": "详细信息", + "5mplidlifr40": "节点信息", + "5mplidlifv40": "线程ID", + "5mplidlig300": "本地端口", + "5mplidlig7w0": "远程端口", + "5mplidligbw0": "获取审计日志出错" + }, + "FileLog": { + "5mplk6504fg0": "正在获取数据, 请稍后", + "5mplk65055s0": "暂无数据" + }, + "ChooseInstallWay": { + "5mpmwx7b1eo0": "请选择要安装方式", + "5mpmwx7b2580": "已选择", + "5mpmwx7b2gg0": "离线安装", + "5mpmwx7b2p00": "在线安装" + }, + "ChooseVersion": { + "5mpmxod8xno0": "请选择要安装的openGauss版本", + "5mpmxod8yh00": "已选择", + "5mpmxod8yr40": "安装模式", + "5mpmxod8yxs0": "极简版", + "5mpmxod8z3w0": "极简版安装的使用主体主要针对高校和个人测试环境,相对企业安装流程更简单快捷。", + "5mpmxod8za00": "轻量版", + "5mpmxod8zg00": "企业版", + "else2": "ShardingProxy+openLooKeng", + "5mpmxod8zlw0": "企业版安装的主要使用主体为企业或对数据库性能要求较高的个人,安装流程比较复杂,功能更全。", + "5mpmxod8zs80": "安装", + "5mpmxod901g0": "导入", + "5mpmxod901g1": "批量导入", + "5mpmxod907k0": "选中的版本", + "else1": "openGauss轻量版在各台机器上分别执行安装操作,因为轻量版版本没有OM,CM等组件,所以安装,卸载,升级等OM功能需要分别在各个节点上手动操作。", + "openLooKeng": "sharding+openLooKeng", + "openLooKengRemark": "openLooKeng是一种\"开箱即用\"的引擎,支持在任何地点(包括地理上的远程数据源)对任何数据进行原位分析。它通过SQL 2003接口提供了所有数据的全局视图。", + "else3": "请注意:5.0.0及以后的版本系统可以区分极简版、轻量版和企业版,之前的版本只能区分企业版和非企业版。请核实您选择的版本和即将导入的版本是否对应" + }, + "ExeImport": { + "5mpmzg3zqu80": "恭喜您导入成功", + "5mpmzg3zrrw0": "请妥善保管用户名密码", + "5mpmzg3zs0o0": "数据库用户", + "5mpmzg3zs6w0": "数据库密码", + "5mpmzg3zseo0": "回到首页", + "5mpmzg3zskw0": "集群运维", + "5mpmzg3zsu80": "导入失败", + "5mpmzg3zt0g0": "正在进行导入", + "5mpmzg3ztak0": "请求发生异常" + }, + "BatchImport":{ + "5mpmzg3zt1o0": "将文件拖至此处 或", + "5mpmzg3zt1o1": "选择文件", + "5mpmzg3zesl0": "仅支持上传Excel格式文件,大小不超过200MB", + "5mpmertfggy0": "模板", + "5mpmertfggy1": "错误报告", + "5mpmzg3zesl1": "导入模板下载", + "5mpmzg3zesl2": "文件上传中", + "5mpmeddf3zl0": "文件上传失败,需上传200M以下文件,请重新上传", + "5mpmeddf3zl1": "文件上传失败,需上传单个文件,请重新上传", + "5mpmeddf3zl2": "文件上传失败,需上传Excel格式的文件,请重新上传", + "5mpmeddf3zl3": ",请修改文件并", + "5mpmeddf3zl4": "文件解析中,请稍等", + "5mpmeddf3zl5": "请不要刷新界面", + "5mpmeddf3zl6": "数据可能会丢失,确定要刷新页面吗?", + "5mpmeddf3zl7": "文件解析失败", + "5mpmzg3zesl3": "重新上传", + "5mpmzg3zesl4": "您仍可以通过拖拽文件到下方区域进行文件替换", + "5mpmzg3zesl5": "删除", + "5mpmzg3zesl6": "文件上传成功", + "5mpmqndzeok0": "确定", + "5mpmqndzeok1": "取消", + "5mpmqndzeok2": "文件解析", + "5mpmqndzeok3": "导入集群", + "5mpmqndzeok4": "导入完成", + "5mpmqndzeok5": "共导入{totalNum}条数据", + "5mpmewrfzse0": "{num}条", + "5mpmewrfzse1": "导入成功,", + "5mpmewrfzse3": "导入失败", + "5mpmewrfzse4": "下载错误报告", + "5mpmewrfzse5": "重新导入", + "5mpmewrfzse6": "完成" + }, + "OfflineInstall": { + "5mpn1nway8c0": "请选择openGauss", + "5mpn1nwaywo0": "要使用的安装包", + "5mpn1nwaz280": "已选择安装包", + "5mpn1nwaz600": "未列出我想要的安装包?点击上传", + "5mpn1nwaz980": "安装包目录", + "5mpn1nwazd40": "请输入安装包目录路径", + "5mpn1nwazgw0": "软件包路径", + "5mpn1nwazks0": "请输入安装目录", + "5mpn1nwazok0": "极简版", + "5mpn1nwazs80": "轻量版", + "5mpn1nwazvg0": "企业版", + "else1": "离线安装需将安装包上传至DataKit所在服务器,该路径指DataKit所在服务器的路径。", + "else2": "路径只能使用Linux路径", + "else3": "正在获取安装包的CPU架构信息,请耐心等待", + "5mpn1nwazvg2": "文件路径:", + "5mpn1nwazvg3": "安装包管理名称:" + }, + "OnlineInstall": { + "else1": "已选择安装包", + "5mpn3mp10hw0": "安装包保存目录", + "5mpn3mp111s0": "下载", + "5mpn3mp117k0": "已下载", + "5mpn3mp11cg0": "下载完成", + "5mpn3mp11g00": "下载进度", + "else2": "正在获取安装包", + "else3": "暂无安装包信息", + "else4": "添加安装包", + "5mpn3mp11g01": "文件扩展名不正确,必须为.tar.gz|.tar|.tar.bz2中的一个" + }, + "OneCheck": { + "clusterName": "集群名称", + "else1": "hash index语法检查", + "else2": "Node group编码格式检查", + "else3": "TD数据库中orc表date类型列检查", + "else4": "检测完成时间", + "else5": "检查项", + "else6": "root密码", + "else7": "请输入root密码", + "5mpiji1q2ng0": "一键自检", + "5mpiji1q3wo0": "请选择集群名称", + "5mpiji1q4a00": "重新检测", + "5mpiji1q4k40": "检测", + "5mpiji1q4tg0": "正在进行一键自检", + "5mpiji1q57w0": "集群", + "5mpiji1q5r40": "检查openGauss状态", + "5mpiji1q5zw0": "检查openGauss参数", + "5mpiji1q6b00": "检查日志级别", + "5mpiji1q8q80": "检查目录权限", + "5mpiji1q96c0": "检查只读模式", + "5mpiji1q9r00": "检查环境变量", + "5mpiji1qa080": "检查系统表膨胀", + "5mpiji1qa940": "关键进程启动时间检测", + "5mpiji1qahc0": "检测环境变量分离文件改动", + "5mpiji1qas40": "数据库", + "5mpiji1qbv80": "检查当前连接数", + "5mpiji1qc780": "检查当前游标数", + "5mpiji1qcjs0": "检查pgxc_group表中需要重分布的个数", + "5mpiji1qczo0": "检查sysadmin用户", + "5mpiji1qdeo0": "检查表空间路径", + "5mpiji1qdlk0": "检查重分布残留的临时表", + "5mpiji1qds80": "系统", + "5mpiji1qdy40": "检查内存总大小", + "5mpiji1qe8s0": "检查CPU核数", + "5mpiji1qehc0": "检查操作系统版本", + "5mpiji1qeoo0": "检查关键进程omm_adj的值", + "5mpiji1qf280": "检查max_process_memory参数设置是否合理", + "5mpiji1qf9k0": "检查编码格式", + "5mpiji1qfgo0": "检查防火墙状态", + "5mpiji1qfnc0": "检查内核版本", + "5mpiji1qfx00": "检查句柄最大设置", + "5mpiji1qg400": "检查NTPD服务", + "5mpiji1qga80": "检查系统参数", + "5mpiji1qgl40": "检查THP服务", + "5mpiji1qgt80": "检查时区一致性", + "5mpiji1qh2k0": "检查sshd服务是否已启动", + "5mpiji1qh8s0": "检查sshd服务配置是否正确", + "5mpiji1qhfs0": "检查crontab服务是否已启动", + "5mpiji1qhn00": "栈深度检查", + "5mpiji1qhto0": "检查ip_local_port_range设置范围", + "5mpiji1qi2s0": "检查超线程是否打开", + "5mpiji1qif40": "检查启动项", + "5mpiji1qioc0": "检查文件句柄", + "5mpiji1qizk0": "检查DropCache进程", + "5mpiji1qjag0": "主机", + "5mpiji1qjh80": "检查磁盘预读块", + "5mpiji1qkn00": "检查磁盘格式参数", + "5mpiji1qkyk0": "检查交换内存", + "5mpiji1ql580": "检查磁盘逻辑块", + "5mpiji1qldc0": "检查IO请求", + "5mpiji1qlmc0": "检查最大异步IO请求", + "5mpiji1qltc0": "检查磁盘使用率", + "5mpiji1qm0s0": "检查磁盘inodes使用率", + "5mpiji1qmbw0": "检查IO配置", + "5mpiji1qmj40": "网络", + "5mpiji1qmqg0": "检查网络通畅", + "5mpiji1qn1c0": "检查网络掉包率", + "5mpiji1qn8g0": "网卡型号和驱动版本一致性检查", + "5mpiji1qnf00": "检查网卡MTU值", + "5mpiji1qnks0": "检查网卡RXTX值", + "5mpiji1qnr00": "检查网卡多队列", + "5mpiji1qnxs0": "本地路由表检查", + "5mpiji1qo440": "项", + "5mpiji1qoe00": "不通过", + "5mpiji1qom40": "内部错误", + "5mpiji1qosc0": "警告", + "5mpiji1qoyw0": "通过", + "5mpiji1qpcc0": "检查类别", + "5mpiji1qpns0": "检查项", + "5mpiji1qpvg0": "检测结果", + "5mpiji1qq180": "查看一键自检返回" + }, + "openLooKeng": { + "5mpiji1qpcb0": "部署名称", + "5mpiji1qpcb1": "请输入部署名称", + "5mpiji1qpcb2": "备注", + "5mpiji1qpcb3": "请输入备注", + "5mpiji1qpcb4": "部署中间件(Dad)", + "5mpiji1qpcb5": "请选择部署中间件", + "5mpiji1qpcb6": "部署完成后可以通过此名称对组件进行运维操作", + "5mpiji1qpcc0": "基础信息", + "5mpiji1qpcc1": "部署主机IP", + "5mpiji1qpbc1": "Deploy中间件部署信息", + "5mpiji1qpcc2": "用户名", + "5mpiji1qpcc3": "root密码", + "5mpiji1qpcc4": "请输入root密码", + "5mpiji1qpcc5": "安装路径", + "5mpiji1qpcc6": "请输入安装路径", + "5mpiji1qpcc7": "安装包信息", + "5mpiji1qpcc8": "openLooKeng安装包", + "5mpiji1qpcc9": "请选择openLooKeng安装包", + "5mpiji1qpcc10": "openLooKeng绑定端口", + "5mpiji1qpcc11": "请输入openLooKeng绑定端口", + "5mpiji1qpcc12": "请选择或输入用户名", + "5mpiji1qpcc13": "请输入密码", + "5mpiji1qpcc14": "请输入安装路径", + "5mpiji1qpcc15": "请输入openLooKeng端口", + "5mpiji1qpcc16": "请选择openLooKeng安装包", + "5mpiji1qpcc17": "添加配置", + "5mpiji1qpcc18": "配置预览", + "5mpiji1qpcc19": "执行安装", + "5mpiji1qpcc21": "安装路径不存在时将会自动创建", + "5mpiji1qpcc22": "Sharding安装包", + "5mpiji1qpcc23": "请选择ShardingProxy安装包", + "5mpiji1qpcc24": "Sharding绑定端口", + "5mpiji1qpcc25": "请输入Sharding绑定端口", + "5mpiji1qpcc26": "Zookeeper安装包", + "5mpiji1qpcc27": "请选择Zookeeper安装包", + "5mpiji1qpcc28": "Zookeeper绑定端口", + "5mpiji1qpcc29": "请输入Zookeeper绑定端口", + "5mpiji1qpcc30": "分片配置", + "5mpiji1qpcc31": "分片表名", + "5mpiji1qpcc32": "请输入分片表名,以逗号,分割", + "5mpiji1qpcc33": "分片键", + "5mpiji1qpcc34": "请输入分片键名,以逗号,分割", + "5mpiji1qpcb35": "基础配置", + "5mpiji1qpcc35": "ShardingProxy配置", + "5mpiji1qpcc36": "openLooKeng配置", + "5mpiji1qpcc37": "您可以在下面的编辑器中编辑分片配置文件rule.yaml", + "5mpiji1qpcc38": "请输入正确的目录路径", + "5mpiji1qpcc39": "请选择Deploy中间件安装包", + "5mpiji1qpcc40": "绑定端口", + "5mpiji1qpcc41": "请输入Deploy中间件绑定端口", + "5mpiji1qpcc42": "连接失败", + "5mpiji1qpcc43": "上传路径", + "5mpiji1qpcc44": "请输入上传路径", + "5mpiji1qpcc45": "上传路径不存在时将自动创建", + "5mpiji1qpcc46": "数据源选择", + "5mpiji1qpcc47": "请选择至少两个数据源", + "5mpiji1qpcc48": "集群/实例/数据库", + "5mpiji1qpcc49": "安装包上传路径", + "5mpiji1qpcc50": "请输入安装包上传路径", + "5mpiji1qpcc51": "重新生成", + "5mpiji1qpcc52": "高级参数配置", + "5mpiji1qpcc53": "请输入xmx", + "5mpiji1qpcc54": "请输入maxMemory", + "5mpiji1qpcc55": "请输入maxTotalMemory", + "5mpiji1qpcc56": "请输入maxMemoryPer", + "5mpiji1qpcc57": "请输入maxTotalMemoryPer", + "5mpiji1qpcc58": "安装成功", + "5mpiji1qpcc59": "返回首页", + "5mpiji1qpcc60": "上传安装包", + "5mpiji1qpcc61": "安装Zookeeper", + "5mpiji1qpcc62": "安装ShardingProxy", + "5mpiji1qpcc63": "安装openLookEng", + "5mpiji1qpcc64": "启动所有服务", + "5mpiji1qpcc65": "下载日志", + "5mpiji1qpcc66": "自定义控制台", + "5mpiji1qpcc67": "重试", + "5mpiji1qpcc68": "您可以前往openLooKeng管理页面对刚刚安装的集群进行管理", + "5mpiji1qpcc69": "openLooKeng管理", + "5mpiji1qpcc70": "部署", + "5mpiji1qpcc71": "请输入部署名称", + "5mpiji1qpcc72": "启动", + "5mpiji1qpcc73": "停止", + "5mpiji1qpcc74": "确定要删除这一条数据?", + "5mpiji1qpcc75": "确定", + "5mpiji1qpcc76": "取消", + "5mpiji1qpcb76": "删除", + "5mpiji1qpcc77": "确定要释放这个部署?相关释放后将无法恢复", + "5mpiji1qpcc78": "销毁", + "5mpiji1qpcc79": "下载操作日志", + "5mpiji1qpcc80": "部署名称", + "5mpiji1qpcc81": "分片表名", + "5mpiji1qpcc82": "分片键", + "5mpiji1qpcc83": "更新时间", + "5mpiji1qpcc84": "备注", + "5mpiji1qpcc85": "操作", + "5mpiji1qpcc86": "Distribute Deploy安装信息", + "5mpiji1qpcc87": "安装包", + "5mpiji1qpcc88": "主机IP", + "5mpiji1qpcc89": "安装用户", + "5mpiji1qpcc90": "安装路径", + "5mpiji1qpcc91": "上传路径", + "5mpiji1qpcc92": "端口", + "5mpiji1qpcc93": "Zookeeper安装信息", + "5mpiji1qpcc94": "ShardingProxy安装信息", + "5mpiji1qpcc95": "openLooKeng安装信息" + }, + "HostTerminal": { + "else1": "IP地址", + "else2": "在特殊屏幕分辨率下如果出现字体模糊等问题,请对网页进行缩放", + "5oxhoh5r6ls0": "终端" + } + }, + "logCenter": { + "index": { + "else1": "IP地址", + "5mpllm8hjao0": "集群名称", + "5mpllm8hk7o0": "请选择集群名称", + "5mpllm8hkk00": "请选择主机IP", + "5mpllm8hkog0": "搜索", + "5mpllm8hks40": "系统日志", + "5mpllm8hkwk0": "操作日志", + "5mpllm8hl000": "审计日志", + "5mpllm8hl3w0": "性能日志", + "5mpllm8hl7w0": "黑匣子日志" + } + }, + "monitor": { + "index": { + "else1": "CPU使用率", + "5mplmn5z06k0": "实例监控", + "5mplmn5z19w0": "集群名称", + "5mplmn5z1lo0": "请选择集群名称", + "5mplmn5z1qs0": "主机IP", + "5mplmn5z1uc0": "请选择主机IP", + "5mplmn5z1y80": "内存占用率", + "5mplmn5z2300": "网络状态", + "5mplmn5z2a00": "锁", + "5mplmn5z2ms0": "会话", + "5mplmn5z30k0": "连接数", + "5mplmn5z37w0": "占用内存最多的会话TOP10", + "5mplmn5z3e90": "请开启enable_thread_pool参数", + "5mplmn5z41w0": "主机缺少依赖包:" + } + }, + "operation": { + "task": { + "step1": { + "index" : { + "serverConfig": "服务器配置", + "IP": "主机IP", + "IPPlaceholder": "请输入或者选择主机IP", + "IPContent": "主备节点操作系统和架构应该一致,更改主机IP后可能会影响节点配置,是否确认更改", + "installUser": "安装用户", + "installUserPlaceholder": "请选择安装用户", + "userContent": "主备节点用户选择应该一致,更改用户后可能会影响节点配置,是否确认更改", + "packageConfig": "安装包配置", + "system": "操作系统", + "cpuArch": "系统架构", + "version": "openGauss版本", + "MINIMAL_LIST": "极简版", + "LITE": "轻量版", + "ENTERPRISE": "企业版", + "MINIMAL_LISTContent": "极简版安装的使用主体主要针对高校和个人测试环境,相对企业安装流程更简单快捷。", + "LITEContent": "openGauss轻量版在各台机器上分别执行安装操作,因为轻量版版本没有OM,CM等组件,所以安装、卸载、升级等OM功能需要分别在各个节点上手动操作。", + "ENTERPRISEContent": "企业版安装的主要使用主体为企业或对数据库性能要求较高的个人,安装流程比较复杂,功能更全。", + "versionNum": "openGauss版本号", + "versionPlaceholder": "请输入版本号", + "selectedPackage": "已选择安装包", + "selecPack": "所选安装包", + "morePackage": "未列出我想要的包?", + "packageManagement": "安装包管理", + "upload": "离线上传", + "download": "在线下载", + "clusterConfig": "集群配置", + "clusterName": "集群标识", + "clusterNamePlaceholder": "请输入集群标识", + "installPath": "安装目录", + "installPathPlaceholder": "请输入安装目录", + "installPathContent": "安装目录指的是安装openGauss的根目录,类似/opt/openGauss", + "installPackagePath": "软件包路径", + "installPackagePathplaceholder": "请输入软件包路径", + "installPackagePathcontent": "软件包路径指的是存放openGauss官方安装包压缩文件的路径", + "port": "端口号", + "dbPwd": "数据库密码", + "dbPwdContent": "用于登录数据库时的凭证, 请输入高安全度密码", + "deployType": "部署类型", + "SINGLE_NODE": "单节点安装", + "CLUSTER": "多节点安装", + "checkflagCM": "是否安装CM", + "checkflagEnvSeqar": "是否环境分离", + "envPath" : "环境分离路径", + "envPathPlaceholder": "请输入环境分离路径", + "placeholder": "请输入", + "proConfig": "高级参数配置", + "logPath": "日志目录", + "tmpPath": "临时文件目录", + "omToolsPath": "数据库工具目录", + "corePath": "数据库core目录", + "logPathPlaceholder": "请输入日志目录", + "tmpPathPlaceholder": "请输入临时文件目录", + "omToolsPathPlaceholder": "请输入数据库工具目录", + "corePathPlaceholder": "请输入数据库core目录", + "nodeConfig": "节点配置", + "addColumn": "添加节点", + "clusterType": "节点类型", + "clusterMaster": "主节点", + "clusterOthers": "备节点", + "dataPath": "数据路径", + "azOwner": "所属AZ", + "azPriority": "AZ优先级", + "isCMMaster": "是否是CM主节点", + "cmMasterContent": "仅支持单个节点作为CM主节点,一旦更改后可能会影响其他节点属性,是否确认更改?", + "yes": "是", + "no": "否", + "confirm": "确定", + "cancel": "取消", + "cmDataPath": "CM数据路径", + "cmPort": "CM端口号", + "cmPortContent": "所有CM节点端口一致,一旦更改后可能会影响其他节点属性,是否确认更改", + "option": "操作", + "edit": "编辑", + "delete": "删除", + "deleteContent": "是否确定删除", + "save": "保存", + "serverDataError": "当前服务器数据不全 请选择其他IP", + "packageExistError": "不存在,请重新选择", + "packageCheckError": "安装包检查未通过,可能会影响流程", + "userNullError": "请选择安装用户,否则无法生成节点", + "ipSameError": "所选ip与主机ip相同,请重新选择", + "ipNullError": "ip不可为空", + "ipUserError": "所选ip不存在该用户,请重新选择ip或在该ip新增用户", + "saveError": "仍有不符合规则项,无法保存", + "sameNameError": "仍有不符合规则项,无法保存", + "cmMasterError": "所有节点中没有CM主节点,无法更改为非主节点", + "websocketErrorMsg": "WebSocket长时间无响应,将关闭下载进度悬浮框", + "onlineDownload": "在线下载", + "ipNoticeError": " IP下", + "portOccupied": "端口被占用,无法保存", + "notificationText": "根据配置匹配到如下安装包并进行下载:", + "passwdWarning": "出于安全考虑,密码信息无法回显,如需修改请重新输入", + "visible": "显示", + "hide": "隐藏" + } + }, + "step2s5cf2y0": "环境监测中...", + "step2s5cf2y1": "硬件环境监测测结果", + "step2s5cf2y2": "操作系统", + "step2s5cf2y3": "请检查操作系统是否为", + "step2s5cf2y4": "操作系统符合要求", + "step2s5cf2y5": "操作系统版本", + "step2s5cf2y6": "请检查操作系统版本是否符合要求", + "step2s5cf2y7": "操作系统版本符合要求", + "step2s5cf2y8": "可用内存", + "step2s5cf2y9": "建议内存32GB或者更大", + "step2s5cf210": "可用内存符合要求", + "step2s5cf211": "CPU核数", + "step2s5cf212": "CPU核数不符合系统要求", + "step2s5cf213": "CPU核数符合系统要求", + "step2s5cf214": "CPU频率", + "step2s5cf215": "CPU频率最小为2.0GHz", + "step2s5cf216": "CPU频率符合要求", + "step2s5cf217": "可用硬盘空间", + "step2s5cf218": "可用硬盘空间不符合系统要求", + "step2s5cf219": "可用硬盘空间符合系统要求", + "step2s5cf220": "软件环境检测结果", + "step2s5cf221": "软件依赖性", + "step2s5cf222": "未安装依赖:", + "step2s5cf223": "符合要求", + "step2s5cf224": "防火墙", + "step2s5cf225": "未开启防火墙", + "step2s5cf226": "已开启防火墙", + "step2s5cf227": "安装用户", + "step2s5cf228": "不允许用户安装", + "step2s5cf229": "允许用户安装", + "step2s5cf230": "其他", + "step2s5cf231": "不符合要求:", + "step2s5cf232": "其他条件均符合要求", + "step2s5cf233": "重新监测", + "step2s5cf234": "获取ip失败" + }, + "ClusterBackupDlg": { + "5mplmzbrmkg0": "备份", + "5mplmzbrntg0": "集群名称", + "5mplmzbro8g0": "备份路径", + "5mplmzbroj00": "请输入备份路径", + "5mplmzbrosg0": "备注", + "5mplmzbrp1s0": "请输入备注", + "5mplmzbrpas0": "集群备份" + }, + "DailyOps": { + "sl3u5s5cf2y0": "集群管理", + "sl3u5s5cf2y1": "使用指南", + "sl3u5s5cf2y2": "创建并行安装任务", + "sl3u5s5cf2y3": "安装集群", + "sl3u5s5cf2y4": "CPU利用率TOP5", + "sl3u5s5cf2y5": "内存占有率TOP5", + "sl3u5s5cf2y6": "会话TOP5", + "sl3u5s5cf2y7": "集群列表", + "sl3u5s5cf2y8": "并行安装任务", + "sl3u5s5cf2y9": "任务列表", + "sl3u5s5cf210": "草稿箱", + "sl3u5s5cf211": "批量执行", + "sl3u5s5cf212": "删除", + "sl3u5s5cf299": "请输入集群名称搜索", + "sl3u5s5cf213": "请选择合适标签", + "sl3u5s5cf214": "待执行", + "sl3u5s5cf215": "执行等待中", + "sl3u5s5cf216": "执行中", + "sl3u5s5cf217": "执行成功", + "sl3u5s5cf218": "执行失败", + "sl3u5s5cf219": "确定要执行吗?", + "sl3u5s5cf220": "一键执行", + "sl3u5s5cf221": "重新执行", + "sl3u5s5cf222": "复制", + "sl3u5s5cf223": "更多", + "sl3u5s5cf224": "执行日志下载", + "sl3u5s5cf225": "编辑", + "sl3u5s5cf226": "发布", + "sl3u5s5cf227": "环境检测", + "sl3u5s5cf228": "服务器信息", + "sl3u5s5cf229": "服务器IP", + "sl3u5s5cf230": "服务器系统", + "sl3u5s5cf231": "CPU架构", + "sl3u5s5cf232": "openGauss版本", + "sl3u5s5cf233": "集群节点数量", + "sl3u5s5cf234": "集群端口号", + "sl3u5s5cf235": "环境变量地址", + "sl3u5s5cf236": "是否安装CM", + "sl3u5s5cf237": "是否环境分离", + "sl3u5s5cf238": "环境分离路径", + "sl3u5s5cf239": "节点类型", + "sl3u5s5cf240": "AZ优先级", + "sl3u5s5cf241": "确定要执行以下任务吗?", + "sl3u5s5cf242": "执行任务会在目标主机进行数据库安装,请谨慎执行", + "sl3u5s5cf243": "执行确认", + "sl3u5s5cf244": "任务ID", + "sl3u5s5cf245": "任务状态", + "sl3u5s5cf246": "服务器IP", + "sl3u5s5cf247": "openGauss版本号", + "sl3u5s5cf248": "任务创建时间", + "sl3u5s5cf249": "任务更新时间", + "sl3u5s5cf250": "同一个父级标签只能选择一个子级标签,", + "sl3u5s5cf251": "标签下有多项,", + "sl3u5s5cf252": "请至少选择一行删除,", + "sl3u5s5cf253": "请至少选择一行执行,", + "sl3u5s5cf254": "最大值(%)", + "sl3u5s5cf255": "利用率(%)", + "else1": "核", + "else2": "实例", + "else3": "个", + "else4": "删除集群", + "else5": "此删除只删除平台数据,确定要删除?", + "else6": "刷新", + "else7": "仲裁", + "else8": "未知", + "else9": "宕机", + "else10": "异常", + "else11": "停止", + "else12": "数据库用户", + "else13": "数据库端口", + "else14": "安装目录", + "else15": "数据目录", + "else16": "安装用户", + "else17": "环境分离文件路径", + "5mplp1xbxl40": "集群名称", + "5mplp1xbyi40": "自检警告", + "lastCheckAt": "最后自检时间", + "5mplp1xbyqc0": "一键自检", + "5mplp1xbyxw0": "当前状态", + "5mplp1xbz600": "可用", + "5mplp1xbzew0": "节点数量", + "5mplp1xbzmw0": "一主", + "5mplp1xbztc0": "备", + "5mplp1xc0000": "单节点", + "5mplp1xc0640": "您可以进行以下操作", + "5mplp1xc0c00": "启动", + "5mplp1xc0i80": "停止", + "5mplp1xc0o40": "重启", + "5mplp1xc0u40": "备份", + "5mplp1xc0zo0": "确定要卸载?", + "5mplp1xc1580": "确定", + "5mplp1xc1b00": "取消", + "5mplp1xc1gs0": "卸载", + "5mplp1xc1ms0": "强制卸载", + "5mplp1xc1t80": "IP(内/外)", + "5mplp1xc20k0": "操作", + "5mplp1xc25o0": "启动所有实例", + "5mplp1xc2b40": "停止所有实例", + "5mplp1xc2gw0": "内网IP", + "5mplp1xc2ls0": "外网IP", + "5mplp1xc2sw0": "主机名称", + "5mplp1xc2xs0": "主机配置", + "5mplp1xc32g0": "内存", + "5mplp1xc3740": "会话", + "5mplp1xc3bw0": "连接数", + "5mplp1xc3hs0": "锁", + "5mplp1xc3no0": "可用区", + "5mplp1xc3ss0": "暂无集群信息", + "5mplp1xc3xg0": "去安装", + "5mplp1xc4200": "正在获取集群信息, 请稍后", + "5mplp1xc46o0": "极简版", + "5mplp1xc4b40": "轻量版", + "5mplp1xc4fg0": "企业版", + "5mplp1xc4jw0": "主", + "5mplp1xc4oc0": "级联", + "5mplp1xc4sw0": "实例开关操作", + "5mplp1xc4x80": "不可用", + "5mplp1xc51s0": "未检测", + "5mplp1xc56k0": "主备切换", + "5mplp1xc5cg0": "生成配置", + "nodeBuild": "节点重建", + "nodeField1": "存在故障节点", + "nodeState1": "可用", + "nodeState2": "需要修复", + "nodeState3": "启动中", + "nodeState4": "等待升级中", + "nodeState5": "正在升级为主节点", + "nodeState6": "降级中", + "nodeState7": "启动失败,需要重建", + "nodeState8": "追赶主节点", + "nodeState9": "崩溃", + "nodeState10": "未知", + "guc5cg0": "guc参数设置", + "guc5cg1": "参数名称:", + "guc5cg2": "请输入参数名称", + "guc5cg3": "搜索", + "guc5cg4": "全部设为默认值", + "guc5cg5": "还原全部修改", + "guc5cg6": "应用到所有实例", + "guc5cg7": "保存", + "guc5cg8": "名称", + "guc5cg9": "参数值", + "guc5ch0": "描述", + "guc5ch1": "备注", + "guc5ch2": "操作", + "guc5ch3": "设为默认值", + "guc5ch4": "还原修改", + "guc5ch5": "正在设置guc参数并重启集群", + "guc5ch6": "正在设置guc参数", + "guc5ch7": "集群重启确认", + "guc5ch8": "您修改了postmaster类型的guc参数,需要重启集群后生效,是否现在设置guc参数并重启?" + }, + "HostPwdDlg": { + "pwdValid": "密码校验", + "IpAddress": "IP地址", + "rootPwd": "root密码", + "rootPwdPlaceholder": "请输入root密码", + "else1": "不能为纯空格" + } + }, + "slowSql": { + "index": { + "else1": "数据库名称", + "else2": "节点名称", + "5mplw69rv000": "集群名称", + "5mplw69rvmo0": "请选择集群名称", + "5mplw69rvt40": "主机IP", + "5mplw69rvz80": "请选择主机IP", + "5mplw69rw300": "开始时间", + "5mplw69rw900": "查询", + "5mplw69rwcw0": "结束时间", + "5mplw69rwgs0": "时间阈值", + "5mplw69rwks0": "SQL语句", + "5mplw69rwog0": "执行计划" + } + }, + "snapshot": { + "index": { + "5mplywcvqh80": "正在生成快照...", + "5mplywcvr080": "创建快照", + "5mplywcvr7c0": "集群名称", + "5mplywcvrb80": "请选择集群名称", + "5mplywcvres0": "主机IP", + "5mplywcvrio0": "请选择主机IP", + "5mplywcvrmg0": "搜索", + "5mplywcvrq80": "快照ID", + "5mplywcvru80": "捕获时间", + "snapshotManage": "快照管理" + } + }, + "wdr": { + "GenerateWdrDlg": { + "else1": "开始快照不能晚于或等于结束快照", + "else2": "结束快照不能早于或等于开始快照", + "5mpm0eufx3g0": "生成", + "5mpm0eufxww0": "集群名称", + "5mpm0eufy340": "请选择集群", + "5mpm0eufy7k0": "主机IP", + "5mpm0eufyb40": "请选择主机IP", + "5mpm0eufyew0": "报告范围", + "5mpm0eufyig0": "请选择报告范围", + "5mpm0eufyo80": "报告类型", + "5mpm0eufyz00": "请选择报告类型", + "5mpm0eufzbk0": "开始快照", + "5mpm0eufzg00": "请选择开始快照", + "5mpm0eufzk80": "结束快照", + "5mpm0eufzo40": "请选择结束快照", + "5mpm0eufzrs0": "生成WDR", + "5mpm0eufzv40": "集群", + "5mpm0eufzyg0": "节点", + "5mpm0eug0300": "明细", + "5mpm0eug0880": "汇总", + "5mpm0eug0bc0": "全部" + }, + "index": { + "snapshotManage": "快照管理", + "5mpm1cuaq540": "集群名称", + "5mpm1cuaqvk0": "请选择集群名称", + "5mpm1cuar0w0": "生产时间", + "5mpm1cuar500": "报告范围", + "5mpm1cuar9s0": "请选择报告范围", + "5mpm1cuardo0": "报告类型", + "5mpm1cuarh00": "请选择报告类型", + "5mpm1cuarks0": "查询", + "5mpm1cuaro40": "重置", + "5mpm1cuarrk0": "生成WDR", + "5mpm1cuarv40": "查看", + "5mpm1cuaryo0": "下载", + "5mpm1cuas2s0": "确定要删除?", + "5mpm1cuas6c0": "确定", + "5mpm1cuasa40": "取消", + "5mpm1cuasdc0": "删除", + "5mpm1cuasgo0": "集群", + "5mpm1cuask80": "节点", + "5mpm1cuasng0": "明细", + "5mpm1cuasr40": "汇总", + "5mpm1cuasvs0": "全部", + "5mpm1cuasys0": "报告生成时间", + "5mpm1cuat200": "报告名称", + "5mpm1cuat4o0": "操作" + } + }, + "backup": { + "index": { + "else1": "集群标识", + "else2": "确定要恢复?", + "5mpm2oya7bg0": "请选择集群名称", + "5mpm2oya7y80": "搜索", + "5mpm2oya83s0": "恢复", + "5mpm2oya87g0": "确定要删除?", + "5mpm2oya8b00": "确定", + "5mpm2oya8ek0": "取消", + "5mpm2oya8i80": "删除", + "5mpm2oya8lk0": "集群标识", + "else3": "备份说明", + "5mpm2oya8r80": "备份标识", + "5mpm2oya8uo0": "所在机器", + "5mpm2oya8y00": "备份路径", + "5mpm2oya9280": "备份时间", + "5mpm2oya95o0": "操作" + } + }, + "enterprise": { + "ClusterConfig": { + "5mpm3ku3go40": "集群配置", + "5mpm3ku3hcg0": "集群标识", + "5mpm3ku3hik0": "请输入集群标识", + "5mpm3ku3hn40": "集群名称", + "5mpm3ku3hr00": "请输入集群名称", + "5mpm3ku3hv40": "安装目录", + "5mpm3ku3i340": "请输入安装目录", + "5mpm3ku3i6s0": "日志目录", + "5mpm3ku3iag0": "请输入日志目录", + "5mpm3ku3ie40": "临时文件目录", + "5mpm3ku3ihg0": "请输入临时文件目录", + "5mpm3ku3il80": "数据库工具目录", + "5mpm3ku3iok0": "请输入数据库工具目录", + "5mpm3ku3is00": "数据库core目录", + "5mpm3ku3ivk0": "请输入数据库core目录", + "5mpm3ku3iz80": "端口号", + "5mpm3ku3j300": "请输入端口号", + "5mpm3ku3j6k0": "数据库用户名", + "5mpm3ku3j9s0": "请输入数据库用户名", + "5mpm3ku3jdg0": "数据库密码", + "5mpm3ku3ji00": "请输入数据库密码", + "5mpm3ku3jkw0": "开启节点自选主", + "5mpm3ku3jmn0": "DCF 端口号", + "5mpm3ku3jnl0": "请输入DCF端口号", + "5mpm3ku3jo00": "集群标识已存在,请重新输入", + "5mpm3ku3jqo0": "判断集群标识是否存在出错", + "5mpm3ku3jpo0": "共享存储配置", + "5mpm3ku3jso0": "集群安装时,主机IP不能相同", + "5mpm3ku3jto0": "集群安装时,安装用户必须相同", + "5mpm3ku3joo0": "安装目录和软件包路径不能相同", + "5mpm3ku3jxo0": "DCF端口不能和数据库端口相同", + "5mpm3ku3jro0": "该字段长度不能超过20个字符", + "5mpm3ku3jwo0": "集群安装时,CM端口必须相同", + "5mpm3ku3juo0": "集群安装时,CM端口和数据库端口不能相同", + "5mpm3ku3juw0": "字段含有非法字符", + "5mpm3ku3juy0": "该字段不能只包含数字", + "5mpm3ku3jux0": "该端口值非法,请输入1024~65529之间的端口", + "5mpm3ku3jyx0": "CM端口和数据库端口不能相同", + "5mpm3ku3jzx0": "CM端口和DCF端口不能相同", + "5mpm3ku3jvx0": "该目录格式不正确", + "5mpm3ku3jvx5": "安装路径与软件包路径不可交叉,请设置单独文件夹", + "5mpm3ku3jvx6": "安装路径与高级参数配置路径不可交叉,请设置单独文件夹,以/app结尾", + "else1": "请输入数字,字母或者下划线", + "else2": "不能为纯空格", + "else3": "主节点", + "else4": "备节点", + "test0": "内核架构", + "test00": "请选择内核架构", + "option1": "主备架构", + "option2": "共享存储架构", + "option3": "单主架构", + "test1": "请输入DSS实例目录", + "test2": "DSS实例目录", + "test3": "请输入DSS共享卷名", + "test4": "DSS共享卷名", + "test5": "请选择DSS共享卷路径", + "test6": "DSS共享卷路径", + "test7": "请输入xlog卷名,如果有多个卷,请用逗号隔开", + "test8": "Xlog卷名", + "test9": "请选择xlog卷路径", + "test10": "Xlog卷路径", + "test11": "请选择CM共享卷路径", + "test12": "CM共享卷路径", + "test13": "请选择CM投票卷路径", + "test14": "CM投票卷路径", + "test15": "请选择网络连接方式", + "test16": "网络连接方式", + "test17": "请输入RDMA绑定的CPU序列,用空格隔开", + "test18": "RDMA网络配置", + "collapse1": "共享存储配置", + "passwdWarning": "出于安全考虑,密码信息无法回显" + }, + "EnterpriseInstall": { + "else2": "提示", + "else3": "执行", + "5mpm4jewxn00": "集群配置", + "5mpm4jewye00": "节点配置", + "5mpm4jewykk0": "环境监测", + "5mpm4jewyp40": "安装", + "5mpm4jewysw0": "导入", + "error1": "Dss Home为空", + "error2": "Dss卷名为空", + "error3": "Dss卷路径为空", + "error4": "Xlog卷名为空", + "error5": "Xlog卷路径为空", + "error6": "CM共享卷路径为空", + "error7": "CM投票卷路径为空", + "error8": "LUN路径不能相同", + "error9": "Xlog卷名和Xlog LUN路径不对应", + "error10": "Xlog卷名数量要等于1或者等于节点数", + "error11": "Xlog卷名和Dss卷名不能相同", + "error12": "Xlog卷名有多个时,xlog卷名之间不能相同", + "error13": "Dss实例目录和数据库安装目录不能相同" + + }, + "EnvMonitor": { + "else1": "个环境检测未通过,建议处理后重试", + "else2": "节点ip", + "5mpm5p9xees0": "正在进行检测", + "5mpm5p9xf4w0": "您有", + "5mpm5p9xfb80": "重新检测", + "5mpm5p9xff40": "硬件环境监测", + "5mpm5p9xfk80": "软件环境检测", + "5mpm5p9xfo40": "检测出问题了", + "5mpm5p9xfrs0": "检测失败,失败原因", + "5mpm5p9xfv80": "正在检测中", + "5mpm5p9xfzo0": "主", + "5mpm5p9xg3o0": "备", + "5mpm5p9xg700": "级联" + }, + "ExeInstall": { + "else1": "数据库用户", + "else2": "数据库密码", + "5mpm9j359hw0": "恭喜您安装成功", + "5mpm9j35a4o0": "请妥善保管用户名密码", + "5mpm9j35aa80": "回到首页", + "5mpm9j35aec0": "集群运维", + "5mpm9j35ahw0": "安装失败,您可以在解决相关问题后重试安装", + "5mpm9j35alg0": "安装过程中请勿进行其他操作,", + "5mpm9j35ap00": "否则将导致安装失败", + "5mpm9j35ats0": "重试" + }, + "InstallPrompt": { + "else1": "所属AZ", + "else2": "安装路径", + "else3": "端口", + "else4": "XLOG目录", + "else5": "CM端口号", + "else6": "CM数据路径", + "5mpmb9e6puk0": "集群信息", + "5mpmb9e6qmg0": "节点ip", + "5mpmb9e6qsc0": "主机IP", + "5mpmb9e6qww0": "数据目录", + "5mpmb9e6r0k0": "是否安装", + "5mpmb9e6r4g0": "是", + "5mpmb9e6r800": "否", + "5mpmb9e6rcw0": "主", + "5mpmb9e6rj80": "备", + "5mpmb9e6rpo0": "级联", + "5mpmb9e6rpp0": "共享存储信息" + }, + "SharingStorageConfig": { + "else1": "DSS实例目录", + "else2": "DSS共享卷名", + "else3": "DSS共享卷路径", + "else4": "Xlog卷名", + "else5": "Xlog卷路径", + "else6": "CM共享卷路径", + "else7": "CM投票卷路径", + "else8": "网络连接方式", + "else9": "RDMA网络配置" + }, + "NodeConfig": { + "else1": "xlog路径", + "else2": "root密码", + "else3": "是否安装 CM", + "else4": "CM数据路径", + "else5": "CM端口号", + "else6": "请输入az优先级", + "else7": "AZ优先级", + "else8": "密码错误", + "else9": "密码校验异常", + "else10": "未检测到端口对应的实例", + "else11": "端口已被占用", + "else12": "未检测到数据目录下对应的实例信息", + "else13": "检测到数据目录不为空", + "else14": "未检测到安装包目录下对应的安装包信息", + "else15": "检测到安装包目录不为空", + "else16": "检测到安装包目录不为空", + "else17": "未检测到环境分离文件", + "else18": "未检测到环境分离文件", + "else19": "DSS数据卷路径不存在", + "else20": "Xlog卷路径不存在", + "else21": "CM共享卷路径不存在", + "else22": "CM投票卷路径不存在", + "else23": "DSS Home路径不为空", + "5mpme7w69yc0": "集群节点数", + "5mpme7w6aj40": "所属AZ", + "5mpme7w6ap00": "请选择所属AZ", + "5mpme7w6aw80": "节点配置", + "5mpme7w6azo0": "主机IP", + "5mpme7w6b3k0": "请选择主机IP", + "5mpme7w6b700": "请输入root密码", + "5mpme7w6bak0": "安装用户", + "5mpme7w6be40": "是否为CM主节点", + "5mpme7w6bhg0": "请输入CM数据路径", + "5mpme7w6bko0": "请输入CM端口号", + "5mpme7w6boc0": "数据节点", + "5mpme7w6brs0": "数据路径", + "5mpme7w6bv40": "请输入数据路径", + "5mpme7w6byo0": "请输入xlog路径", + "5mpme7w6c1w0": "请选择主机", + "5mpme7w6c5g0": "请选择安装用户", + "5mpme7w6c8s0": "请填写CM数据路径", + "5mpme7w6cc40": "请填写数据路径", + "5mpme7w6cfw0": "请填写xlog路径", + "5mpme7w6ckk0": "主", + "5mpme7w6cng0": "备", + "5mpme7w6cqk0": "级联", + "noRootTip": "所选主机需要有root用户" + } + }, + "lightweight": { + "EnvMonitor": { + "else1": "个环境检测未通过,建议处理后重试", + "5mpmgwswi880": "正在进行检测", + "5mpmgwswj8g0": "您有", + "5mpmgwswjlc0": "节点ip", + "5mpmgwswjuk0": "重新检测", + "5mpmgwswk640": "硬件环境监测", + "5mpmgwswkb40": "软件环境检测", + "5mpmgwswkeo0": "检测出问题了", + "5mpmgwswki80": "检测失败,失败原因", + "5mpmgwswkls0": "正在检测中", + "5mpmgwswkpc0": "主", + "5mpmgwswkso0": "备", + "5mpmgwswkwg0": "级联" + }, + "ExeInstall": { + "else1": "环境准备", + "else2": "安装包准备", + "else3": "执行安装", + "else4": "安装后处理", + "else5": "主节点环境准备", + "else6": "主节点安装包准备", + "else7": "主节点执行安装", + "else8": "备节点环境准备", + "else9": "备节点安装包准备", + "else10": "备节点执行安装", + "5mpmjd1mam40": "恭喜您安装成功", + "5mpmjd1mbdc0": "请妥善保管用户名密码", + "5mpmjd1mbo00": "数据库用户", + "5mpmjd1mbxg0": "数据库密码", + "5mpmjd1mc580": "回到首页", + "5mpmjd1mce00": "集群运维", + "5mpmjd1mcm40": "安装失败,您可以在解决相关问题后重试安装", + "5mpmjd1mcu40": "安装过程中请勿进行其他操作,", + "5mpmjd1md2o0": "否则将导致安装失败", + "5mpmjd1mdf40": "重试" + }, + "InstallConfig": { + "else1": "root密码", + "else2": "软件包路径指的是存放openGauss官方安装包压缩文件的路径", + "5mpmkfqy71w0": "主", + "5mpmkfqy8400": "备", + "5mpmkfqy8es0": "节点配置", + "5mpmkfqy8nc0": "集群标识", + "5mpmkfqy8r00": "请输入集群标识", + "5mpmkfqy8vk0": "主机IP", + "5mpmkfqy91w0": "请选择主机IP", + "5mpmkfqy9h80": "请输入root密码", + "5mpmkfqy9rw0": "安装用户", + "5mpmkfqy9yo0": "请选择安装用户", + "5mpmkfqya4o0": "安装目录", + "5mpmkfqyaas0": "请输入安装目录", + "5mpmkfqyah00": "数据目录", + "5mpmkfqyan00": "请输入数据目录", + "5mpmkfqyasw0": "端口号", + "5mpmkfqyay80": "请输入端口号", + "5mpmkfqyb4c0": "数据库用户名", + "5mpmkfqyb9w0": "请输入数据库用户名", + "5mpmkfqybf80": "数据库密码", + "5mpmkfqybo00": "请输入数据库密码", + "5mpmkfqybw00": "请选择主机", + "5mpmkfqyc0w0": "集群标识已存在,请重新输入", + "5mpmkfqyc5w0": "判断集群标识是否存在出错", + "5mpmkfqycac0": "获取主机列表数据失败" + }, + "InstallPrompt": { + "5mpmly4bw340": "节点ip", + "5mpmly4bwww0": "主机IP", + "5mpmly4bx7g0": "安装目录", + "5mpmly4bxfg0": "数据目录", + "5mpmly4bxlo0": "端口号", + "5mpmly4bxtg0": "主", + "5mpmly4by1k0": "备", + "5mpmly4by980": "级联" + }, + "LightWeightInstall": { + "else1": "配置", + "else2": "提示", + "else3": "执行", + "5mpmmqzpre40": "部署方式", + "5mpmmqzprz80": "安装", + "5mpmmqzps6w0": "导入", + "5mpmmqzpsao0": "环境监测" + } + }, + "simple": { + "DeployWay": { + "5mpmphlozp00": "单节点", + "5mpmphlp0eo0": "主", + "5mpmphlp0k00": "备", + "5mpmphlp0nw0": "一主一备" + }, + "EnvMonitor": { + "else1": "个环境检测未通过,建议处理后重试", + "5mpmqpcxjpc0": "正在进行检测", + "5mpmqpcxke40": "您有", + "5mpmqpcxkq80": "节点ip", + "5mpmqpcxkzc0": "重新检测", + "5mpmqpcxlas0": "硬件环境监测", + "5mpmqpcxlk40": "软件环境检测", + "5mpmqpcxlxc0": "检测出问题了", + "5mpmqpcxm6o0": "检测失败,失败原因", + "5mpmqpcxmfs0": "正在检测中", + "5mpmqpcxmog0": "主", + "5mpmqpcxmwg0": "备", + "5mpmqpcxn5o0": "级联", + "confirm": "确认", + "errorConfirm": "当前有{noPassNum}个环境检测不通过,建议处理后重试,仍需继续安装请点击确认", + "failConfirm": "环境检测失败,建议检查后重试,仍需继续安装请点击确认" + }, + "ExeInstall": { + "else1": "环境准备", + "else2": "安装包准备", + "else3": "执行安装", + "else4": "安装后处理", + "5mpmsp16nuo0": "恭喜您安装成功", + "5mpmsp16oy40": "请妥善保管用户名密码", + "5mpmsp16pb40": "数据库用户", + "5mpmsp16pig0": "数据库密码", + "5mpmsp16pp80": "回到首页", + "5mpmsp16pxs0": "集群运维", + "5mpmsp16q5o0": "安装失败,您可以在解决相关问题后重试安装", + "5mpmsp16qhc0": "安装过程中请勿进行其他操作,", + "5mpmsp16qr80": "否则将导致安装失败", + "5mpmsp16qzc0": "重试" + }, + "InstallConfig": { + "else1": "root密码", + "else2": "请输入正确的端口号", + "else3": "未检测到安装目录下对应的实例信息", + "else4": "检测到安装目录下不为空", + "else5": "不少于8位字符,至少包含大写字母(A-Z)、小写字母(a-z)、数字(0-9)、非字母数字字符中的三类字符", + "else6": "软件包路径", + "else7": "请输入软件包路径", + "else8": "检测到软件包路径下不为空", + "else9": "环境分离文件路径", + "else10": "请输入环境分离文件路径", + "else11": "是否环境分离", + "else12": "安装目录指的是安装openGauss的根目录,类似/opt/openGauss", + "else13": "重要:请务必确认已按照参考链接(https://docs-opengauss.osinfra.cn/zh/docs/latest/docs/BriefTutorial/%E8%B5%84%E6%BA%90%E6%B1%A0%E5%8C%96.html)完成了所有配置工作,否则安装会失败。", + "else14": "关闭环境变量分离,在部署多套集群时可能造成环境变量冲突", + "else15": "未检测到数据目录下对应的实例信息", + "5mpmu0lapic0": "节点配置", + "5mpmu0laqc80": "集群标识", + "5mpmu0laqiw0": "请输入集群标识", + "5mpmu0laqow0": "主机IP", + "5mpmu0laqss0": "请选择主机IP。若无主机信息请到资源中心 -> 服务器管理界面添加主机信息", + "5mpmu0laqwo0": "请输入root密码", + "5mpmu0lar0c0": "安装用户", + "5mpmu0lar480": "安装目录", + "5mpmu0lar800": "请输入安装目录", + "5mpmu0laran0": "编辑数据路径", + "5mpmu0larbb0": "不编辑数据路径时,默认为安装路径拼接而成", + "5mpmu0larsl0": "数据目录示例:/opt/openGauss/data/single_node", + "5mpmu0larbk0": "数据目录", + "5mpmu0larew0": "请输入数据目录", + "5mpmu0larj40": "端口号", + "5mpmu0larmo0": "请输入端口号", + "5mpmu0larq40": "数据库用户名", + "5mpmu0larto0": "请输入数据库用户名", + "5mpmu0larx00": "数据库密码", + "5mpmu0las0k0": "请输入数据库密码", + "5mpmu0las4g0": "集群标识已存在,请重新输入", + "5mpmu0las800": "判断集群标识是否存在出错" + }, + "InstallPrompt": { + "5mpmunt3eco0": "主", + "5mpmunt3ezw0": "节点ip", + "5mpmunt3f5k0": "主机IP", + "5mpmunt3f9s0": "安装目录", + "5mpmunt3fdc0": "数据目录", + "5mpmunt3fjc0": "端口号" + }, + "SimpleInstall": { + "else0": "部署方式", + "else1": "配置", + "else2": "提示", + "else3": "执行", + "5mpmv5d5bmo0": "安装", + "5mpmv5d5cyk0": "导入", + "5mpmv5d5dak0": "环境监测" + } + }, + "install": { + "Install": { + "5mpn60ejqkg0": "上一步", + "5mpn60ejriw0": "下一步" + } + }, + "dtCluster": { + "index": "容灾集群安装", + "exeInstall": "执行安装", + "installConstraints": "安装约束", + "installConstraint1": "用于搭建资源池化容灾集群的单集群,需要满足如下约束:", + "installConstraint2": "1) 每个单集群需关联不同的Dorado磁阵,且磁阵之间需要能够相互访问。", + "installConstraint3": "2) 每个单集群的xlog卷只能有一个,并且需要建立好多个集群上xlog盘之间的同步复制关系。上述操作均需要在DeviceManager上操作完成。", + "installConstraint4": "3) 具有同步关系的xlog盘的容量要相同。", + "dtClusterName": "容灾集群名称", + "dtClusterType": "容灾集群类型", + "resourcePoolingDt": "资源池化容灾", + "tranditionalDt": "传统高可用容灾", + "replicationType": "复制类型", + "storageReplication": "存储复制", + "networkReplication": "网络复制", + "cluster": "集群", + "diskArray": "磁阵", + "selectCluster": "请选择集群", + "selectDiskArray": "请选择磁阵", + "clusterInstallation": "集群安装", + "diskArrayManagement": "磁阵管理", + "inputDtClusterName": "请输入容灾集群名称", + "selectDtClusterType": "请选择容灾集群类型", + "selectReplicationType": "请选择复制类型", + "refresh": "刷新", + "nextStep": "下一步", + "lastStep": "上一步", + "installing": "安装中,请勿进行其他操作,否则将导致安装失败", + "installFailure": "安装失败,您可以在解决相关问题后重试安装", + "installSuccess": "恭喜您安装成功", + "goHome": "回到首页", + "downloadLog": "下载日志", + "retry": "重试", + "dtClusterManagement": "容灾集群管理", + "operations": "您可以进行以下操作", + "switchover": "主备切换", + "releaseDt": "解除灾备", + "clusterName": "集群名称", + "currentStatus": "当前状态", + "available": "可用", + "unavailable": "不可用", + "degraded": "存在故障节点", + "fullBackup": "主集群全量复制中", + "backupFail": "主集群全量复制失败", + "archive": "主集群日志同步复制中", + "archiveFail": "主集群日志同步复制失败", + "switchovering": "主备切换中", + "restore": "备集群全量恢复中", + "restoreFail": "备集群全量恢复失败", + "recovery": "备集群日志同步复制中", + "recoveryFail": "备集群日志同步复制失败", + "promote": "备集群升主中", + "promoteFail": "备集群升主失败", + "nodeNum": "节点数量", + "singleNode": "单节点", + "primary": "主", + "standby": "备", + "dbUser": "数据库用户", + "dbPort": "数据库端口", + "envVarPath": "环境变量路径", + "primaryNodeIp": "主节点IP", + "standbyNodeIp": "备节点IP", + "unknown": "未知", + "illegalCharacters": "存在不合法字符,请删除", + "empty": "暂无容灾集群信息", + "noSameCluster": "请选择不同的集群用作主备", + "noSameDiskArray": "请选择不同的磁阵用作主备" + }, + "diskArray": { + "index": "磁阵", + "create": "创建", + "update": "修改", + "diskArrayName": "磁阵名称", + "hostIp": "主机IP", + "port": "端口号", + "username": "用户名", + "password": "密码", + "pairId": "Pair Id", + "operations": "操作", + "modify": "修改", + "delete": "删除", + "cancel": "取消", + "ok": "确定", + "confirmDelete": "确定删除这个磁阵吗?", + "connectivityTest": "连通性测试", + "available": "可用", + "unavailable": "不可用", + "inputName": "请输入磁阵名称", + "inputHostIp": "请输入主机IP", + "inputPort": "请输入端口号", + "inputUsername": "请输入用户名", + "inputPassword": "请输入密码", + "inputPairId": "请输入pair id", + "illegalCharacters": "存在不合法字符,请删除", + "duplicatedName": "名称已存在,请修改", + "wrongHostIp": "请输入正确的IP地址", + "wrongPort": "请输入正确的端口号", + "currentStatus": "当前状态", + "success": "成功" + }, + "migrate": { + "index": { + "5mpn6g8g9680": "正在开发中,敬请期待" + } + }, + "simpleInstall": { + "index": { + "else1": "极简版", + "else2": "root密码", + "else3": "已经安装了极简版单点", + "else4": "您可以选择其它版本进行安装", + "else5": "数据库版本", + "else6": "请选择数据库版本", + "5mpn813gtvs0": "正在安装openGauss的极简版", + "5mpn813guf00": "主机IP", + "5mpn813gukw0": "请选择主机IP", + "user": "用户", + "userPlaceholder": "请选择用户", + "5mpn813gupc0": "请输入root密码", + "5mpn813gut00": "一键安装", + "5mpn813guxc0": "正在安装", + "5mpn813gv880": "您正在安装openGauss极简版", + "5mpn813gvc40": "到", + "5mpn813gvfw0": "安装失败,您可以在解决相关问题后重试安装", + "5mpn813gvjk0": "主机信息", + "5mpn813gvmw0": "重试", + "5mpn813gvqo0": "恭喜您安装成功", + "5mpn813gvu40": "请妥善保管用户名密码", + "5mpn813gvxg0": "数据库用户", + "5mpn813gw100": "数据库密码", + "5mpn813gw4c0": "回到首页", + "5mpn813gw7w0": "集群运维", + "5mpn813gwbk0": "请选择主机。若无主机信息请到资源中心 -> 服务器管理界面添加主机信息" + } + }, + "upgrade": { + "index": { + "5uunxx6pdzg0": "升级配置", + "5uunxx6pf5w0": "环境检查", + "5uunxx6pfic0": "执行升级", + "5uunxx6pftc0": "上一步", + "5uunxx6pg380": "下一步" + }, + "UpgradeConifg": { + "5uuo00svvrc0": "请选择要升级的企业版集群", + "5uuo00svwnk0": "请输入集群名称", + "5uuo00svwu80": "正在检索可用的企业版集群", + "5uuo00svwy40": "请输入root密码", + "5uuo00svx1g0": "未检索到企业版集群", + "5uuo00svx580": "集群安装", + "5uuo00svx8k0": "正在收集升级版本", + "5uuo00svxc00": "升级目标", + "5uuo00svxfo0": "单节点", + "5uuo00svxlo0": "请选择至少一个企业版集群进行升级", + "5uuo00svxpc0": "请选择一个要升级的目标版本", + "else1": "集群名称", + "else2": "节点数量", + "else3": "xml配置路径", + "else4": "安装系统", + "else5": "主节点服务器root密码", + "else6": "一主", + "else7": "备", + "5uuo00svx1g1": "未检索到目标版本", + "5uuo00svx1g2": "升级包管理" + }, + "EnvCheck": { + "5uuo4bwfpzg0": "升级到", + "5uuo4bwfr6k0": "重新检测", + "else1": "集群名称", + "else2": "硬盘使用率", + "5uuo4bwfr6k1": "集群{clusterId}主节点{ip}磁盘空间不足,无法进行升级" + }, + "ExeUpgrade": { + "5uuo8zf4cfw0": "展开全部", + "5uuo8zf4dn40": "全部收起", + "5uuo8zf4dxo0": "批量提交升级", + "5uuo8zf4e4w0": "批量回滚", + "5uuo8zf4ebw0": "升级到", + "5uuo8zf4ej80": "升级中", + "5uuo8zf4eq40": "正在回滚", + "5uuo8zf4eww0": "正在提交升级", + "5uuo8zf4f480": "下载日志", + "5uuo8zf4fao0": "开始升级", + "5uuo8zf4fmg0": "提交升级", + "5uuo8zf4ftc0": "重试", + "5uuo8zf4fzg0": "升级回滚", + "5uuo8zf4g600": "准备升级包", + "5uuo8zf4gc40": "开启GUC参数", + "5uuo8zf4gi00": "检查集群状态", + "5uuo8zf4gs80": "执行PreInstall", + "5uuo8zf4gz40": "执行升级", + "5uuo8zf4h580": "升级验证", + "5uuo8zf4hbk0": "请点击开始升级按钮重新升级", + "5uuo8zf4hk40": "正在执行", + "5uuo8zf4hq00": "升级进度100%待提交", + "5uuo8zf4hvk0": "升级已完成", + "5uuo8zf4i0s0": "升级失败", + "else1": "集群名称" + }, + "ClusterControlDlg": { + "5uuobb2rd640": "控制台" + } + }, + "packageManage": { + "index": { + "else1": "CPU架构", + "else2": "全部", + "else3": "请选择版本名称", + "5myq5c8yz7c0": "新增安装包", + "5myq5c8z8540": "搜索...", + "5myq5c8zmbk0": "修改", + "5myq5c8zms40": "确定要删除?", + "5myq5c8zn100": "确定", + "5myq5c8zn7k0": "取消", + "5myq5c8znew0": "删除", + "5myq5c8zpu85": "名称", + "5myq5c8zpu83": "安装包类型", + "5myq5c8znkk0": "所属系统", + "5myq5c8zns00": "版本名称", + "5myq5c8zp680": "版本号", + "5myq5c8zpu80": "在线下载地址", + "5myq5c8zpu82": "离线安装包", + "5myq5c8zpu84": "备注", + "5myq5c8zq380": "操作", + "5myq5c8zu5w0": "企业版", + "5myq5c8zuxc0": "极简版", + "5myq5c8zw680": "轻量版", + "5myq5c8zw681": "返回" + }, + "AddPackageDlg": { + "else1": "CPU架构", + "else2": "请填写正确的地址", + "5myq6nnea2w0": "所属系统", + "5myq6nneap40": "请选择所属系统", + "5myq6nneap41": "请选择安装包类型", + "5myq6nneaw40": "请选择CPU架构", + "5myq6nneb180": "版本名称", + "5myq6nneb5w0": "请选择版本名称", + "5myq6nnebag0": "版本号", + "5myq6nnebew0": "请输入版本号", + "5myq6nnebis0": "在线下载地址", + "5myq6nnebn40": "请输入在线下载地址", + "5myq6nnebn41": "离线安装包", + "5myq6nnebn42": "请上传离线安装包", + "5myq6nnebn43": "备注", + "5myq6nnebn44": "请输入备注", + "5myq6nnebrc0": "新增安装包", + "5myq6nnebwo0": "修改安装包", + "5myq6nnec400": "企业版", + "5myq6nnec8c0": "极简版", + "5myq6nnecc40": "轻量版", + "5myq6nnecc41": "关闭确认", + "5myq6nnecc42": "您已经上传了文件,如果此时取消将导致已经上传的文件无法被删除,您可以先删除文件后再关闭", + "5myq6nnecc43": "请输入在线地址或者增加离线安装包", + "5myq6nnecc44": "请输入在线地址", + "5myq6nnecc45": "确认删除", + "5myq6nnecc46": "?一旦删除将无法恢复安装包文件", + "5myq6nnecc47": "可以上传任意文件作为安装包", + "5myq6nnecc48": "当前的上传路径为: ", + "5myq6nnecc49": ",您可以在系统设置中进行更改", + "5myq6nnecc50": "请选择一个安装包进行上传", + "5myq5c8zpu93": "安装包名称", + "5myq5c8zpu94": "安装包名称重复", + "5myq5c8zpu95": "请输入安装包名称" + } + }, + "modeling": { + "dy_common": { + "saveSuccess": "保存成功!", + "noResult": "无结果", + "saveFailed": "保存失败!", + "sqlyunxing": "SQL语句", + "saveAndClose": "保存配置并关闭", + "AutomaticallySaved": "已自动保存", + "AutomaticallySaving": "自动保存中", + "page_empty": "正在开发中,敬请期待", + "xConfig": "X轴配置", + "yConfig": "Y轴配置", + "xFB": "X轴分布", + "xConfigMessage": "请选择X轴配置", + "yConfigMessage": "请选择Y轴配置", + "warning": "警告", + "warningBatchDelete1": "确定要删除这", + "warningBatchDelete2": "个数据流吗", + "batchDelete": "批量删除", + "deleteText": "删除", + "deleteNotice": "确定要删除?", + "paste": "粘贴", + "isNo": "无", + "heatmapConfig": { + "notice1": "请选择统计指标字段!", + "notice2": "请选择X轴分布字段!", + "notice3": "请选择Y轴配置字段!" + }, + "chartTips1": "统计维度:在X轴选择的字段基础之上,再次进行多个维度的分组可在此处配置,每个维度可以设置只取前N条,避免条目过多展示效果不好。如:在X轴选择了品牌字段(brand_type)后,还想在看到不同品牌内赠品和非赠品的销售情况差异,则可以在统计维度添加一条,选择是否赠品字段(present)。\n\n自定义维度:可以通过自定义配置,对某个字段的所有枚举值进行分类,并形成一个新的统计维度,该统计维度使用方法与常规字段一致,用户完成新增自定义维度并保存后,则可以在该算子中任意图表使用。", + "chartNotice1": "请选择第", + "chartNotice2": "个统计维度的类型!", + "chartNotice3": "请选择[第", + "chartNotice4": "个副图]的第", + "geoMapLabelColor0": "热力颜色配置", + "geoMapLabelColor1": "热力低值", + "geoMapLabelColor2": "热力中值", + "geoMapLabelColor3": "热力高值", + "visualEdit": { + "mapTitle1": "地图参数(只读)", + "showNum": "是否显示数值", + "showNumYes": "是", + "showNumNo": "否", + "snapshotBtnText": "前往快照列表", + "snapshotSuccessNotice": "可在工作区上方点击可视化报表查看历史快照" + }, + "detail": { + "copy": "复制", + "dataSourceDesc": "1.数据源算子只有一个下拉框配置项,用于指定一体化平台中已经配置好的集群/节点/数据库/schema信息。 \n\n2.数据源算子可以指向多个基础算子,且只能指向基础算子,所有与之相连的基础算子,在执行增删改查等操作时,都会在该数据源算子配置的数据库中执行SQL。 \n\n3.数据源算子不是必要算子,若一个基础算子没有对应的数据源算子与之相连,则会使用新建数据流时配置的默认数据源进行SQL的执行。 \n\n4.如果修改一个已经连接使用的数据源算子,将有可能导致与之相连的后续所有算子不可用,比如:修改数据源后,新的数据库中不存在之前数据库中的表名,字段等。所以在修改数据源算子时,将会对用户进行二次确认的提示,并需要用户自行检查后续算子的配置是否可用。", + "queryDesc": "1.查询算子的配置包含一个必选下拉框-表名,用户可以从下拉框中选择数据源中包含的所有表名。\n\n2.查询算子可以用过点击加号配置提取字段,效果等同于`SELECT goods_name , brand_type , goods_model from xxx_list` ,如果不配置提取字段,则默认会提取所有字段,效果等同于`SELECT * from xxx_list` 。3.查询算子下方可以连接多个组合算子,但是不可以连接其他基础算子。后续描述中【基础算子为查询算子的序列】即以查询算子作为某条算子流的开始的一系列算子组合。", + "insertDesc": "1.插入算子的配置包含一个必选下拉框-表名,用户可以从下拉框中选择数据源中包含的所有表名。\n\n2.插入算子可以通过点击【添加一条】按钮,添加多条插入数据。\n\n3.插入算子下方可以连接多个组合算子,但是不可以连接其他基础算子。后续描述中【基础算子为插入算子的序列】即以插入算子作为某条算子流的开始的一系列算子组合。", + "updateDesc": "1.更新算子的配置包含一个必选下拉框-表名,用户可以从下拉框中选择数据源中包含的所有表名。\n\n2.更新算子可以用过点击加号配置一个或多个更新字段,更新方式有两种,第一种默认为自定义语句,即将指定字段的值更新为输入框内的内容。另一种为字段模式,可以使用表内其他字段来进行更新操作,通过选择后置字段后方的下拉框(原值、拼接)可以将后置字段的原值覆盖或拼接更新到目标字段上。\n\n3.更新算子一般需要搭配条件算子使用,如果更新算子不连接任何条件算子,则会对整表进行更新。\n\n4.更新算子下方可以连接多个组合算子,但是不可以连接其他基础算子。后续描述中【基础算子为更新算子的序列】即以更新算子作为某条算子流的开始的一系列算子组合。", + "deleteDesc": "1.删除算子的配置包含一个必选下拉框-表名,用户可以从下拉框中选择数据源中包含的所有表名。\n\n2.删除算子一般需要搭配条件算子使用,如果更新算子不连接任何条件算子,则会删除整张表数据。\n\n3.删除算子下方可以连接多个组合算子,但是不可以连接其他基础算子。后续描述中【基础算子为删除算子的序列】即以删除算子作为某条算子流的开始的一系列算子组合。", + "conditionDesc": "1.条件算子的配置至少需要包含一个或条件,每个或条件内至少包含一个或多个且条件。\n\n 2.每个条件由3个部分组成,字段、条件类型、值。其中条件类型和值为用户手动选择或输入,字段将从条件算子上下游连接的算子中获取所有表的字段,举例:在查询算子-条件算子-桥接算子这样的连接序列中,查询算子选择的查询表为USER,而桥接算子将ROLE表桥接进来,那么在条件算子中,可以选择的字段将同时包含USER表和ROLE表的所有字段。\n\n3.条件算子可以同时连接多个,并会同时生效。", + "joinDesc": "1.桥接算子的配置包含需要桥接的表名,用户可以从下拉框中选择数据源中包含的所有表名。\n\n2.桥接算子需要选择桥接类型,桥接类型包含内连接,左外连接,右外连接,全外连接,桥接条件。分别对应`join,left join,right join,full join` 四种语法。\n\n3.桥接条件的选择由3个下拉框组成,第一个为基础算子中选择的表字段,第二个为条件类型一般选择等于即可,第三个为桥接表中的字段。如图示中配置将会得到如下的join语句:`left join goods_type on ( modeling_test_data_sales_list.brand_type = goods_type.type )` \n\n4.桥接算子可以同时连接多个,并会同时生效。\n\n5.连接桥接算子后,会使得其他组合算子在选择配置字段时,可以使用桥接表中的字段,如条件算子。在图示2中可以看到,使用桥接算子后,后续连接的条件算子中选择字段的下拉框(1)处出现了桥接表的表名(灰色字体),在表名下方的(2)处即为桥接表拥有的字段。", + "sortDesc": "1.排序算子的配置至少需要包含一个或多个字段排序设置。\n\n2.每个排序条件由2个部分组成,字段、排序类型。字段将从算子序列中包含的所有字段中选择,排序类型分为三种,升序,降序,中文拼音降序。\n\n3.其中中文拼音降序选项需要数据库有对应的格式支持,具体请查阅openGauss使用手册。\n\n4.排序算子可以同时连接多个,并会同时生效。", + "limitDesc": "1.限条算子的配置由两个输入框组成,跳过前N条记录,以及只提取前M条记录。\n\n2.如图示配置得到的语法结果如下:`select * from modeling_test_data_sales_list limit 1000 offset 100;`", + "groupDesc": "1.分组算子和聚合算子都为组合算子,这两种算子需要搭配使用,所以放在一起说明。\n\n2.分组算子需要至少配置一个或多个字段对数据进行分组,即GROUP BY语法,每个聚合字段的配置包含三个部分,字段名、聚合类型、别名。\n\n3.在分组算子后使用聚合算子,可以对分组后的数据中的其他字段进行聚合函数操作,目前支持三种类型的聚合,求和、最大值、平均值,分别对应语法`SUM(),MAX(),AVG()`\n\n4.分组算子和聚合算子应该在基础算子为查询算子的序列中使用。", + "polymerizationDesc": "1.分组算子和聚合算子都为组合算子,这两种算子需要搭配使用,所以放在一起说明。\n\n2.分组算子需要至少配置一个或多个字段对数据进行分组,即GROUP BY语法,每个聚合字段的配置包含三个部分,字段名、聚合类型、别名。\n\n3.在分组算子后使用聚合算子,可以对分组后的数据中的其他字段进行聚合函数操作,目前支持三种类型的聚合,求和、最大值、平均值,分别对应语法`SUM(),MAX(),AVG()`\n\n4.分组算子和聚合算子应该在基础算子为查询算子的序列中使用。", + "mappingDesc": "1.映射算子的配置至少包含一条或多条映射规则,每条规则由3部分组成,原字段,映射类型(目前只有as),映射别名。\n\n2.映射算子只在基础算子为查询算子的序列中生效。\n\n3.如上图配置得到的语法结果如下:`select goods_name as g_name` ...", + "dictionaryDesc": "1.字典算子的配置包含4个下拉框选择,分别为字典表表名,原字段,匹配字段,转义字段。\n\n2.举例说明,字典表goods_type中包含了每种商品名称(name)、商品类型(type),而主查询的结果中只包含了商品名称(goods_name),在这种情况下我们想要查询结果中出现的是商品类型,而不是商品名称,那么我们就可以使用字典算子来完成字典转义的功能。我们在字典算子中首先选择字典表的表名(goods_type),然后选择需要替换掉的原表字段(goods_name),然后在匹配字段下拉框中选择字典表中与(goods_name)一一对应的字段(name),到此已经完成字典表和原字段的匹配,最后在转义字段中选择需要从字典表中用来替换的字段即可,这里我们选择商品类型(type)。\n\n3.字典算子的转义过程由left join语法完成,以上配置结果得到的完整语句如下:`select *,\"goods_type\".type as goods_name from modeling_test_data_sales_list left join \"goods_type\" on \"goods_type\".name = goods_name ;`" + }, + "panel": { + "datePlaceholder": "请选择日期", + "queryTip1": "如果不选择提取任何字段,则会提取所有字段,等效于 SELECT * FROM。", + "updateTip": "如果不连接条件算子,则会更新整表数据。", + "sortTip1": "排序算子字段选择支持别名,别名字段将带有【as】标识" + }, + "header": { + "gridText": "切换网格背景" + } + }, + "table": { + "index": { + "5mpu30zsqys0": "剪切", + "5mpu30zsrj00": "复制", + "5mpu30zsroc0": "删除", + "5mpu30zsrrw0": "重命名", + "5mpu30zsrvc0": "重置属性", + "5mpu30zss2o0": "表" + } + }, + "components": { + "Fields": { + "5mpu393tc580": "添加字段", + "5mpu393tcr00": "插入字段", + "5mpu393td080": "删除字段", + "5mpu393td3o0": "主键", + "5mpu393tdcs0": "上移", + "5mpu393tdfo0": "下移", + "5mpu393tdi40": "名", + "5mpu393tdk80": "类型", + "5mpu393tdpo0": "长度", + "5mpu393tdsk0": "小数点", + "5mpu393tdus0": "不是null", + "5mpu393tdyc0": "虚拟", + "5mpu393te100": "注释" + }, + "Rename": { + "5mpu3fm9iw00": "重命名", + "5mpu3fm9jbw0": "提交", + "5mpu3fm9jf40": "取消", + "5mpu3fm9jhc0": "表名" + }, + "VisualConfig": { + "5mpu5vnytdc0": "一轴数据来源", + "5mpu5vnytu40": "请选择一轴数据来源", + "5mpu5vnytxk0": "二轴数据来源", + "5mpu5vnytzw0": "请选择二轴数据来源", + "5mpu5vnyu200": "图表类型", + "5mpu5vnyu4g0": "请选择图表类型", + "5mpu5vnyu6k0": "添加配置组", + "5mpu5vnyu8g0": "仓库名称", + "5mpu5vnyuak0": "出库数量", + "5mpu5vnyuck0": "入库数量", + "5mpu5vnyuek0": "年度总数", + "5mpu5vnyugo0": "柱状图", + "5mpu5vnyuig0": "饼图" + }, + "CU": { + "5m6g1w13gjo0": "提交", + "5m6g1w13gz40": "取消", + "5m6g1w13h2w0": "数据流名称", + "5m6g1w13h7c0": "请输入数据流名称", + "5m6g1w13ha80": "默认访问数据库", + "5m6g1w13hdo0": "集群名称/外网IP/数据库名称/Schema", + "5m6g1w13hgc0": "标签", + "5m6g1w13hiw0": "请输入标签", + "5m6g1w13hlc0": "查询", + "5m6g1w13hno0": "更新", + "5m6g1w13hps0": "插入", + "5m6g1w13hso0": "删除", + "5m6g1w13hv40": "添加用户", + "5m6g1w13hyo0": "备注", + "5m6g1w13i180": "请输入备注", + "5m6g1w13i3g0": "管理权限", + "5m6g1w13i640": "开发权限", + "5m6g1w13i8k0": "访客权限", + "5m6g1w13iaw0": "新建数据流", + "5m6g1w13id80": "请输入数据流ID", + "5m6g1w13ihs0": "请选择行为", + "5m6g1w13ik00": "请输入用户", + "5m6g1w13ing0": "修改数据流", + "submitok1": "新建成功!", + "submitok2": "修改成功!" + }, + "CUUser": { + "5m78jvncojc0": "添加", + "5m78jvncp7o0": "取消", + "5m78jvncpc80": "开发人员(测试、未通讯)", + "5m78jvncpfc0": "测试人员(测试、未通讯)", + "5m78jvncpi40": "添加用户", + "5mpu44y3ic40": "添加", + "5mpu44y3iso0": "取消", + "5mpu44y3ivw0": "添加用户" + }, + "Header": { + "5m78qbczrv40": "算子菜单", + "5m78qbczsmk0": "保存", + "5m78qbczsqw0": "删除所选", + "5m78qbczsto0": "撤销", + "5m78qbczswg0": "恢复", + "5m78qbczszg0": "放大", + "5m78qbczt2k0": "缩小", + "5m78qbczt4w0": "居中", + "5m78qbczt7g0": "导入", + "5m78qbczt9s0": "导出", + "5m78qbcztdc0": "导出为JSON文件", + "5m78qbcztg00": "导出为SVG", + "5m78qbcztio0": "导出为JPEG", + "5m78qbcztlk0": "导出为PNG", + "5m78qbcztok0": "查看代码", + "5m78qbcztqs0": "运行", + "5m78qbcztws0": "可视化展示", + "5m78qbcztzk0": "导出JSON文件_", + "5m78qbczu200": "导出为SVG_$", + "5m78qbczu500": "导出为SVG_", + "5m78qbczu8c0": "导入失败,请检查文件内容!" + }, + "ScatterConfig": { + "5m7iiczn8k80": "统计指标", + "5m7iiczn92w0": "单位", + "5m7iiczn97w0": "地理位置配置", + "5m7iiczn9b80": "坐标字段", + "5m7iiczn9dw0": "经纬度偏移", + "5m7iiczn9gk0": "经度", + "5m7iiczn9j80": "纬度", + "5m7iiczn9m40": "缩放比例", + "5m7iiczn9ow0": "高亮前", + "5m7iiczn9rk0": "条", + "5m7iiczn9u40": "坐标类型", + "5m7iiczn9wk0": "上传地图json", + "5m7iiczn9zg0": "坐标系", + "5m7iiczna1w0": "确定要删除?", + "5m7iiczna4o0": "确定", + "5m7iiczna740": "取消", + "5m7iiczna9k0": "删除", + "5m7iicznac00": "映射关系", + "5m7iicznaec0": "经纬度", + "5m7iicznags0": "上传JSON地图文件", + "5m7iicznak00": "上传", + "5m7iicznam80": "地图名", + "5m7iicznaok0": "请输入地图名", + "5m7iicznaqs0": "文件", + "5m7iicznask0": "删除成功!", + "5m7iicznaug0": "请拖拽文件上传", + "5m7iicznawk0": "只允许上传json格式的geo地图文件!", + "5m7iicznayc0": "上传成功!", + "5mjcl4pcs700": "字段", + "5mjcl4pct500": "类型", + "5mjcl4pctbc0": "请选择坐标字段", + "5mjcl4pctfw0": "请选择坐标系", + "5mjcl4pctqg0": "请选择统计指标字段!", + "5mjcl4pcu280": "请选择统计指标类型!", + "5mjcl4pcu7c0": "请选择坐标字段!", + "5mjcl4pcuec0": "请输入完整的经纬度偏移!", + "5mjcl4pcuk00": "请选择坐标系!" + }, + "PieConfig": { + "5m7ij2mkd0w0": "统计指标", + "5m7ij2mkdyw0": "单位", + "5m7ij2mke7w0": "统计维度", + "5m7ij2mkeas0": "自定义维度", + "5m7ij2mkeek0": "取前", + "5m7ij2mkejs0": "展现方式", + "5m7ij2mkemg0": "嵌套维度", + "5m7ij2mkepc0": "排列", + "5m7ij2mkes80": "展示百分比", + "5m7ij2mkeuo0": "是", + "5m7ij2mkex40": "否", + "5mpu2f4wcy80": "字段", + "5mpu2f4wdd40": "类型" + }, + "LineConfig": { + "5mpu292dkhc0": "字段", + "5mpu292dky40": "类型", + "5m7ijyex0ts0": "根据维度", + "5m7ijyex1h80": "排列", + "5m7ijyex1m00": "按时间维度", + "5m7ijyex1oo0": "指定时间字段", + "5m7ijyex1r80": "时间粒度", + "5m7ijyex1ts0": "对比方式", + "5m7ijyex1x40": "统计指标", + "5m7ijyex2040": "单位", + "5m7ijyex22w0": "统计维度", + "5m7ijyex2540": "自定义维度", + "5m7ijyex29k0": "取前", + "5m7ijyex2c00": "请选择X轴配置!", + "5m7ijyex2e80": "请选择统计指标字段!", + "5m7ijyex2gg0": "请选择统计指标类型!", + "5m7ijyex2jc0": "请选择统计指标单位!", + "5m7ijyex2lk0": "配置不完善" + }, + "HeatmapConfig": { + "5m7ikcw34ic0": "统计指标", + "5m7ikcw35840": "单位", + "5m7ikcw35e80": "自定义维度", + "5m7ikcw35hc0": "取前", + "5m7ikcw35kc0": "热力范围" + }, + "GraphConfig": { + "5m7ikjwuoig0": "节点配置", + "5m7ikjwuphs0": "节点ID", + "5m7ikjwupmw0": "节点名称", + "5m7ikjwupsg0": "节点X坐标", + "5m7ikjwupv40": "节点Y坐标", + "5m7ikjwupxw0": "节点尺寸", + "5m7ikjwuq0w0": "关系配置", + "5m7ikjwuq3c0": "出节点", + "5m7ikjwuq600": "入节点", + "5m7ikjwuq8k0": "关系值", + "5mjckw7can80": "请选择节点ID!", + "5mjckw850is0": "请选择节点名称!", + "5mjckw8511c0": "请选择出节点!", + "5mjckw8516w0": "请选择入检点!", + "5mjckw851bo0": "请选择关系值!", + "notice1": "请选择节点尺寸", + "notice2": "请选择节点X坐标", + "notice3": "请选择节点Y坐标" + }, + "CustomDimensionDialog": { + "5m7iksonqnk0": "自定义维度", + "5m7iksonr9s0": "保存报表", + "5m7iksonrew0": "已保存的维度列表", + "5m7iksonri40": "未命名", + "5m7iksonrkg0": "删除", + "5m7iksonros0": "新建维度", + "5m7iksonrrg0": "维度名称:", + "5m7iksonru00": "请输入维度名称", + "5m7iksonrwc0": "字段:", + "5m7iksonryw0": "请选择字段", + "5m7iksons100": "维度设置:", + "5m7iksons3g0": "添加类目", + "5m7iksons5w0": "请输入类目名", + "5m7iksons8g0": "请选择统计类型", + "5m7iksonsak0": "包含", + "5m7iksonscs0": "不包含", + "5m7iksonsf40": "请选择类目值", + "5m7iksonshg0": "保存", + "5m7iksonsk00": "删除成功!", + "5m7iksonsmc0": "保存成功!", + "5m7iksonspg0": "类目名", + "5m7iksonsro0": "统计类型", + "5m7iksonsu00": "类目值", + "5m7iksonsvs0": "操作" + }, + "BarLineConfig": { + "5m7in04pdbs0": "根据维度", + "5m7in04pe240": "排列", + "5m7in04pe6c0": "按时间维度", + "5m7in04pe8s0": "指定时间字段", + "5m7in04pebk0": "时间粒度", + "5m7in04peec0": "对比方式", + "5m7in04pegw0": "图表类型:", + "5m7in04pej80": "折线图", + "5m7in04pemg0": "柱状图", + "5m7in04peoo0": "统计指标", + "5m7in04per40": "单位", + "5m7in04peu00": "统计维度", + "5m7in04pewg0": "自定义维度", + "5m7in04pez40": "取前", + "5m7in04pf1c0": "添加副图", + "5m7in04pf3o0": "应最少有一张副图!", + "5m7in04pf600": "请选择[第", + "5m7in04pf8o0": "个副图]的图表类型!", + "5m7in04pfas0": "个副图]的统计指标字段!", + "5m7in04pfdg0": "个副图]的统计指标类型!", + "5m7in04pfgs0": "个副图]的统计指标单位!", + "5m7in04pfis0": "配置不完善" + }, + "BarConfig": { + "5m7insim1uc0": "根据维度", + "5m7insim2qg0": "排列", + "5m7insim2v40": "按时间维度", + "5m7insim2yc0": "指定时间字段", + "5m7insim3280": "时间粒度", + "5m7insim3500": "对比方式", + "5m7insim37w0": "统计指标", + "5m7insim3ak0": "单位", + "5m7insim3dc0": "统计维度", + "5m7insim3g00": "自定义维度", + "5m7insim3ig0": "取前", + "5m7insim3lc0": "请选择X轴配置!", + "5m7insim3o80": "请选择统计指标字段!", + "5m7insim3qo0": "请选择统计指标类型!", + "5m7insim3sw0": "请选择统计指标单位!", + "5m7insim3vc0": "配置不完善" + }, + "Share": { + "5m7ipfzhwh40": "分享链接", + "5m7ipfzhx540": "复制链接", + "5m7ipfzhx9g0": "关闭", + "5m7ipfzhxd40": "复制成功!" + }, + "CUSnapshot": { + "5m7iplopyjo0": "快照详情" + }, + "CUReportDrag": { + "5m7ipr5lr5s0": "编辑报表", + "5m7ipr5lrw40": "保存报表", + "5m7ipr5ls0w0": "基础配置", + "5m7ipr5ls3k0": "报表名称", + "5m7ipr5ls800": "请输入报表名称", + "5m7ipr5lsao0": "排布方式", + "5m7ipr5lsd40": "请选择排布方式", + "5m7ipr5lsfc0": "自由排布", + "5m7ipr5lsho0": "可用快照" + }, + "CUReport": { + "5m7iq3dyy200": "编辑报表", + "5m7iq3dyzf80": "保存报表", + "5m7iq3dyzls0": "删除", + "5m7iq3dyzps0": "基础配置", + "5m7iq3dyzsg0": "报表名称", + "5m7iq3dyzvw0": "请输入报表名称", + "5m7iq3dyzyo0": "排布方式", + "5m7iq3dz0140": "请选择排布方式", + "5m7iq3dz0400": "自由排布", + "5m7iq3dz0780": "可用快照", + "5m7iq3dz09k0": "保存成功!" + } + }, + "dataflow": { + "index": { + "5m77w0y57io0": "新建数据流", + "5m77w0y5b3k0": "输入数据流ID/名称", + "5m77w0y5brg0": "数据流列表", + "5m77w0y5caw0": "修改", + "5m77w0y5cgg0": "属性", + "5m77w0y5d8k0": "复制", + "5m77w0y5dec0": "分享", + "5m77w0y5dio0": "导出", + "5m77w0y5fq00": "确定要删除?", + "5m77w0y5g400": "确定", + "5m77w0y5g9k0": "取消", + "5m77w0y5ghc0": "删除", + "5m77w0y5in80": "数据流ID/名称", + "5m77w0y5ivk0": "标签", + "5m77w0y5jtg0": "主要类型", + "5m77w0y5mb80": "集群名称", + "5m77w0y5mgc0": "节点标识", + "5m77w0y5n5w0": "最新更新时间", + "5m77w0y5qjs0": "最新更新者", + "5m77w0y5qsw0": "创建时间", + "5m77w0y5rm80": "创建者", + "5m77w0y5rqo0": "备注", + "5m77w0y5ruw0": "操作", + "5m77w0y5rzk0": "查询", + "5m77w0y5s2s0": "更新", + "5m77w0y5s6o0": "插入", + "5m77w0y5sak0": "删除成功!" + } + }, + "share": { + "index": { + "5m78k584nkw0": "一体化平台", + "5m78k584o7w0": "未找到目标的分享页面", + "5m78k584ock0": "没有必需id" + } + }, + "detail": { + "index": { + "5m7apwiz75o0": "初始化画板中....", + "5m7apwiz8b00": "配置项中请传入相关ID" + } + }, + "left-sidebar": { + "index": { + "5m78udtsry80": "搜索" + } + }, + "nodes": { + "BaseNode": { + "5m78v3ubttc0": "剪切", + "5m78v3ubuac0": "复制", + "5m78v3ubuek0": "删除" + } + }, + "utils": { + "index": { + "5m78yfizcxc0": "配置项数据不合法,请完善。", + "5m835tc0m9o0": "线的目标算子不可以是数据源算子", + "5m835tc0n8k0": "数据源算子的线的目标算子只能是基础算子", + "5m835tc0ndo0": "线的目标算子和来源算子不可以都是基础算子", + "5m835tc0nh00": "基础算子和组合算子只能有一根来源算子为自身的线", + "5m835tc0njw0": "基础算子和组合算子只能有一根目标算子为自身的线" + } + }, + "antv": { + "dataSource": "数据源算子", + "query": "查询算子", + "update": "更新算子", + "delete": "删除算子", + "insert": "插入算子", + "condition": "条件算子", + "join": "桥接算子", + "sort": "排序算子", + "limit": "限条算子", + "group": "分组算子", + "polymerization": "聚合算子", + "mapping": "映射算子", + "dictionary": "字典算子" + }, + "config-panel": { + "index": { + "5m7adga7to40": "无配置项" + } + }, + "special": { + "DataSource": { + "5m7adso1d000": "配置参数", + "5m7adso1dv00": "数据源", + "5m7adso1e040": "集群名称/外网IP/数据库名称/Schema", + "5m7adso1e340": "请选择表名", + "5m7adso1e580": "如果不选择将使用默认数据源连接", + "5m7adso1e7s0": "确定修改", + "5m7adso1eag0": "取消修改", + "5m7adso1eeo0": "警告!", + "5m7adso1ehg0": "更换数据源后,需将与前数据源相关的所有算子的配置项进行重新配置!否则无法运行!" + } + }, + "base": { + "QueryOperator": { + "5m7hhed6am80": "配置参数", + "5m7hhed94ik0": "表名", + "5m7hhed94qc0": "请选择表名", + "5m7hhed94tg0": "提取字段", + "5m7hhed94w80": "请选择字段", + "5m7hhed950s0": "输入来源", + "5m7hhed953o0": "来源", + "5m7hhed95680": "别名", + "5m7hhed95b00": "请选择来源", + "5m7hhed95dw0": "请输入别名", + "5m7hhed95go0": "输出配置", + "5m7hhed95jk0": "输出别名", + "5m7hhed95m00": "自定义输出", + "5m7hhed95og0": "条件", + "5m7hhed95zg0": "更换所选的表后,需将与当前算子相关的所有算子的配置项进行重新配置!否则无法运行!", + "5m7hr5m93c80": "确定修改", + "5m7hr5m943c0": "取消修改", + "5m7hr5m948k0": "警告!", + "5m7hr5m94c00": "更换所选的表后,需将与当前查询算子相关的所有算子的配置项进行重新配置!否则无法运行!" + }, + "DeleteOperator": { + "5m7hqy836z40": "配置参数", + "5m7hqy838600": "表名", + "5m7hqy838bg0": "请选择表名", + "5m7hqy838eo0": "提取字段", + "5m7hqy838hc0": "请选择字段", + "5m7hqy838kw0": "确定修改", + "5m7hqy838no0": "取消修改", + "5m7hqy838q40": "警告!", + "5m7hqy838t40": "更换所选的表后,需将与当前删除算子相关的所有算子的配置项进行重新配置!否则无法运行!", + "5maolasd5hk0": "如果不连接条件算子则会删除所有数据" + }, + "InsertOperator": { + "5m7hqzngdeg0": "配置参数", + "5m7hqzngebw0": "表名", + "5m7hqzngeho0": "请选择表名", + "5m7hqzngels0": "插入方式", + "5m7hqzngeoo0": "逐条插入", + "5m7hqzngeto0": "从输入批量插入", + "5m7hqzngewg0": "字段", + "5m7hqzngez40": "请选择字段", + "5m7hqzngf200": "请输入值", + "5m7hqzngf4k0": "添加一条", + "5m7hqzngf7g0": "输入配置", + "5m7hqzngfac0": "接收表", + "5m7hqzngfdc0": "请选择表", + "5m7hqzngfgw0": "确定修改", + "5m7hqzngfjg0": "取消修改", + "5m7hqzngfls0": "警告!", + "5m7hqzngfoo0": "更换所选的表后,需将与当前插入算子相关的所有算子的配置项进行重新配置!否则无法运行!" + }, + "UpdateOperator": { + "5m7hr2f43280": "配置参数", + "5m7hr2f43ro0": "表名", + "5m7hr2f43ws0": "请选择表名", + "5m7hr2f43zo0": "更新字段列表", + "5m7hr2f442c0": "请选择要更新的字段", + "5m7hr2f44580": "请选择值的类型", + "5m7hr2f44a40": "请输入要修改的值", + "5m7hr2f44h40": "请选择字段", + "5m7hr2f44o80": "确定修改", + "5m7hr2f44qo0": "取消修改", + "5m7hr2f44tg0": "警告!", + "5m7hr2f44vk0": "更换所选的表后,需将与当前更新算子相关的所有算子的配置项进行重新配置!否则无法运行!", + "5mav1wd54g00": "原值", + "5mav1wd9z9o0": "自定义语句", + "5mav1wd9zos0": "字段", + "5mav1wd9ztw0": "拼接" + } + }, + "combination": { + "BridgingOperator": { + "5m82a7hpfow0": "属性配置", + "5m82a7hpivk0": "表名", + "5m82a7i01wc0": "请选择表", + "5m82a7i02a80": "类型", + "5m82a7i02f40": "条件:", + "5m82a7i02kw0": "或", + "5m82a7i02p80": "且条件", + "5m82a7i02t40": "请选择字段", + "5m82a7i02xc0": "条件", + "5m82a7i039g0": "条件值或字段", + "5m82a7i03lk0": "内连接", + "5m82a7i03qs0": "取交集", + "5m82a7i03w00": "左外连接", + "5m82a7i041g0": "取基础表所有数据,如果基础表的某行在连接表中没有匹配行,则在相关联的结果集行中连接表的所有选择列表列均为空值", + "5m82a7i044w0": "右外连接", + "5m82a7i047k0": "取连接表所有数据,如果连接表的某行在基础表中没有匹配行,则在相关联的结果集行中基础表的所有选择列表列均为空值", + "5m82a7i04bk0": "全外连接", + "5m82a7i04ec0": "取基础表和链接表的所有数据", + "5m82a7i04k00": "等于", + "5m82a7i04nk0": "不等于", + "5m82a7i04ro0": "小于", + "5m82a7i04ug0": "小于等于", + "5m82a7i04x80": "大于", + "5m82a7i04z80": "大于等于", + "5m82a7i052o0": "包含", + "5m82a7i054o0": "不包含", + "5m82a7i056w0": "为空", + "5m82a7i058w0": "非空" + }, + "ConditionOperator": { + "5m82aq4s7qo0": "配置参数", + "5m82aq4s8cw0": "或", + "5m82aq4s8gw0": "且条件", + "5m82aq4s8js0": "请选择字段", + "5m82aq4s8m40": "条件", + "5m82aq4s8ow0": "输入...", + "5m82aq4s8rc0": "添加或条件", + "5m82aq4s8ts0": "内连接", + "5m82aq4s8wc0": "等于", + "5m82aq4s8yo0": "不等于", + "5m82aq4s9180": "小于", + "5m82aq4s93k0": "小于等于", + "5m82aq4s95s0": "大于", + "5m82aq4s98c0": "大于等于", + "5m82aq4s9ao0": "包含", + "5m82aq4s9cw0": "不包含", + "5m82aq4s9fk0": "为空", + "5m82aq4s9i00": "非空", + "5m82aq4s9fa1": "在列表内", + "5m82aq4s9fa2": "不在列表内", + "5m82aq4s9fk1": "长度小于", + "5m82aq4s9fk2": "长度大于" + }, + "PolymerizationOperator": { + "5m82b2nqsa00": "属性配置", + "5m82b2nqt0k0": "聚合字段(需搭配分组算子使用)", + "5m82b2nqt4k0": "请选择字段", + "5m82b2nqt780": "聚合方式", + "5m82b2nqt9s0": "别名", + "5m82b2nqtcl0": "无", + "5m82b2nqtck0": "取最大值", + "5m82b2nqtck1": "取最小值", + "5m82b2nqtf40": "求和", + "5m82b2nqths0": "取平均值", + "5m82b2nqths1": "条数求和", + "5m82b2nqths2": "截取年份", + "5m82b2nqths3": "截取月份" + }, + "SortOperator": { + "5m82bdo2r0w0": "属性配置", + "5m82bdo2rns0": "排序字段", + "5m82bdo2rs00": "请选择字段", + "5m82bdo2rus0": "条件", + "5m82bdo2rx80": "升序", + "5m82bdo2rzo0": "降序", + "5m82bdo2s2c0": "中文拼音降序" + }, + "DictionaryOperator": { + "5me6g9wp5p40": "属性配置", + "5me6g9wp6xk0": "字典表", + "5me6g9wp74g0": "请选择表名", + "5me6g9wp78o0": "原字段", + "5me6g9wp7cg0": "请选择字段", + "5me6g9wp7gw0": "匹配字段", + "5me6g9wp7lc0": "转义字段" + }, + "GroupOperator": { + "5me6gg5ogk00": "属性配置", + "5me6gg5oh8c0": "分组字段", + "5me6gg5ohjk0": "请选择字段" + }, + "MappingOperator": { + "5me6giyzpck0": "属性配置", + "5me6giyzq4s0": "字段映射", + "5me6giyzqds0": "请选择字段", + "5me6giyzqmo0": "条件", + "5me6giyzqq80": "输入..." + }, + "RestrictionOperator": { + "5me6gnj6b8s0": "属性配置", + "5me6gnj6bpc0": "跳过前", + "5me6gnj6bts0": "条数据", + "5me6gnj6c3w0": "只提取前" + } + }, + "visual-edit": { + "index": { + "5m7ihlv0lrk0": "关闭弹窗", + "5m7ihlv0mig0": "未命名", + "5m7ihlv0mmo0": "保存的配置项", + "5m7ihlv0mpc0": "删除", + "5m7ihlv0ms00": "新建图表", + "5m7ihlv0muo0": "配置参数", + "5m7ihlv0mxo0": "名称", + "5m7ihlv0n0c0": "请输入报表名称", + "5m7ihlv0n2s0": "图表类型", + "5m7ihlv0n5c0": "请选择图表类型", + "5m7ihlv0n7s0": "预览", + "5m7ihlv0na80": "保存配置", + "5m7ihlv0ndo0": "保存快照", + "5m7ihlv0ng80": "选择图表类型", + "5m7ihlv0niw0": "编辑可视化信息", + "5m7ihlv0nl00": "保存成功!", + "5m7ihlv0nnw0": "保存快照成功!", + "5m7ihlv0nqc0": "删除成功!", + "5mpsromgvig0": "请输入图表名称!", + "5mpsromgvxg0": "请选择图表类型!" + } + }, + "visual-list": { + "index": { + "5m7iox8oj6k0": "可视化展示", + "5m7iox8ok400": "快照历史", + "5m7iox8oka00": "删除快照", + "5m7iox8okdw0": "报表", + "5m7iox8okgs0": "名称搜素", + "5m7iox8okmg0": "新建报表", + "5m7iox8okpk0": "修改", + "5m7iox8oksk0": "分享", + "5m7iox8okv80": "删除报表", + "5m7iox8okyc0": "删除成功!", + "5m7iox8ol0w0": "报表名称", + "5m7iox8ol3k0": "排布类型", + "5m7iox8ol680": "创建时间", + "5m7iox8ol8o0": "更新时间", + "5m7iox8olb00": "操作", + "5m7iox8oldc0": "自由排布" + } + }, + "dialogs": { + "Run": { + "5m7iqlci0400": "运行结果:", + "5m7iqlci0rk0": "代码预览运行", + "5m7iqlci0vk0": "运行中...", + "5m7iqlci0y80": "代码预览", + "5m7iqlci10s0": "代码运行" + } + }, + "hooks": { + "index": { + "5m83asj8uws0": "特殊算子", + "5m83asj8vok0": "数据源算子", + "5m83asj8vsw0": "基础算子", + "5m83asjg7480": "查询算子", + "5m83asjg7f40": "更新算子", + "5m83asjg7j80": "字段", + "5m83asjg7m80": "拼接", + "5m83asjg7oo0": "删除算子", + "5m83asjg7rk0": "插入算子", + "5m83asjg7u80": "组合算子", + "5m83asjg7x40": "条件算子", + "5m83asjg7zs0": "桥接算子", + "5m83asjg8280": "排序算子", + "5m83asjg84w0": "限条算子", + "5m83asjg87c0": "分组算子", + "5m83asjg8ak0": "聚合算子", + "5m83asjg8f40": "映射算子", + "5m83asjg8i00": "字典算子" + }, + "visual-edit": { + "5m83coh1cb00": "求和", + "5m83coh1d3k0": "平均值", + "5m83coh1d7c0": "最大值", + "5m83coh1d9s0": "最小值", + "5m83coh1dcg0": "中位数", + "5m83coh1df40": "柱状图", + "5m83coh1dhs0": "折线图", + "5m83coh1dk40": "混合图", + "5m83coh1dmk0": "饼图", + "5m83coh1dqg0": "热力图", + "5m83coh1dso0": "地理坐标", + "5m83coh1dvc0": "关系图", + "5m83coh1dxs0": "同比", + "5m83coh1e080": "环比", + "5m83coh1e2g0": "年", + "5m83coh1e500": "月", + "5m83coh1e7g0": "日", + "5m83coh1e9o0": "时" + } + } + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/locale/zh-CN.ts b/ER-Model/plugins/base-ops/web-ui/src/locale/zh-CN.ts new file mode 100644 index 000000000..a7330f849 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/locale/zh-CN.ts @@ -0,0 +1,6 @@ + +import cn from './locale/zh-cn.json' + +export default { + ...cn +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/main.ts b/ER-Model/plugins/base-ops/web-ui/src/main.ts new file mode 100644 index 000000000..cabea8a44 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/main.ts @@ -0,0 +1,34 @@ +import { createApp } from 'vue' +import ArcoVue from '@arco-design/web-vue' +import ArcoVueIcon from '@arco-design/web-vue/es/icon' +import VueGridLayout from 'vue-grid-layout' +import globalComponents from '@/components' +import App from './App.vue' +import router from './router' +import store from './store' +import i18n from './locale/index' +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +import opendesign2 from '@computing/opendesign2' +import '@computing/opendesign2/themes/es/css' +import 'element-plus/dist/index.css' +import '@/assets/icons' +import '@/api/interceptor' + +import '@arco-design/web-vue/dist/arco.less' +import '@/assets/style/global.less' + +const app = createApp(App) + +app.use(ArcoVue, {}) +app.use(ArcoVueIcon) +app.use(ElementPlus) +app.use(opendesign2) + +app.use(router) +app.use(store) +app.use(globalComponents) +app.use(i18n) +app.use(VueGridLayout) + +app.mount('#app') diff --git a/ER-Model/plugins/base-ops/web-ui/src/router/constants.ts b/ER-Model/plugins/base-ops/web-ui/src/router/constants.ts new file mode 100644 index 000000000..257a55e66 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/router/constants.ts @@ -0,0 +1,2 @@ +export const DEFAULT_LAYOUT = () => import('@/layout/default-layout.vue') +export const ParentView = () => import('@/layout/parent-view/index.vue') diff --git a/ER-Model/plugins/base-ops/web-ui/src/router/index.ts b/ER-Model/plugins/base-ops/web-ui/src/router/index.ts new file mode 100644 index 000000000..088ff27cb --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/router/index.ts @@ -0,0 +1,34 @@ +import { createRouter, createWebHistory } from 'vue-router' + +import { appRoutes } from './routes' + +const router = createRouter({ + history: createWebHistory('/static-plugin/base-ops/'), + routes: [ + { + path: '/', + name: 'index', + redirect: '/modeling/dataflow', + meta: { + title: '', + requiresAuth: false, + keepAlive: false + } + }, + // web share + { + path: '/modeling/visualization/report/share/:id', + name: 'ModelingPreview', + component: () => import('@/views/modeling/dataflow/child/share/index.vue'), + meta: { + requiresAuth: false + } + }, + ...appRoutes + ], + scrollBehavior () { + return { top: 0 } + } +}) + +export default router diff --git a/ER-Model/plugins/base-ops/web-ui/src/router/routes/index.ts b/ER-Model/plugins/base-ops/web-ui/src/router/routes/index.ts new file mode 100644 index 000000000..f84e4cb1d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/router/routes/index.ts @@ -0,0 +1,14 @@ +import type { RouteRecordNormalized } from 'vue-router' + +const context = require.context('./modules', true, /\.ts$/) +const modules = context.keys() + +function formatModules (_modules: any, result: RouteRecordNormalized[]) { + _modules.forEach((key: any) => { + result = result.concat(context(key).default) + }) + console.log(result) + return result +} + +export const appRoutes: RouteRecordNormalized[] = formatModules(modules, []) diff --git a/ER-Model/plugins/base-ops/web-ui/src/router/routes/modules/modeling.ts b/ER-Model/plugins/base-ops/web-ui/src/router/routes/modules/modeling.ts new file mode 100644 index 000000000..bf89c0592 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/router/routes/modules/modeling.ts @@ -0,0 +1,71 @@ +import { DEFAULT_LAYOUT } from '@/router/constants' +import { AppRouteRecordRaw } from '../types' + +const MODELING: AppRouteRecordRaw = { + path: '/modeling', + name: 'modeling', + component: DEFAULT_LAYOUT, + redirect: '/modeling/basic', + meta: { + title: '业务设计', + requiresAuth: false, + icon: 'icon-relation', + order: 2 + }, + children: [ + { + path: '/modeling/databasedesign', + name: 'ModelingDatabasedesign', + component: () => import('@/views/modeling/databasedesign/index.vue'), + meta: { + title: '数据建模', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/modeling/workflow', + name: 'ModelingWorkflow', + component: () => import('@/views/modeling/workflow/index.vue'), + meta: { + title: '业务流设计', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/modeling/dataflow', + name: 'ModelingDataFlow', + component: () => import('@/views/modeling/dataflow/index.vue'), + meta: { + title: '数据流设计', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/modeling/dataflow/detail', + name: 'ModelingDataFlowDetail', + component: () => import('@/views/modeling/dataflow/child/detail/index.vue'), + meta: { + title: '数据流详情', + hideInMenu: true, + requiresAuth: false, + roles: ['*'] + } + }, + { + path: '/modeling/modelDesign', + name: 'ModelDesign', + component: () => import('@/views/modeling/modelDesign/index.vue'), + meta: { + title: '模型设计', + hideInMenu: true, + requiresAuth: false, + roles: ['*'] + } + } + ] +} + +export default MODELING diff --git a/ER-Model/plugins/base-ops/web-ui/src/router/routes/modules/monitor.ts b/ER-Model/plugins/base-ops/web-ui/src/router/routes/modules/monitor.ts new file mode 100644 index 000000000..38dabb4bb --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/router/routes/modules/monitor.ts @@ -0,0 +1,109 @@ +import { DEFAULT_LAYOUT } from '@/router/constants' +import { AppRouteRecordRaw } from '../types' + +const MONITOR: AppRouteRecordRaw = { + path: '/monitor', + name: 'monitor', + component: DEFAULT_LAYOUT, + redirect: '/monitor/basic', + meta: { + title: '监控运维', + requiresAuth: false, + icon: 'icon-robot', + order: 3 + }, + children: [ + { + path: '/monitor/dailyOps', + name: 'DailyOps', + component: () => import('@/views/monitor/operation/DailyOps.vue'), + meta: { + title: '集群运维', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/monitor/taskDetails', + name: 'taskDetails', + component: () => import('@/views/monitor/operation/taskDetails.vue'), + meta: { + title: '任务详情', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/monitor/basic', + name: 'MonitorBasic', + component: () => import('@/views/monitor/monitor/index.vue'), + meta: { + title: '集群监控', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/monitor/logCenter', + name: 'LogCenter', + component: () => import('@/views/monitor/logCenter/index.vue'), + meta: { + title: '日志分析', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/monitor/slowSql', + name: 'SlowSql', + component: () => import('@/views/monitor/slowSql/index.vue'), + meta: { + title: 'SQL诊断', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/monitor/wdr', + name: 'Wdr', + component: () => import('@/views/monitor/wdr/index.vue'), + meta: { + title: 'WDR报告', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/monitor/customControl', + name: 'CustomControl', + component: () => import('@/views/monitor/customControl/index.vue'), + meta: { + title: '自定义控制台', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/monitor/packageManage', + name: 'PackageManage', + component: () => import('@/views/monitor/packageManage/index.vue'), + meta: { + title: '安装包管理', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/monitor/operation/step', + name: 'step', + component: () => import('@/views/monitor/operation/task/index.vue'), + meta: { + title: '步骤页面', + requiresAuth: true, + roles: ['*'] + } + }, + ] +} + +export default MONITOR diff --git a/ER-Model/plugins/base-ops/web-ui/src/router/routes/modules/ops.ts b/ER-Model/plugins/base-ops/web-ui/src/router/routes/modules/ops.ts new file mode 100644 index 000000000..5e0db8215 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/router/routes/modules/ops.ts @@ -0,0 +1,100 @@ +import { DEFAULT_LAYOUT } from '@/router/constants' +import { AppRouteRecordRaw } from '../types' + +const OPS: AppRouteRecordRaw = { + path: '/ops', + name: 'ops', + component: DEFAULT_LAYOUT, + redirect: '/ops/simpleInstall', + meta: { + title: '安装部署', + requiresAuth: false, + icon: 'icon-storage', + order: 1 + }, + children: [ + { + path: '/ops/simpleInstall', + name: 'SimpleInstall', + component: () => import('@/views/ops/simpleInstall/index.vue'), + meta: { + title: '一键安装', + requiresAuth: true, + roles: ['*'], + ignoreCache: true + } + }, + { + path: '/ops/install', + name: 'OpsInstall', + component: () => import('@/views/ops/install/Install.vue'), + meta: { + title: '集群安装', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/ops/batchInstall', + name: 'batchInstall', + component: () => import('@/views/ops/batchInstall/index.vue'), + meta: { + title: '批量安装', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/ops/upgrade', + name: 'Upgrade', + component: () => import('@/views/ops/upgrade/index.vue'), + meta: { + title: '批量升级', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/ops/migrate', + name: 'Migrate', + component: () => import('@/views/ops/migrate/index.vue'), + meta: { + title: '集群迁移', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/ops/backup', + name: 'Backup', + component: () => import('@/views/ops/backup/index.vue'), + meta: { + title: '备份恢复', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/ops/olk', + name: 'Olk', + component: () => import('@/views/ops/resource/openLooKeng/index.vue'), + meta: { + title: 'OpenLooKeng管理', + requiresAuth: true, + roles: ['*'] + } + }, + { + path: '/ops/disasterClusterInstall', + name: 'dtClusterInstall', + component: () => import('@/views/ops/dtClusterInstall/index.vue'), + meta: { + title: '容灾集群安装', + requiresAuth: true, + roles: ['*'] + } + }, + ] +} + +export default OPS diff --git a/ER-Model/plugins/base-ops/web-ui/src/router/routes/modules/resource.ts b/ER-Model/plugins/base-ops/web-ui/src/router/routes/modules/resource.ts new file mode 100644 index 000000000..c0eb7778b --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/router/routes/modules/resource.ts @@ -0,0 +1,39 @@ +import { DEFAULT_LAYOUT } from "@/router/constants"; +import { AppRouteRecordRaw } from "../types"; + +const OPS: AppRouteRecordRaw = { + path: "/resource", + name: "resource", + component: DEFAULT_LAYOUT, + redirect: "/resource/disasterClusterManager", + meta: { + title: "资源中心", + requiresAuth: false, + icon: "icon-storage", + order: 1, + }, + children: [ + { + path: "/resource/deviceManager", + name: "DeviceManager", + component: () => import("@/views/resource/diskArray/index.vue"), + meta: { + title: "磁阵管理", + requiresAuth: true, + roles: ["*"], + }, + }, + { + path: "/resource/disasterClusterManager", + name: "DisasterClusterManager", + component: () => import("@/views/resource/dtCluster/index.vue"), + meta: { + title: "容灾集群管理", + requiresAuth: true, + roles: ["*"], + }, + }, + ], +}; + +export default OPS; diff --git a/ER-Model/plugins/base-ops/web-ui/src/router/routes/types.ts b/ER-Model/plugins/base-ops/web-ui/src/router/routes/types.ts new file mode 100644 index 000000000..5903ce22c --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/router/routes/types.ts @@ -0,0 +1,20 @@ +import { defineComponent } from 'vue' +import type { RouteMeta, NavigationGuard } from 'vue-router' + +export type Component = + | ReturnType + | (() => Promise) + | (() => Promise); + +export interface AppRouteRecordRaw { + path: string; + name?: string | symbol; + meta?: RouteMeta; + redirect?: string; + component: Component | string; + children?: AppRouteRecordRaw[]; + alias?: string | string[]; + props?: Record; + beforeEnter?: NavigationGuard | NavigationGuard[]; + fullPath?: string; +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/router/typings.d.ts b/ER-Model/plugins/base-ops/web-ui/src/router/typings.d.ts new file mode 100644 index 000000000..9b07a09ae --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/router/typings.d.ts @@ -0,0 +1,16 @@ +import 'vue-router' + +declare module 'vue-router' { + interface RouteMeta { + roles?: string[]; // Controls roles that have access to the page + requiresAuth: boolean; // Whether login is required to access the current page (every route must declare) + icon?: string; // The icon show in the side menu + title?: string; // The locale name show in side menu and breadcrumb + hideInMenu?: boolean; // If true, it is not displayed in the side menu + hideChildrenInMenu?: boolean; // if set true, the children are not displayed in the side menu + activeMenu?: string; // if set name, the menu will be highlighted according to the name you set + order?: number; // Sort routing menu items. If set key, the higher the value, the more forward it is + noAffix?: boolean; // if set true, the tag will not affix in the tab-bar + ignoreCache?: boolean; // if set true, the page will not be cached + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/shims-vue.d.ts b/ER-Model/plugins/base-ops/web-ui/src/shims-vue.d.ts new file mode 100644 index 000000000..75c3b046e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/shims-vue.d.ts @@ -0,0 +1,19 @@ +/* eslint-disable */ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} + +declare module 'jsencrypt/bin/jsencrypt.min' + +declare module '*.svg' +declare module '*.png' +declare module '*.jpg' +declare module '*.jpeg' +declare module '*.gif' +declare module '*.bmp' +declare module '*.tiff' +declare module '*.json' + +declare module 'vue-grid-layout' diff --git a/ER-Model/plugins/base-ops/web-ui/src/store/index.ts b/ER-Model/plugins/base-ops/web-ui/src/store/index.ts new file mode 100644 index 000000000..89077d870 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/store/index.ts @@ -0,0 +1,9 @@ +import { createPinia } from 'pinia' +import useOpsStore from './modules/ops' +import useAppStore from './modules/app' +import useUserStore from './modules/user' + +const pinia = createPinia() + +export { useOpsStore, useAppStore, useUserStore } +export default pinia diff --git a/ER-Model/plugins/base-ops/web-ui/src/store/modules/app/index.ts b/ER-Model/plugins/base-ops/web-ui/src/store/modules/app/index.ts new file mode 100644 index 000000000..18bbb7f6e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/store/modules/app/index.ts @@ -0,0 +1,86 @@ +import { defineStore } from 'pinia' +import type { RouteRecordNormalized } from 'vue-router' +import defaultSettings from '@/config/settings.json' +import { getMenuList } from '@/api/user' +import { filterAsyncRouter } from '@/utils/route' +import { AppState } from './types' + +const useAppStore = defineStore('app', { + state: (): AppState => ({ ...defaultSettings }), + + getters: { + appCurrentSetting (state: AppState): AppState { + return { ...state } + }, + appDevice (state: AppState) { + return state.device + }, + appAsyncMenus (state: AppState): RouteRecordNormalized[] { + return state.serverMenu as unknown as RouteRecordNormalized[] + } + }, + + actions: { + // Update app settings + updateSettings (partial: Partial) { + // @ts-ignore-next-line + this.$patch(partial) + }, + // Change theme color + toggleTheme (dark: boolean) { + if (dark) { + this.theme = 'dark' + document.body.setAttribute('arco-theme', 'dark') + } else { + this.theme = 'light' + document.body.removeAttribute('arco-theme') + } + }, + toggleDevice (device: string) { + this.device = device + }, + toggleMenu (value: boolean) { + this.hideMenu = value + }, + async fetchServerMenuConfig () { + const locale = localStorage.getItem('locale') || 'zh-CN' + try { + const { data } = await getMenuList() + data.unshift({ + path: '/', + name: 'Home', + component: 'DEFAULT_LAYOUT', + redirect: '/dashboard', + meta: { + title: locale === 'zh-CN' ? '首页' : 'Home', + requiresAuth: false, + icon: 'home', + order: 0, + hideChildrenInMenu: true + }, + children: [ + { + path: '/dashboard', + name: 'Dashboard', + component: 'ops/home/Index', + meta: { + title: locale === 'zh-CN' ? '首页' : 'Home', + requiresAuth: true, + roles: ['*'] + } + } + ] + }) + const routes = filterAsyncRouter(data) + this.serverMenu = routes + } catch (error) { + console.log(error) + } + }, + clearServerMenu () { + this.serverMenu = [] + } + } +}) + +export default useAppStore diff --git a/ER-Model/plugins/base-ops/web-ui/src/store/modules/app/types.ts b/ER-Model/plugins/base-ops/web-ui/src/store/modules/app/types.ts new file mode 100644 index 000000000..e958c3555 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/store/modules/app/types.ts @@ -0,0 +1,19 @@ +import type { RouteRecordNormalized } from 'vue-router' + +export interface AppState { + theme: string; + colorWeak: boolean; + navbar: boolean; + menu: boolean; + hideMenu: boolean; + menuCollapse: boolean; + footer: boolean; + themeColor: string; + menuWidth: number; + globalSettings: boolean; + device: string; + tabBar: boolean; + menuFromServer: boolean; + serverMenu: RouteRecordNormalized[]; + [key: string]: unknown; +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/store/modules/modeling/common.ts b/ER-Model/plugins/base-ops/web-ui/src/store/modules/modeling/common.ts new file mode 100644 index 000000000..6111334e9 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/store/modules/modeling/common.ts @@ -0,0 +1,68 @@ +import { KeyValue } from '@/api/modeling/request' +import { defineStore } from 'pinia' + +export const useModelCommonStore = defineStore(`modeling-common`, { + state: () => { + return { + currentSelectData: {}, + selectNode: null as KeyValue | null, + showConfig: false, + isRegisterNodes: [] as Array, + rules: {} as KeyValue, + nodeEventData: null as any, + i18n: null as any + } + }, + getters: { + getI18n: state => state.i18n, + getCurrentSelectData: state => state.currentSelectData, + getSelectNode: state => state.selectNode, + getRules: state => state.rules + }, + actions: { + setI18n (i18n: any) { + this.$patch(state => { + state.i18n = i18n + }) + }, + // nodeEvent + nodeEvent (data: any) { + this.nodeEventData = data + }, + // add register node + setIsResigterNodes (node: string) { + this.$patch(state => { + state.isRegisterNodes.push(node) + }) + }, + // modify seleced node + setSelectNode (nodeInfo: KeyValue | null, showConfig: boolean) { + this.$patch(state => { + state.showConfig = showConfig + state.selectNode = nodeInfo + }) + }, + // + setSelectData () { + const data = [ + { id: 5, name: 'hs.article', fields: [ + { id: 1, name: 'article.type' }, + { id: 2, name: 'article.title' }, + { id: 3, name: 'article.content' } + ] }, + { id: 1, name: 'hs.user_test', fields: [ + { id: 1, name: 'user.name' }, + { id: 2, name: 'user.sex' }, + { id: 3, name: 'user.age' } + ] } + ] + this.currentSelectData = data + }, + // + setRule (key: string, rule: any) { + this.$patch(state => { + state.rules[key] = rule + }) + } + } +}) diff --git a/ER-Model/plugins/base-ops/web-ui/src/store/modules/modeling/data-flow.ts b/ER-Model/plugins/base-ops/web-ui/src/store/modules/modeling/data-flow.ts new file mode 100644 index 000000000..e403fac32 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/store/modules/modeling/data-flow.ts @@ -0,0 +1,186 @@ +import { getTableFields, getTableList } from '@/api/modeling' +import { defineStore } from 'pinia' +import { Message } from '@arco-design/web-vue' +import { KeyValue } from '@/api/modeling/request' +import { Graph } from '@antv/x6' + +interface stateType { + graph: any, + dataFlowInfo: KeyValue, + rawData: KeyValue, + useData: KeyValue, + useDatabase: string, + useTable: string[], + fieldsAlias: { database: string, table: string, name: string }[] +} +const defineState: stateType = { + graph: null, + dataFlowInfo: {}, + rawData: {}, + useData: {}, + useDatabase: ``, + useTable: [], + fieldsAlias: [] +} + +export const useDataFlowStore = defineStore(`dataFlow`, { + state () { + return JSON.parse(JSON.stringify(defineState)) + }, + getters: { + getTableSelectList: (state: stateType) => { + if (state.useDatabase) { + return state.useData[state.useDatabase] + } else return [] + }, + getFieldsSelectList: (state: stateType) => { + if (state.useDatabase && state.useTable.length > 0) { + const database = state.useData[state.useDatabase] + const tableList: { group: string, fields: KeyValue[] }[] = [] + state.useTable.forEach((item: string) => { + const arr = { group: item, fields: [] } as any + const index = database.findIndex((item2: KeyValue) => item2.tablename === item) + if (index !== -1) { + arr.fields = database[index].fields + } + if (arr.fields && Array.isArray(arr.fields)) { + state.fieldsAlias.forEach(item => { + if (item.table === arr.group) { + arr.fields.push({ + name: item.name + }) + } + }) + } + tableList.push(arr) + }) + console.log(tableList) + console.log(state.fieldsAlias) + return tableList + } else return [] + }, + getFlowDataInfo: (state: stateType) => state.dataFlowInfo, + getFieldsByTable: (state: stateType) => (tableName: string | number) => { + if (state.useDatabase) { + const database = state.useData[state.useDatabase] + if (tableName === 0 || tableName === '0') { + const index = database.findIndex((item2: KeyValue) => item2.tablename === state.useTable[0]) + if (index === -1) return [] + else return database[index].fields + } else { + const index = database.findIndex((item2: KeyValue) => item2.tablename === tableName) + if (index === -1) return [] + else return database[index].fields + } + } else return [] + }, + getUseTable: (state: stateType) => state.useTable, + getUseDatabase: (state: stateType) => state.useDatabase + }, + actions: { + clearFieldsAlias () { + this.$patch((state: stateType) => { + state.fieldsAlias = [] + }) + }, + setFieldsAlias (field: { database: string, table: string, name: string, value: string }) { + this.$patch((state: stateType) => { + const i = state.fieldsAlias.findIndex(item => (item.database === field.database && item.table === field.table && item.name === field.value)) + if (i === -1) { + state.fieldsAlias.push({ + database: field.database, + table: field.table, + name: field.value + '[as]' + }) + } + }) + }, + setGraph (graph: Graph) { + this.$patch((state: stateType) => { + state.graph = graph + }) + }, + initState () { + return new Promise((resolve) => { + this.$patch((state: stateType) => { + state.useData = [] + state.useDatabase = `` + state.useTable = [] + state.rawData = [] + state.dataFlowInfo = [] + resolve(true) + }) + }) + }, + clearUse () { + return new Promise((resolve) => { + this.$patch((state: stateType) => { + state.useDatabase = `` + state.useTable = [] + resolve(true) + }) + }) + }, + setFlowDataInfo (info: KeyValue) { + this.$patch((state: stateType) => state.dataFlowInfo = info) + }, + setDatabaseInfo (dbName: string, clusterNodeId: string, schema: string) { + return new Promise((resolve, reject) => { + this.$patch((state: stateType) => { + state.useDatabase = `${dbName}|,|${clusterNodeId}|,|${schema}` + state.useTable = [] + if (!Object.keys(state.rawData).includes(`${dbName}|,|${clusterNodeId}|,|${schema}`)) { + getTableList(dbName, clusterNodeId, schema).then((res: KeyValue) => { + if (Number(res.code) === 200) { + const arr = res.data.map((item: KeyValue) => ({ ...item, tables: {}})) + state.rawData[`${dbName}|,|${clusterNodeId}|,|${schema}`] = arr + state.useData[`${dbName}|,|${clusterNodeId}|,|${schema}`] = arr + resolve(true) + } else { + Message.error(`database not found`) + reject(true) + } + }).catch(() => { + Message.error(`database not found`) + reject(true) + }) + } else { + resolve(true) + } + }) + }) + }, + setDatabaseTableInfo (tableName: string, params?: KeyValue) { + return new Promise(resolve => { + this.$patch((state: stateType) => { + if (tableName) { + if (params && (params.tableKey || params.tableKey === 0)) state.useTable[params.tableKey] = tableName + else { + if (!(params && params.noUse)) state.useTable.push(tableName) + } + + const index = state.rawData[state.useDatabase].findIndex((item: KeyValue) => item.tablename === tableName) + if (!Object.keys(state.rawData[state.useDatabase][index].tables).includes(tableName)) { + getTableFields(`${state.useDatabase.split('|,|').join('/')}/${tableName}`).then((res: KeyValue) => { + resolve(true) + state.rawData[state.useDatabase][index].fields = res.data + state.useData[state.useDatabase][index].fields = res.data + }) + } else resolve(false) + } else resolve(false) + }) + }) + }, + removeDatabaseTable (tableName: string) { + return new Promise(resolve => { + this.$patch((state: stateType) => { + const index = state.useTable.indexOf(tableName) + if (index !== -1) { + state.useTable.splice(index, 1) + resolve(true) + } else resolve(false) + }) + }) + } + } +}) diff --git a/ER-Model/plugins/base-ops/web-ui/src/store/modules/ops/index.ts b/ER-Model/plugins/base-ops/web-ui/src/store/modules/ops/index.ts new file mode 100644 index 000000000..82874512b --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/store/modules/ops/index.ts @@ -0,0 +1,114 @@ +import { defineStore } from 'pinia' +import { OpenLookengInstallConfig, MinimalistInstallConfig, LiteInstallConfig, EnterpriseInstallConfig, OpenGaussVersionEnum, InstallModeEnum, DeployTypeEnum, WsConnectTypeEnum } from '@/types/ops/install' +import { KeyValue } from '@antv/x6/lib/types' + +export const installStore = defineStore('install', { + state: () => { + return { + installContext: { + // only for ui use + id: '', + installType: 'install', + installOs: 'CENTOS_X86_64', + openGaussVersion: OpenGaussVersionEnum.LITE, + openGaussVersionNum: '5.0.0', + installMode: InstallModeEnum.OFF_LINE, + packageId: '', + packagePath: '', + packageName: '', + installPackagePath: '', + deployType: DeployTypeEnum.SINGLE_NODE, + isEnvSeparate: false, + envPath: '', + clusterId: '', + clusterName: '' + }, + installPackageHasDownload: [] as any[], + minimalistInstallConfig: {} as MinimalistInstallConfig, + liteInstallConfig: {} as LiteInstallConfig, + enterpriseInstallConfig: {} as EnterpriseInstallConfig, + openLookengInstallConfig: {} as OpenLookengInstallConfig + } + }, + + getters: { + getInstallConfig: state => state.installContext, + getMiniConfig: state => state.minimalistInstallConfig, + getLiteConfig: state => state.liteInstallConfig, + getEnterpriseConfig: state => state.enterpriseInstallConfig, + getOpenLookengInstallConfig: state => state.openLookengInstallConfig, + getHasDownload: state => state.installPackageHasDownload, + getInstallParam: (state): KeyValue => { + const param = { + installContext: { + minimalistInstallConfig: {}, + liteInstallConfig: {}, + enterpriseInstallConfig: {} + }, + wsConnectType: WsConnectTypeEnum.COMMAND_EXEC, + businessId: 'installLog' + } + if (state.installContext.openGaussVersion === OpenGaussVersionEnum.MINIMAL_LIST) { + param.installContext.minimalistInstallConfig = state.minimalistInstallConfig + } else if (state.installContext.openGaussVersion === OpenGaussVersionEnum.LITE) { + param.installContext.liteInstallConfig = state.liteInstallConfig + } else { + param.installContext.enterpriseInstallConfig = state.enterpriseInstallConfig + } + Object.assign(param.installContext, state.installContext) + return param + } + }, + + actions: { + setInstallContext(installInfo: KeyValue) { + this.$patch(state => { + Object.assign(state.installContext, installInfo) + }) + }, + addDownloadInstallPackage(installPackageUrl: string) { + this.$patch(state => { + state.installPackageHasDownload.push(installPackageUrl) + }) + }, + setMiniConfig(config: MinimalistInstallConfig) { + this.$patch(state => { + const param = { + clusterName: config.clusterName + } + this.setInstallContext(param) + state.minimalistInstallConfig.port = config.port + state.minimalistInstallConfig.databaseUsername = config.databaseUsername + state.minimalistInstallConfig.databasePassword = config.databasePassword + state.minimalistInstallConfig.installPackagePath = config.installPackagePath + state.minimalistInstallConfig.nodeConfigList = [] + state.minimalistInstallConfig.nodeConfigList = config.nodeConfigList + }) + }, + setLiteConfig(config: LiteInstallConfig) { + this.$patch(state => { + state.liteInstallConfig.port = config.port + state.liteInstallConfig.databaseUsername = config.databaseUsername + state.liteInstallConfig.databasePassword = config.databasePassword + state.liteInstallConfig.installPackagePath = config.installPackagePath + state.liteInstallConfig.nodeConfigList = [] + state.liteInstallConfig.nodeConfigList = config.nodeConfigList + }) + }, + setEnterpriseConfig(config: EnterpriseInstallConfig) { + this.$patch(state => { + Object.assign(state.enterpriseInstallConfig, config) + }) + }, + setOpenLookengConfig(config: OpenLookengInstallConfig) { + this.$patch(state => { + Object.assign(state.openLookengInstallConfig, config) + }) + }, + resetInstall() { + this.$reset() + } + } +}) + +export default installStore diff --git a/ER-Model/plugins/base-ops/web-ui/src/store/modules/ops/upgrade.ts b/ER-Model/plugins/base-ops/web-ui/src/store/modules/ops/upgrade.ts new file mode 100644 index 000000000..de1dc0bb0 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/store/modules/ops/upgrade.ts @@ -0,0 +1,46 @@ +import { defineStore } from 'pinia' +import { KeyValue } from '@/types/global' + +export interface upClusterInfo { + id: string, + currentVersion: string, + rootPassword: string, + diskUsed: number, + osCheckResult: string, + checkLoading: boolean, + hostList: Array +} + +export interface hostInfo { + isMaster: boolean, + hostId: string, + publicIp: string +} + +export interface targetVersion { + packageId: string, + packageUrl: string, + packagePath: KeyValue, + packageVersionNum: string +} + +export const useUpgradeStore = defineStore('upgrade', { + state: () => { + return { + upClusterList: [] as upClusterInfo[], + upTargetVersion: {} as targetVersion + } + }, + getters: { + getUpClusterList: state => state.upClusterList, + getUpTargetVersion: state => state.upTargetVersion + }, + actions: { + setUpgradeContext(upClusterList: Array, upTargetVersion: targetVersion) { + this.$patch(state => { + state.upClusterList = [...upClusterList] + Object.assign(state.upTargetVersion, upTargetVersion) + }) + } + } +}) diff --git a/ER-Model/plugins/base-ops/web-ui/src/store/modules/user/index.ts b/ER-Model/plugins/base-ops/web-ui/src/store/modules/user/index.ts new file mode 100644 index 000000000..0fdc8360f --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/store/modules/user/index.ts @@ -0,0 +1,63 @@ +import { defineStore } from 'pinia' +import { + logout as userLogout, + getUserInfo +} from '@/api/user' +import { clearToken } from '@/utils/auth' +import { removeRouteListener } from '@/utils/route-listener' +import useAppStore from '../app' + +const useUserStore = defineStore('user', { + state: (): any => ({ + userName: '', + nickName: '', + userId: undefined, + phonenumber: '', + email: undefined, + remark: undefined, + avatar: undefined + }), + + getters: { + userInfo (state: any): any { + return { ...state } + } + }, + + actions: { + // Set user's information + setInfo (partial: any) { + this.$patch(partial) + }, + + // Reset user's information + resetInfo () { + this.$reset() + }, + + // Get user's information + async info () { + const res = await getUserInfo() + + this.setInfo(res.data) + }, + + logoutCallBack () { + const appStore = useAppStore() + this.resetInfo() + clearToken() + removeRouteListener() + appStore.clearServerMenu() + }, + // Logout + async logout () { + try { + await userLogout() + } finally { + this.logoutCallBack() + } + } + } +}) + +export default useUserStore diff --git a/ER-Model/plugins/base-ops/web-ui/src/types/echarts.ts b/ER-Model/plugins/base-ops/web-ui/src/types/echarts.ts new file mode 100644 index 000000000..e72dbaad0 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/types/echarts.ts @@ -0,0 +1,10 @@ +import { CallbackDataParams } from 'echarts/types/dist/shared' + +export interface ToolTipFormatterParams extends CallbackDataParams { + axisDim: string; + axisIndex: number; + axisType: string; + axisId: string; + axisValue: string; + axisValueLabel: string; +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/types/global.ts b/ER-Model/plugins/base-ops/web-ui/src/types/global.ts new file mode 100644 index 000000000..f6c943038 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/types/global.ts @@ -0,0 +1,41 @@ +export interface KeyValue { + [key: string]: any +} + +export interface AnyObject { + [key: string]: unknown; +} + +export interface Options { + value: unknown; + label: string; +} + +export interface NodeOptions extends Options { + children?: NodeOptions[]; +} + +export interface GetParams { + body: null; + type: string; + url: string; +} + +export interface PostData { + body: string; + type: string; + url: string; +} + +export interface Pagination { + current: number; + pageSize: number; + total?: number; +} + +export type TimeRanger = [string, string]; + +export interface GeneralChart { + xAxis: string[]; + data: Array<{ name: string; value: number[] }>; +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/types/mock.ts b/ER-Model/plugins/base-ops/web-ui/src/types/mock.ts new file mode 100644 index 000000000..9219ef450 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/types/mock.ts @@ -0,0 +1,5 @@ +export interface MockParams { + url: string; + type: string; + body: string; +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/types/ops/install.ts b/ER-Model/plugins/base-ops/web-ui/src/types/ops/install.ts new file mode 100644 index 000000000..a5088f9fe --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/types/ops/install.ts @@ -0,0 +1,238 @@ +import { KeyValue } from '@/api/modeling/request' + +export enum OpenGaussVersionEnum { + ENTERPRISE = 'ENTERPRISE', + MINIMAL_LIST = 'MINIMAL_LIST', + LITE = 'LITE', + OPENlOOKENG = 'OPENlOOKENG' +} + +export enum InstallModeEnum { + ON_LINE = 'ON_LINE', + OFF_LINE = 'OFF_LINE' +} + +export enum DeployTypeEnum { + CLUSTER = 'CLUSTER', + SINGLE_NODE = 'SINGLE_NODE' +} + +export enum ClusterRoleEnum { + MASTER = 'MASTER', + SLAVE = 'SLAVE', + CASCADE = 'CASCADE', + PENDING = 'PENDING', + UNKNOWN = 'UNKNOWN', + DOWN = 'DOWN', + ABNORMAL = 'ABNORMAL', + STOPED = 'STOPED' +} + +export enum CMStateEnum { + Primary = 'Primary', + Standby = 'Standby', + Down = 'Down' +} +export enum WsConnectTypeEnum { + DOWNLOAD_INSTALL_PACKAGE = 'DOWNLOAD_INSTALL_PACKAGE', + SSH = 'SSH', + COMMAND_EXEC = 'COMMAND_EXEC' +} + +export enum DatabaseKernelArch { + MASTER_SLAVE = 'MASTER_SLAVE', + SHARING_STORAGE = 'SHARING_STORAGE' +} + +export enum ConnectTypeEnum { + TCP = 'TCP', + RDMA = 'RDMA' +} + +export interface InstallContext { + // version + openGaussVersion?: OpenGaussVersionEnum; + // openGauss version number + openGaussVersionNum?: string; + // install way + installMode?: InstallModeEnum; + // path + installPackagePath?: string; + // deploy + deployType: DeployTypeEnum; + + clusterId: string; + + clusterName: string; +} + +export interface MiniNodeConfig { + clusterRole: string; + hostId: string; + rootPassword: string; + privateIp: string; + publicIp: string; + installUserId: string; + installUserName: string; + installPath: string; + dataPath: string; + isInstallDemoDatabase: boolean; +} + +export interface MinimalistInstallConfig { + clusterName: string; + port: number; + installPackagePath: string; + databaseUsername: string; + databasePassword: string; + databasePasswordOriginal: string; + nodeConfigList: MiniNodeConfig[] +} + +export interface LiteNodeConfig { + clusterRole: string; + hostId: string; + rootPassword: string; + privateIp: string; + publicIp: string; + installUserId: string; + installUserName: string; + installPath: string; + dataPath: string; + port: number; + databasePassword: string; + isEnvSeparate: boolean, + envPath: string; +} + +export interface LiteInstallConfig { + clusterName: string; + installPackagePath: string; + port: number; + databaseUsername: string; + databasePassword: string; + nodeConfigList: LiteNodeConfig[] +} + +export interface EnterpriseInstallNodeConfig { + clusterRole: string; + hostId: string; + rootPassword: string; + privateIp: string; + publicIp: string; + hostname: string; + installUserId: string; + installUsername: string; + azId: string; + azName: string; + isCMMaster: boolean; + cmDataPath: string; + cmPort: number; + dataPath: string; + xlogPath: string; + azPriority: number; +} + +export interface SharingStorageInstallConfig { + dssHome: string; + dssVgName: string; + dssDataLunPath: string; + xlogVgName: string; + xlogLunPath: string; + cmSharingLunPath: string; + cmVotingLunPath: string; + interconnectType: ConnectTypeEnum; + rdmaConfig: string; +} + +export interface EnterpriseInstallConfig { // eslint-disable-line + installPath: string; + installPackagePath: string; + logPath: string; + tmpPath: string; + omToolsPath: string; + corePath: string; + port: number; + enableDCF: boolean; + databaseUsername: string; + databasePassword: string; + isInstallCM: boolean, + azId: string; + azName: string; + databaseKernelArch: DatabaseKernelArch; + sharingStorageInstallConfig: SharingStorageInstallConfig; + nodeConfigList: EnterpriseInstallNodeConfig[]; +} + +export interface OpenLookengInstallConfig { + name: '', + dadInstallHostId: '', + dadInstallUsername: '', + dadTarId: '', + dadInstallPath: '/data/install', + dadInstallPassword: '', + dadRootPassword: '', + dadPort: 8080, + ssInstallHostId: '', + ssInstallUsername: '', + ssInstallPassword: '', + ssRootPassword: '', + ssInstallPath: '/data/install', + ssUploadPath: '/data/tar', + ssTarId: '', + ssPort: 1234, + zkTarId: '', + zkPort: 2181, + dsConfig: Array>, + tableName: '', + columns: '', + olkInstallHostId: '', + olkInstallUsername: '', + olkInstallPassword: '', + olkRootPassword: '', + olkInstallPath: '/data/install', + olkUploadPath: '/data/tar', + olkTarId: '', + olkPort: 2345, + remark: '', + dadNeedEncrypt: false, + ssNeedEncrypt: false, + olkNeedEncrypt: false, + ruleYaml: '', + olkParamConfig: OlkParamConfig +} +export interface OlkParamConfig { + xmx: '16G', + maxMemory: '50GB', + maxTotalMemory: '50GB', + maxMemoryPer: '10GB', + maxTotalMemoryPer: '10GB' +} + +export interface ShardingDsConfig { + host: '', + port: '', + dbName: '', + username: '' + password: '' +} + +export interface downloadPackage { + resourceUrl: string, + targetPath: string, + fileName: string, + connectType: string | 'DOWNLOAD_INSTALL_PACKAGE', + businessId: string | 'downloadPackage' +} + +export enum WsConnectType { + DOWNLOAD_INSTALL_PACKAGE = 'DOWNLOAD_INSTALL_PACKAGE', + SSH = 'SSH', + COMMAND_EXEC = 'COMMAND_EXEC' +} + +export interface SSHBody { + hostId: string, + wsConnectType: WsConnectType, + businessId: string +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/types/os.ts b/ER-Model/plugins/base-ops/web-ui/src/types/os.ts new file mode 100644 index 000000000..01401f395 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/types/os.ts @@ -0,0 +1,17 @@ +export enum OS { + All = 'all', + CENTOS = 'centos', + OPEN_EULER = 'openEuler' +} + +export enum CpuArch { + NOARCH = 'noarch', + X86_64 = 'x86_64', + AARCH64 = 'aarch64' +} + +export enum SysArch { + X86 = 'x86', + ARM = 'arm', + X86_OPEN_EULER = 'x86_openEuler' +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/types/resource/host.ts b/ER-Model/plugins/base-ops/web-ui/src/types/resource/host.ts new file mode 100644 index 000000000..49e211ca5 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/types/resource/host.ts @@ -0,0 +1,12 @@ +export interface hostData { + publicIp: string, + privateIp: string, + password: string, + azId: string, + remark: string +} +export interface hostUserData { + hostId: string, + username: string, + password: string +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/types/resource/package.ts b/ER-Model/plugins/base-ops/web-ui/src/types/resource/package.ts new file mode 100644 index 000000000..12872e153 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/types/resource/package.ts @@ -0,0 +1,23 @@ +export enum PackageType { + OPENGAUSS = 'openGauss', + ZOOKEEPER = 'zookeeper', + SHARDING_PROXY = 'shardingProxy', + OPENLOOKENG = 'openLooKeng', + DISTRIBUTE_DEPLOY = 'dad' +} + +export enum PackageState { + OK, + ERROR +} + +export interface UploadInfo { + realPath: string, + realName: string, + name: string, + file: File +} + +export enum DefaultOpenGauss { + VERSION = '5.0.0' +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/types/uploadInfo.ts b/ER-Model/plugins/base-ops/web-ui/src/types/uploadInfo.ts new file mode 100644 index 000000000..20a2e2b54 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/types/uploadInfo.ts @@ -0,0 +1,6 @@ +export interface UploadInfo { + realPath: string, + realName: string, + name: string, + file: File +} \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/utils/auth.ts b/ER-Model/plugins/base-ops/web-ui/src/utils/auth.ts new file mode 100644 index 000000000..420e7a6af --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/utils/auth.ts @@ -0,0 +1,19 @@ +const TOKEN_KEY = 'opengauss-token' + +const isLogin = () => { + return !!localStorage.getItem(TOKEN_KEY) +} + +const getToken = () => { + return localStorage.getItem(TOKEN_KEY) +} + +const setToken = (token: string) => { + localStorage.setItem(TOKEN_KEY, token) +} + +const clearToken = () => { + localStorage.removeItem(TOKEN_KEY) +} + +export { isLogin, getToken, setToken, clearToken } diff --git a/ER-Model/plugins/base-ops/web-ui/src/utils/env.ts b/ER-Model/plugins/base-ops/web-ui/src/utils/env.ts new file mode 100644 index 000000000..f371dc5c6 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/utils/env.ts @@ -0,0 +1,3 @@ +const debug = process.env.NODE_ENV !== 'production' + +export default debug diff --git a/ER-Model/plugins/base-ops/web-ui/src/utils/errorCode.ts b/ER-Model/plugins/base-ops/web-ui/src/utils/errorCode.ts new file mode 100644 index 000000000..c81634ca8 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/utils/errorCode.ts @@ -0,0 +1,6 @@ +export default { + '401': 'Authentication failed, unable to access system resource', + '403': 'No permission for current operation', + '404': 'The access resource does not exist', + 'default': 'unknown error, please feedback to administrator' +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/utils/event.ts b/ER-Model/plugins/base-ops/web-ui/src/utils/event.ts new file mode 100644 index 000000000..64badb76a --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/utils/event.ts @@ -0,0 +1,27 @@ +export function addEventListen ( + target: Window | HTMLElement, + event: string, + handler: EventListenerOrEventListenerObject, + capture = false +) { + if ( + target.addEventListener && + typeof target.addEventListener === 'function' + ) { + target.addEventListener(event, handler, capture) + } +} + +export function removeEventListen ( + target: Window | HTMLElement, + event: string, + handler: EventListenerOrEventListenerObject, + capture = false +) { + if ( + target.removeEventListener && + typeof target.removeEventListener === 'function' + ) { + target.removeEventListener(event, handler, capture) + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/utils/fetch.ts b/ER-Model/plugins/base-ops/web-ui/src/utils/fetch.ts new file mode 100644 index 000000000..ecec33a43 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/utils/fetch.ts @@ -0,0 +1,4 @@ +// Credentials carrying login status must be include +export default function fetch (url: any, options: any) { + return window.fetch(url, { ...options, credentials: 'omit' }) +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/utils/index.ts b/ER-Model/plugins/base-ops/web-ui/src/utils/index.ts new file mode 100644 index 000000000..2131346bd --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/utils/index.ts @@ -0,0 +1,25 @@ +type TargetContext = '_self' | '_parent' | '_blank' | '_top'; + +export const openWindow = ( + url: string, + opts?: { target?: TargetContext; [key: string]: any } +) => { + const { target = '_blank', ...others } = opts || {} + window.open( + url, + target, + Object.entries(others) + .reduce((preValue: string[], curValue) => { + const [key, value] = curValue + return [...preValue, `${key}=${value}`] + }, []) + .join(',') + ) +} + +export const regexUrl = new RegExp( + '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$', + 'i' +) + +export default null diff --git a/ER-Model/plugins/base-ops/web-ui/src/utils/jsencrypt.ts b/ER-Model/plugins/base-ops/web-ui/src/utils/jsencrypt.ts new file mode 100644 index 000000000..7d192f994 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/utils/jsencrypt.ts @@ -0,0 +1,33 @@ +import JSEncrypt from 'jsencrypt/bin/jsencrypt.min' +import { getEntryKey } from '@/api/ops' +import { KeyValue } from '@/types/global' + +const isEncryptedData = (data: string | null) => { + if (!data) return false; + // Check Base64 format (encrypted data is usually Base64 encoded) + const base64Check = /^(?=.*[+/=])[A-Za-z0-9+/=]+$/.test(data) && + data.length % 4 === 0 && + /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(data) + return base64Check; +} +// host password encryption +export async function encryptPassword(pwd: string) { + if (!pwd) { + return + } + const isEncryptTxt = isEncryptedData(pwd); + if (isEncryptTxt) { + return pwd; + } + let publicKey = '' + const getPublicKey: KeyValue = await getEntryKey() + if (Number(getPublicKey.code) === 200 && getPublicKey.key) { + const newKey = getPublicKey.key + publicKey = newKey + } + const encryptor = new JSEncrypt() + encryptor.setPublicKey(publicKey) + return encryptor.encrypt(pwd) + +} + diff --git a/ER-Model/plugins/base-ops/web-ui/src/utils/monitor.ts b/ER-Model/plugins/base-ops/web-ui/src/utils/monitor.ts new file mode 100644 index 000000000..942217959 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/utils/monitor.ts @@ -0,0 +1,28 @@ +import { App, ComponentPublicInstance } from 'vue' +import axios from 'axios' + +export default function handleError (Vue: App, baseUrl: string) { + if (!baseUrl) { + return + } + Vue.config.errorHandler = ( + err: unknown, + instance: ComponentPublicInstance | null, + info: string + ) => { + // send error info + axios.post(`${baseUrl}/report-error`, { + err, + instance, + info + // location: window.location.href, + // message: err.message, + // stack: err.stack, + // browserInfo: getBrowserInfo(), + // user info + // dom info + // url info + // ... + }) + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/utils/route-listener.ts b/ER-Model/plugins/base-ops/web-ui/src/utils/route-listener.ts new file mode 100644 index 000000000..babfa1e10 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/utils/route-listener.ts @@ -0,0 +1,31 @@ +/** + * Listening to routes alone would waste rendering performance. Use the publish-subscribe model for distribution management + * + */ +import mitt, { Handler } from 'mitt' +import type { RouteLocationNormalized } from 'vue-router' + +const emitter = mitt() + +const key = Symbol('ROUTE_CHANGE') + +let latestRoute: RouteLocationNormalized + +export function setRouteEmitter (to: RouteLocationNormalized) { + emitter.emit(key, to) + latestRoute = to +} + +export function listenerRouteChange ( + handler: (route: RouteLocationNormalized) => void, + immediate = true +) { + emitter.on(key, handler as Handler) + if (immediate && latestRoute) { + handler(latestRoute) + } +} + +export function removeRouteListener () { + emitter.off(key) +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/utils/route.ts b/ER-Model/plugins/base-ops/web-ui/src/utils/route.ts new file mode 100644 index 000000000..a3eb7eb21 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/utils/route.ts @@ -0,0 +1,70 @@ +import { DEFAULT_LAYOUT, ParentView } from '@/router/constants' + +const loadView = (view: string) => { // Routing lazy loading + return () => import( + /* webpackExclude: /\.d.ts$/ */ + `@/views/${view}` + ) +} + +function filterChildren (childrenMap: any[], lastRouter: any = false) { + let children: any[] = [] + childrenMap.forEach((el) => { + if (el.children && el.children.length) { + if (el.component === 'ParentView') { + el.children.forEach((c: any) => { + c.path = el.path + '/' + c.path + if (c.children && c.children.length) { + children = children.concat(filterChildren(c.children, c)) + return + } + children.push(c) + }) + return + } + } + if (lastRouter) { + el.path = lastRouter.path + '/' + el.path + } + children = children.concat(el) + }) + return children +} + +// Traverse the routing string from the background and convert it to a component object +export function filterAsyncRouter (asyncRouterMap: any[], lastRouter = false, type = false) { // eslint-disable-line + return asyncRouterMap.filter(route => { + if (type && route.children) { + route.children = filterChildren(route.children) + } + if (route.component) { + // Layout Component Special Handling + if (route.component === 'DEFAULT_LAYOUT') { + route.component = DEFAULT_LAYOUT + } else if (route.component === 'ParentView') { + route.component = ParentView + } else { + route.component = loadView(route.component) + } + } + if (route.children != null && route.children && route.children.length) { + route.children = filterAsyncRouter(route.children, route, type) + } else { + delete route['children'] + delete route['redirect'] + } + return true + }) +} + +export function getQuery (url: string) { + const obj: any = {} + const i = url.indexOf('?') + 1 + const str = url.substr(i) + const arr = str.split('&') + for (let i = 0; i < arr.length; i++) { + const arr1 = arr[i].split('=') + obj[arr1[0]] = arr1[1] + } + return obj +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/utils/setup-mock.ts b/ER-Model/plugins/base-ops/web-ui/src/utils/setup-mock.ts new file mode 100644 index 000000000..0bb278f9d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/utils/setup-mock.ts @@ -0,0 +1,23 @@ +import debug from './env' + +export default ({ mock, setup }: { mock?: boolean; setup: () => void }) => { + if (mock !== false && debug) setup() +} + +export const successResponseWrap = (data: unknown) => { + return { + data, + status: 'ok', + msg: 'success', + code: 20000 + } +} + +export const failResponseWrap = (data: unknown, msg: string, code = 50000) => { + return { + data, + status: 'fail', + msg, + code + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/utils/websocket.ts b/ER-Model/plugins/base-ops/web-ui/src/utils/websocket.ts new file mode 100644 index 000000000..a95f9c206 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/utils/websocket.ts @@ -0,0 +1,145 @@ +export type Callback = (e: Event) => void +export type MessageCallback = (e: RT) => void + +interface Ioptions { + url: string | null + heartTime?: number + heartMsg?: string + isReconnect?: boolean + isRestory?: boolean + reconnectTime?: number + reconnectCount?: number + openCb?: Callback + closeCb?: Callback + messageCb?: MessageCallback + errorCb?: Callback +} + +export class Heart { + heartTimeOut!: number + ServerHeartTimeOut!: number + timeout = 5000 + reset(): void { + clearTimeout(this.heartTimeOut) + clearTimeout(this.ServerHeartTimeOut) + } + + start(cb: Callback): void { + this.heartTimeOut = setTimeout((e: Event) => { + cb(e) + this.ServerHeartTimeOut = setTimeout((e: Event) => { + cb(e) + this.reset() + this.start(cb) + }, this.timeout) + }, this.timeout) + } +} + +export default class Socket extends Heart { + ws!: WebSocket + + reconnectTimer = 0 + reconnectCount = 10 + + options: Ioptions = { + url: null, + heartTime: 5000, + heartMsg: 'ping', + isReconnect: true, + isRestory: false, + reconnectTime: 5000, + reconnectCount: 5, + openCb: (e: Event) => { + console.log('connect success callback::::', e) + }, + closeCb: (e: Event) => { + console.log('close callback::::', e) + }, + messageCb: (e: RT) => { + console.log('message callback::::', e) + }, + errorCb: (e: Event) => { + console.log('error callback::::', e) + } + } + + constructor(ops: Ioptions) { + super() + Object.assign(this.options, ops) + this.create() + } + + create(): void { + if (!('WebSocket' in window)) { + throw new Error('The current browser does not support it and cannot be used') + } + if (!this.options.url) { + throw new Error('The address does not exist, and the channel cannot be established') + } + // this.ws = null + console.log('get locaion host: ', window.location.host, window.location.protocol) + let wsUrl + if (process.env.NODE_ENV === 'development') { + // change by yourself + wsUrl = `ws://${process.env.VUE_APP_WS_BASE_URL}/ws/base-ops/${this.options.url}` + } else { + const wsPrefix = window.location.protocol.includes('https') ? 'wss' : 'ws' + wsUrl = `${wsPrefix}://${window.location.host}/ws/base-ops/${this.options.url}` + } + this.ws = new WebSocket(wsUrl) + this.onopen(this.options.openCb as Callback) + this.onclose(this.options.closeCb as Callback) + this.onmessage(this.options.messageCb as MessageCallback) + } + + onopen(callback: Callback): void { + this.ws.onopen = event => { + if (typeof callback === 'function') { + callback(event) + } else { + typeof this.options.openCb === 'function' && this.options.openCb(event) + } + } + } + + onclose(callback: Callback): void { + this.ws.onclose = event => { + if (typeof callback === 'function') { + callback(event) + } else { + typeof this.options.closeCb === 'function' && this.options.closeCb(event) + } + } + } + + onerror(callback: Callback): void { + this.ws.onerror = event => { + if (typeof callback === 'function') { + callback(event) + } else { + typeof this.options.errorCb === 'function' && this.options.errorCb(event) + } + } + } + + onmessage(callback: MessageCallback): void { + this.ws.onmessage = (event: MessageEvent) => { + const strMessage = event.data + if (typeof callback === 'function') { + callback(strMessage) + } + } + } + + send(data: T | string): void { + console.log('doNothing', data) + } + + destroy(): void { + super.reset() + clearTimeout(this.reconnectTimer) + this.options.isRestory = true + this.ws.close() + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/components/DSelectOT.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/components/DSelectOT.vue new file mode 100644 index 000000000..74f68ce0d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/components/DSelectOT.vue @@ -0,0 +1,12 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/components/DragModal.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/components/DragModal.vue new file mode 100644 index 000000000..198a54022 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/components/DragModal.vue @@ -0,0 +1,232 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/components/OverflowTooltip.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/components/OverflowTooltip.vue new file mode 100644 index 000000000..ed66bac53 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/components/OverflowTooltip.vue @@ -0,0 +1,40 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/databasedesign/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/databasedesign/index.vue new file mode 100644 index 000000000..29e532a6e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/databasedesign/index.vue @@ -0,0 +1,35 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/Command.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/Command.vue new file mode 100644 index 000000000..0ece465cd --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/Command.vue @@ -0,0 +1,7 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/Header.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/Header.vue new file mode 100644 index 000000000..bf7f8c989 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/Header.vue @@ -0,0 +1,284 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/VisualConfig.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/VisualConfig.vue new file mode 100644 index 000000000..b10f7b798 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/VisualConfig.vue @@ -0,0 +1,77 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/base/DeleteOperator.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/base/DeleteOperator.vue new file mode 100644 index 000000000..89fd99652 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/base/DeleteOperator.vue @@ -0,0 +1,198 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/base/InsertOperator.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/base/InsertOperator.vue new file mode 100644 index 000000000..1485ff0ac --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/base/InsertOperator.vue @@ -0,0 +1,266 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/base/QueryOperator.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/base/QueryOperator.vue new file mode 100644 index 000000000..96d9325bd --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/base/QueryOperator.vue @@ -0,0 +1,229 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/base/UpdateOperator.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/base/UpdateOperator.vue new file mode 100644 index 000000000..820b5dcee --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/base/UpdateOperator.vue @@ -0,0 +1,277 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/BridgingOperator.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/BridgingOperator.vue new file mode 100644 index 000000000..961acff18 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/BridgingOperator.vue @@ -0,0 +1,211 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/ConditionOperator.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/ConditionOperator.vue new file mode 100644 index 000000000..7be68fc14 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/ConditionOperator.vue @@ -0,0 +1,165 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/DictionaryOperator.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/DictionaryOperator.vue new file mode 100644 index 000000000..a74535c98 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/DictionaryOperator.vue @@ -0,0 +1,119 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/GroupOperator.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/GroupOperator.vue new file mode 100644 index 000000000..5ab1edb4f --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/GroupOperator.vue @@ -0,0 +1,86 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/MappingOperator.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/MappingOperator.vue new file mode 100644 index 000000000..03391dd8c --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/MappingOperator.vue @@ -0,0 +1,104 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/PolymerizationOperator.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/PolymerizationOperator.vue new file mode 100644 index 000000000..809109967 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/PolymerizationOperator.vue @@ -0,0 +1,129 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/RestrictionOperator.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/RestrictionOperator.vue new file mode 100644 index 000000000..f39fbad27 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/RestrictionOperator.vue @@ -0,0 +1,69 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/SortOperator.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/SortOperator.vue new file mode 100644 index 000000000..495d33ebb --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/combination/SortOperator.vue @@ -0,0 +1,113 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/special/DataSource.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/special/DataSource.vue new file mode 100644 index 000000000..2486de158 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/special/DataSource.vue @@ -0,0 +1,219 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/table/Base.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/table/Base.vue new file mode 100644 index 000000000..0ece465cd --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/components/table/Base.vue @@ -0,0 +1,7 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/index.vue new file mode 100644 index 000000000..36312fe1d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/index.vue @@ -0,0 +1,158 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/utils/tool.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/utils/tool.ts new file mode 100644 index 000000000..9bdb1e664 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/config-panel/utils/tool.ts @@ -0,0 +1,19 @@ +import { KeyValue } from '@/api/modeling/request' +import { Cell } from '@antv/x6' +export const checkDisabled = (checkArr: Array, checkValue: number | string, comparedKey?: string) : boolean => { + if (!checkArr || checkArr.length === 0) return false + if (typeof checkArr[0] === 'string' || typeof checkArr[0] === 'number') { + return checkArr.indexOf(checkValue) !== -1 + } else { + const arr = checkArr as Array + return arr.findIndex((field: KeyValue) => field[comparedKey ? comparedKey : 'value'] === checkValue) !== -1 + } +} +/** When filling in the content, save it in the data of the node itself */ +export const saveData = (key: string, value: any, cell: Cell) => { + console.log(key, value, cell) + if (cell) { + const cellData = cell.getData() + cell?.setData({ ...cellData, [key]: JSON.parse(JSON.stringify(value)) }, { overwrite: true }) + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/Run.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/Run.vue new file mode 100644 index 000000000..2583f850d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/Run.vue @@ -0,0 +1,268 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/BarConfig.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/BarConfig.vue new file mode 100644 index 000000000..3689d774e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/BarConfig.vue @@ -0,0 +1,255 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/BarLineConfig.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/BarLineConfig.vue new file mode 100644 index 000000000..55e816afe --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/BarLineConfig.vue @@ -0,0 +1,299 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/Common.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/Common.vue new file mode 100644 index 000000000..7d4d2824d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/Common.vue @@ -0,0 +1,8 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/CustomDimensionDialog.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/CustomDimensionDialog.vue new file mode 100644 index 000000000..aed524aac --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/CustomDimensionDialog.vue @@ -0,0 +1,294 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/GraphConfig.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/GraphConfig.vue new file mode 100644 index 000000000..f38a7731f --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/GraphConfig.vue @@ -0,0 +1,210 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/HeatmapConfig.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/HeatmapConfig.vue new file mode 100644 index 000000000..9b18d15c0 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/HeatmapConfig.vue @@ -0,0 +1,199 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/LineConfig.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/LineConfig.vue new file mode 100644 index 000000000..235bc60a4 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/LineConfig.vue @@ -0,0 +1,244 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/PieConfig.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/PieConfig.vue new file mode 100644 index 000000000..02706a8ce --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/PieConfig.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/ScatterConfig.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/ScatterConfig.vue new file mode 100644 index 000000000..eb1bbb887 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/components/ScatterConfig.vue @@ -0,0 +1,354 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/hooks/chart.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/hooks/chart.ts new file mode 100644 index 000000000..4fbf84914 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/hooks/chart.ts @@ -0,0 +1,47 @@ +import { EChartsOption } from 'echarts' +import * as echarts from 'echarts' +let chart: echarts.ECharts +export const initChart = (dom: any, cb: any) => { + chart = echarts.init(dom) + chart.on('georoam', () => { + if (chart && chart.getOption()) { + const nowOption: any = chart.getOption() + console.log(nowOption) + if (nowOption && nowOption.geo && nowOption.geo[0] && nowOption.geo[0].center && nowOption.geo[0].center[0] && nowOption.geo[0].center[1] && nowOption.geo[0].zoom) { + const center = nowOption.geo[0].center + center[0] = center[0].toFixed(5) + center[1] = center[1].toFixed(5) + const zoom = nowOption.geo[0].zoom.toFixed(5) + console.log(cb) + cb('georoam', { center, zoom }) + } else if (nowOption.series[0].center && nowOption.series[0].center[0] && nowOption.series[0].center[1] && nowOption.series[0].zoom) { + const center = nowOption.series[0].center + center[0] = center[0].toFixed(5) + center[1] = center[1].toFixed(5) + const zoom = nowOption.series[0].zoom.toFixed(5) + cb('georoam', { center, zoom }) + } else { + console.log(123) + } + } + }) +} +export const renderChart = (option: EChartsOption, mapData?: any) => { + if (mapData) echarts.registerMap(mapData.name, mapData.data) + chart && chart.setOption(option, true) + console.log(option) +} +export const clearChart = () => { + chart && chart.clear() +} +export const showLoading = () => { + chart && chart.showLoading({ + maskColor: 'rgba(255, 255, 255, 0.2)' + }) +} +export const hideLoading = () => { + chart && chart.hideLoading() +} +export const getBase64 = () => { + if (chart) return chart.getDataURL() +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/hooks/options.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/hooks/options.ts new file mode 100644 index 000000000..c50c8bca9 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/hooks/options.ts @@ -0,0 +1,56 @@ +import { KeyValue } from '@/types/global' +import i18n from '@/locale/index' +export const indicatorType = [ + { value: 'sum', label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1cb00') }, + { value: 'avg', label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1d3k0') }, + { value: 'max', label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1d7c0') }, + { value: 'min', label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1d9s0') }, + { value: 'mid', label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1dcg0') } +] +export const indicatorTypeSum = [ + { value: 'sum', label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1cb00') } +] +export const chartTypes = [ + { value: 'bar', label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1df40') }, + { value: 'line', label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1dhs0') }, + { value: 'mix', label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1dk40') }, + { value: 'pie', label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1dmk0') }, + { value: 'heatmap', label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1dqg0') }, + { value: 'scatter', label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1dso0') }, + { value: 'graph', label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1dvc0') } +] +let queryData: KeyValue[] = [] +let cacheQueryData: any = [] +let cacheQueryDataKeys: string[] = [] +export const getQueryDataByKey = (key: string, page: number, size: number) => { + let pageSize = 10 + if (size) pageSize = size + let arr = [] + if (cacheQueryDataKeys.includes(key)) { + arr = JSON.parse(JSON.stringify(cacheQueryData[key])) + } else { + arr = Array.from(new Set(queryData.map((item: KeyValue) => item[key]))) + .filter((item: KeyValue) => item && item.trim()) + .map((item: KeyValue) => ({ label: item, value: item })) + cacheQueryData[key] = JSON.parse(JSON.stringify(arr)) + cacheQueryDataKeys.push(key) + } + return arr.splice((page - 1) * pageSize, pageSize) +} +export const setQueryData = (data: KeyValue[]) => { + if (data && Array.isArray(data)) { + queryData = data + cacheQueryData = [] + cacheQueryDataKeys = [] + } +} +export const compareWays = [ + { value: 1, label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1dxs0') }, + { value: 2, label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1e080') } +] +export const particles = [ + { value: 1, label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1e2g0') }, + { value: 2, label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1e500') }, + { value: 3, label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1e7g0') }, + { value: 4, label: i18n.global.t('modeling.hooks.visual-edit.5m83coh1e9o0') } +] diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/index.vue new file mode 100644 index 000000000..8a2eeffa8 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-edit/index.vue @@ -0,0 +1,642 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/CUReport.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/CUReport.vue new file mode 100644 index 000000000..cc05e5372 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/CUReport.vue @@ -0,0 +1,284 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/CUReportDrag.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/CUReportDrag.vue new file mode 100644 index 000000000..3fb6da01e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/CUReportDrag.vue @@ -0,0 +1,328 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/CUSnapshot.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/CUSnapshot.vue new file mode 100644 index 000000000..5555ddaf3 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/CUSnapshot.vue @@ -0,0 +1,69 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/Share.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/Share.vue new file mode 100644 index 000000000..c878f3073 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/Share.vue @@ -0,0 +1,60 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/ShareDialog.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/ShareDialog.vue new file mode 100644 index 000000000..cbd7af61b --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/components/ShareDialog.vue @@ -0,0 +1,60 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/index.vue new file mode 100644 index 000000000..718fe0cb5 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/dialogs/visual-list/index.vue @@ -0,0 +1,332 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/index.vue new file mode 100644 index 000000000..2c4e6218c --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/index.vue @@ -0,0 +1,321 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/BaseNode.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/BaseNode.vue new file mode 100644 index 000000000..0f58b6397 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/BaseNode.vue @@ -0,0 +1,320 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/index.vue new file mode 100644 index 000000000..31807c135 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/index.vue @@ -0,0 +1,17 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/table/components/Fields.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/table/components/Fields.vue new file mode 100644 index 000000000..63b23e602 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/table/components/Fields.vue @@ -0,0 +1,177 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/table/components/Rename.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/table/components/Rename.vue new file mode 100644 index 000000000..cba28d712 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/table/components/Rename.vue @@ -0,0 +1,54 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/table/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/table/index.vue new file mode 100644 index 000000000..ee281760d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/table/index.vue @@ -0,0 +1,131 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/view/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/view/index.vue new file mode 100644 index 000000000..1c5355e26 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/components/left-sidebar/nodes/view/index.vue @@ -0,0 +1,10 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/hooks/flowGraph.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/hooks/flowGraph.ts new file mode 100644 index 000000000..1f785cb55 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/hooks/flowGraph.ts @@ -0,0 +1,278 @@ +import { useModelCommonStore } from '@/store/modules/modeling/common' +import { useDataFlowStore } from '@/store/modules/modeling/data-flow' +import { saveJsonData } from '@/api/modeling' +import { Cell, Edge, FunctionExt, Graph, Node, Shape } from '@antv/x6' +import { Options } from '@antv/x6/lib/graph/options' +import { RouteLocationNormalizedLoaded } from 'vue-router' +import { KeyValue } from '@antv/x6/lib/types' +import { PropsOptions } from '../types' +import { lineConnect } from '../utils/validate' +import { getCheckedNodes } from '../utils/tools' +const dFStore = useDataFlowStore() +const mCStore = useModelCommonStore() +const edgeConfig = { + attrs: { + line: { + stroke: '#ECC300', strokeWidth: 2, + targetMarker: { name: 'classic', size: 8 } + } + }, + zIndex: 0, + connector: { name: 'rounded' }, + router: { name: 'manhattan' } +} + +let gridType = 1 +const gridModal = { + size: 10, visible: true, type: 'doubleMesh', + args: [ + { color: 'rgba(136, 136, 136, .2)', thickness: 1 }, + { color: 'rgba(136, 136, 136, .2)', thickness: 1, factor: 4 } + ] +} +const t = localStorage.getItem('dianayako_antvx6_grid_type') +if (t) gridType = t ? Number(t) : 1 + +export default class FlowGraph { + public static graph: Graph + private static option: Partial ={ + width: 1920, height: 1080, autoResize: true, + background: { color: 'var(--color-bg-1)' }, + grid: gridType === 1 ? gridModal : undefined, + interacting: function (cellView) { + const data = cellView.cell.getData() + if (data && data.disableMove) return false + return true + }, + scroller: { + enabled: true, pageVisible: false, pageBreak: true, pannable: false, padding: 0 + }, + mousewheel: { + enabled: true, modifiers: ['ctrl', 'meta'], minScale: 0.5, maxScale: 2 + }, + selecting: { + enabled: true, multiple: true, rubberband: true, movable: true + }, + connecting: { + anchor: 'center', connectionPoint: 'anchor', allowBlank: false, allowMulti: true, highlight: true, + snap: { radius: 30 }, + createEdge () { + return new Shape.Edge(edgeConfig) + }, + validateConnection: ({ + sourceView, + targetView, + sourceMagnet, + targetMagnet + }: KeyValue) => { + if (sourceView === targetView) + return false + if (!sourceMagnet) + return false + if (!targetMagnet) + return false + return true + } + }, + highlighting: { + magnetAvailable: { + name: 'stroke', + args: { + padding: 4, + attrs: { strokeWidth: 4, stroke: 'rgba(200,200,200)' } + } + } + }, + snapline: true, + history: { + enabled: true, + ignoreAdd: false, + ignoreRemove: false, + ignoreChange: true + }, + clipboard: true, keyboard: true, + embedding: { + enabled: true, + findParent (this: { getNodes: any }, { node }: { node: Node }): any { + const bbox = node.getBBox() + return this.getNodes().filter((node: Node) => { + const data = node.getData() + if (data && data.parent) { + const targetBBox = node.getBBox() + return bbox.isIntersectWithRect(targetBBox) + } + return false + }) + } + } + } + public static init (options: PropsOptions, route: RouteLocationNormalizedLoaded, containerInfo: HTMLElement | undefined, callbacks?: KeyValue) { + if (containerInfo && containerInfo.offsetWidth && containerInfo.offsetHeight) { + } + this.option.container = document.getElementById(options.containerId) as HTMLElement + if (this.option.connecting) { + this.option.connecting!.validateEdge = ({ edge }: any) => { + if (callbacks && callbacks.edgeConnect) callbacks.edgeConnect() + return lineConnect(this.graph.getCellById(edge.source.cell) as Edge, this.graph.getCellById(edge.target.cell) as Edge, this.graph) + } + } + this.graph = new Graph(options.canvasOption ? { ...this.option, ...options.canvasOption } : this.option) + this.initEvent(options, route, callbacks) + return this.graph + } + private static initEvent (options: PropsOptions, route: RouteLocationNormalizedLoaded, callbacks?: KeyValue) { + const { graph } = this + const container = document.getElementById(options.containerId) as HTMLElement + const checkDisabled = ({ cell }: { cell: Cell }) => { + const ports = container.querySelectorAll('.x6-port-body') as NodeListOf + if (cell.data && cell.data.disabled) this.togglePorts(ports, false) + else this.togglePorts(ports, true) + } + graph.on('node:mouseenter', FunctionExt.debounce(({ node }: { node: Node }) => { + node.setData({ showDisabledCheckbox: true }) + const ports = container.querySelectorAll('.x6-port-body') as NodeListOf + if (!node.data.disabled) this.togglePorts(ports, true) + node.on(`change:data`, checkDisabled) + }), 500) + graph.on('node:mouseleave', ({ node }: { node: Node }) => { + node.setData({ showDisabledCheckbox: false }) + const ports = container.querySelectorAll( + '.x6-port-body' + ) as NodeListOf + this.togglePorts(ports, false) + node.off(`change:data`, checkDisabled) + }) + graph.on('node:collapse', ({ node, e }: any) => { + e.stopPropagation() + node.toggleCollapse() + const collapsed = node.isCollapsed() + const cells = node.getDescendants() + cells.forEach((n: any) => collapsed ? n.hide() : n.show) + }) + graph.on(`cell:selected`, ({ cell }: { cell: Cell }) => { + cell.setData({ antvSelected: true }) + }) + graph.on(`cell:unselected`, ({ cell }: { cell: Cell }) => { + cell.setData({ antvSelected: false }) + }) + graph.on('blank:contextmenu', () => { + if (callbacks && callbacks.blankContextmenu) callbacks.blankContextmenu() + }) + graph.on('blank:click', () => { + const nodes = graph.getNodes() + nodes.forEach(item => { + if (item.data.antvSelected) { + item.setData({ antvSelected: false }) + } + }) + mCStore.setSelectNode(null, false) + graph.unselect(getCheckedNodes(graph)) + }) + graph.on('edge:mouseenter', ({ edge }) => { + if (!edge.data || !edge.data.operate || (edge.data.operate && Array.isArray(edge.data.operate) && edge.data.operate.includes('showChangeArrow'))) { + edge.addTools([ + 'source-arrowhead', + 'target-arrowhead' + ]) + } + }) + graph.on('edge:mouseleave', ({ edge }) => { + if (!edge.data || !edge.data.operate || (edge.data.operate && Array.isArray(edge.data.operate) && edge.data.operate.includes('showChangeArrow'))) { + edge.removeTools() + } + }) + graph.bindKey('delete', () => { + const cells = graph.getSelectedCells() + if (cells.length) { + const deleteArr = cells.filter((item: Cell) => (!item.data.operate || item.data.operate.includes('delete'))) + graph.removeCells(deleteArr) + } + }) + graph.bindKey([`ctrl+s`], () => { + const jsonData: { cells: Array } = graph.toJSON() + jsonData.cells.forEach((item: KeyValue) => { + if (item.data && item.data.cells_type) item.cells_type = item.data.cells_type + else if (item.shape === 'edge') item.cells_type = 'line' + }) + saveJsonData(dFStore.getFlowDataInfo.id as string, jsonData) + }) + graph.bindKey([`ctrl+c`], () => { + graph.copy(getCheckedNodes(graph), { useLocalStorage: true }) + }) + graph.bindKey([`ctrl+x`], () => { + graph.cut(getCheckedNodes(graph), { useLocalStorage: true }) + }) + graph.bindKey([`ctrl+v`], () => { + graph.paste({ useLocalStorage: true }) + }) + graph.bindKey([`ctrl+z`], () => { + graph?.undo() + }) + graph.bindKey([`ctrl+y`], () => { + graph?.redo() + }) + graph.on(`cell:removed`, (cell: KeyValue) => { + if (cell.cell && cell.cell.cells_type === 'join') { + dFStore.removeDatabaseTable(cell.cell.table) + } + if (callbacks && callbacks.deleteCell) callbacks.deleteCell() + }) + } + private static togglePorts (ports: NodeListOf, show: boolean) { + for (let i = 0, len = ports.length; i < len; i = i + 1) { + ports[i].style.visibility = show ? 'visible' : 'hidden' + } + } + private static createEdge (source: any, target: any, data?: KeyValue) { + this.graph.addEdge({ + shape: 'edge', + source, + target, + ...edgeConfig, + data: data ? data : {} + }) + } + public static renderInitJson = () => { + const item = dFStore.getFlowDataInfo + const json = JSON.parse(item.operatorContent) + if (json && json.cells && json.cells.length > 0) { + json.cells.forEach((item: KeyValue) => { + if ((item.cells_type in mCStore.getRules) && typeof mCStore.getRules[item.cells_type] === 'function') { + item.data.rule = mCStore.getRules[item.cells_type] + } + }) + this.graph.fromJSON(json) + } else { + } + } + private static createNode = (options: PropsOptions, configName: string, x?: number, y?: number) => { + if (configName) { + let tNode = null + options.stencil.nodes.forEach(group => { + group.child.forEach(node => { + if (node.data.configName === configName) { + tNode = this.graph.createNode({ + shape: 'BaseNode', + x: (this.option.width ? this.option.width : 1920) / 2 + (x ? x : 0), y: (this.option.height ? this.option.height / 2 : 1080) / 2 + 60 + (y ? y : 0), + width: 220, height: 60, + ports: node.ports ? node.ports : {}, + data: node.data ? node.data : {} + }) + } + }) + }) + tNode && this.graph.addNode(tNode) + return tNode + } + } + + public static toggleGridType = () => { + gridType = gridType === 1 ? 2 : 1 + localStorage.setItem('dianayako_antvx6_grid_type', String(gridType)) + if (gridType === 1) { + this.graph.drawGrid(gridModal) + } else { + this.graph.drawGrid(undefined) + } + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/hooks/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/hooks/index.vue new file mode 100644 index 000000000..44437a188 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/hooks/index.vue @@ -0,0 +1,7 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/hooks/options.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/hooks/options.ts new file mode 100644 index 000000000..910d2bd6d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/hooks/options.ts @@ -0,0 +1,167 @@ + +import { KeyValue } from '@antv/x6/lib/types' +import { PropsOptions } from '../types' +import { edge1 } from '../utils/edgeConfig' +import i18n from '@/locale/index' +export const options = () => ({ + containerId: 'database_container_id', + stencil: { + nodes: [ + { + name: i18n.global.t('modeling.hooks.index.5m83asj8uws0'), icon: 'icon-layers', child: [ + { + width: 180, height: 35, shape: 'vue-shape', + data: { + type: 'BaseNode', cells_type: 'dataSource', text: i18n.global.t('modeling.hooks.index.5m83asj8vok0'), configName: 'DataSource', icon: 'modeling-dataSource', + source: '', + rule: () => true + }, + ports: edge1.ports + } + ] + }, + { + name: i18n.global.t('modeling.hooks.index.5m83asj8vsw0'), icon: 'icon-common', child: [ + { + width: 180, height: 35, shape: 'vue-shape', + data: { + type: 'BaseNode', cells_type: 'query', text: i18n.global.t('modeling.hooks.index.5m83asjg7480'), configName: 'QueryOperator', icon: 'modeling-chaxun', + table: '', fields: [{field:undefined}], + rule: (data: KeyValue) => Boolean(data.table) && data.fields.filter((item: KeyValue) => !item.value).length === 0 + }, + ports: edge1.ports + }, { + width: 180, height: 35, shape: 'vue-shape', + data: { + type: 'BaseNode', cells_type: 'update', text: i18n.global.t('modeling.hooks.index.5m83asjg7f40'), configName: 'UpdateOperator', icon: 'modeling-gengxin', + table: '', list: [{ field: undefined, valueType: 'customSql', value: '', fieldType: 'originalValue', splitValue: undefined }], + rule: (data: KeyValue) => { + let flag = true + if (!data.table) flag = false + data.list.forEach((item: { field: string, value: string, valueType: string, fieldType: string, splitValue: string }) => { + if (!item.field || !item.value) flag = false + else if (item.valueType === i18n.global.t('modeling.hooks.index.5m83asjg7j80') && item.fieldType === i18n.global.t('modeling.hooks.index.5m83asjg7m80') && !item.splitValue) flag = false + }) + return flag + } + }, + ports: edge1.ports + }, { + width: 180, height: 35, shape: 'vue-shape', + data: { + type: 'BaseNode', cells_type: 'delete', text: i18n.global.t('modeling.hooks.index.5m83asjg7oo0'), configName: 'DeleteOperator', icon: 'modeling-shanchu', + table: '', fields: [], + rule: (data: KeyValue) => Boolean(data.table) && data.fields.filter((item: KeyValue) => !item).length === 0 + }, + ports: edge1.ports + }, { + width: 180, height: 35, shape: 'vue-shape', + data: { + type: 'BaseNode', cells_type: 'insert', text: i18n.global.t('modeling.hooks.index.5m83asjg7rk0'), configName: 'InsertOperator', icon: 'modeling-charu', + table: '', list1: [[{ field: '', value: '' }]], + rule: (data: KeyValue) => { + return Boolean(data.table) && + data.list1.filter((item: any) => { + return (item.filter((item2: KeyValue) => { + return !item2.field || !item2.value + }).length !== 0) + }).length === 0 + } + }, + ports: edge1.ports + } + ] + }, + { + name: i18n.global.t('modeling.hooks.index.5m83asjg7u80'), icon: 'icon-apps', child: [ + { + width: 180, height: 35, shape: 'vue-shape', + data: { + type: 'BaseNode', cells_type: 'condition', text: i18n.global.t('modeling.hooks.index.5m83asjg7x40'), configName: 'ConditionOperator', icon: 'modeling-tiaojian', + or: [[{ field: null, condition: null, value: '' }]], + rule: (data: KeyValue) => data.or.filter((orItem: KeyValue) => { + return orItem.filter((andItem: KeyValue) => !(andItem.field && ( + (andItem.condition === 'isNull' || andItem.condition === 'notNull') ? true : (andItem.value ? true : false) + ))).length !== 0 + }).length === 0 + }, + ports: edge1.ports + }, + { + width: 180, height: 35, shape: 'vue-shape', + data: { + type: 'BaseNode', cells_type: 'join', text: i18n.global.t('modeling.hooks.index.5m83asjg7zs0'), configName: 'BridgingOperator', icon: 'modeling-qiaojie', + table: '', connectType: '', condition: [[{ field: null, condition: null, value: '', valueType: 'string' }]], + rule: (data: KeyValue) => data.table && data.connectType && data.condition.filter((orItem: KeyValue) => { + return orItem.filter((andItem: KeyValue) => !(andItem.field && ( + (andItem.condition === 'isNull' || andItem.condition === 'notNull') ? true : (andItem.value ? true : false) + ))).length !== 0 + }).length === 0 + }, + ports: edge1.ports + }, + { + width: 180, height: 35, shape: 'vue-shape', + data: { + type: 'BaseNode', cells_type: 'sort', text: i18n.global.t('modeling.hooks.index.5m83asjg8280'), configName: 'SortOperator', icon: 'modeling-paixu', + sorts: [], + rule: (data: KeyValue) => data.sorts.filter((item: KeyValue) => (!item.field || !item.value)).length === 0 + }, + ports: edge1.ports + }, { + width: 180, height: 35, shape: 'vue-shape', + data: { + type: 'BaseNode', cells_type: 'limit', text: i18n.global.t('modeling.hooks.index.5m83asjg84w0'), configName: 'RestrictionOperator', icon: 'modeling-xiantiao', + restriction: { skip: null, limitCount: null } + }, + ports: edge1.ports + }, + { + width: 180, height: 35, shape: 'vue-shape', + data: { + type: 'BaseNode', cells_type: 'group', text: i18n.global.t('modeling.hooks.index.5m83asjg87c0'), configName: 'GroupOperator', icon: 'modeling-fenzu', + groups: [{ fields: null }] + }, + ports: edge1.ports + }, { + width: 180, height: 35, shape: 'vue-shape', + data: { + type: 'BaseNode', cells_type: 'polymerization', text: i18n.global.t('modeling.hooks.index.5m83asjg8ak0'), icon: 'modeling-juhe', + configName: 'PolymerizationOperator', + polymerization: [{ field: null, way: null, alias: '' }], + rule: (data: KeyValue) => + data.polymerization.filter((item: KeyValue, key: number) => ( + !item.field || !item.way || !item.alias + || data.polymerization.findIndex((item2: KeyValue, key2: number) => (key !== key2 && item.alias === item2.alias)) !== -1 + )).length === 0 + }, + ports: edge1.ports + }, { + width: 180, height: 35, shape: 'vue-shape', + data: { + type: 'BaseNode', cells_type: 'mapping', text: i18n.global.t('modeling.hooks.index.5m83asjg8f40'), configName: 'MapOperator', icon: 'modeling-yingshe', + mappings: [{ field: null, condition: null, value: '' }], + rule: (data: KeyValue) => + data.mappings.filter((item: KeyValue, key: number) => ( + !item.field || !item.condition || !item.value + || data.mappings.findIndex((item2: KeyValue, key2: number) => (key !== key2 && item.value === item2.value)) !== -1 + )).length === 0 + }, + ports: edge1.ports + }, { + width: 180, height: 35, shape: 'vue-shape', + data: { + type: 'BaseNode', cells_type: 'dictionary', text: i18n.global.t('modeling.hooks.index.5m83asjg8i00'), configName: 'DictionaryOperator', icon: 'modeling-zidian', + table: '', field: '', matchField: '', rigidField: '', + rule: (data: KeyValue) => data.table && data.field && data.matchField && data.rigidField + }, + ports: edge1.ports + } + ] + } + ] + }, + canvasOption: { + selecting: { multiple: true, enabled: true, rubberband: true, movable: true } + } +} as PropsOptions) diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/index.vue new file mode 100644 index 000000000..e387ac53d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/index.vue @@ -0,0 +1,391 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/style/common.less b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/style/common.less new file mode 100644 index 000000000..fa42505fb --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/style/common.less @@ -0,0 +1,85 @@ +.d-form-item-label { + display: flex; + align-items: center; +} +.d-control-add { + width: 18px; + height: 18px; + line-height: 14px; + border-radius: 50%; + + background-color: #000; + background: rgb(var(--primary-6)); + text-align: center; + color: var(--color-border); + + font-weight: bolder; + margin-left: 5px; + font-size: 20px; + cursor: pointer; +} +.d-control-remove { + width: 25px; + height: 25px; + line-height: 22px; + text-align: center; + font-size: 25px; + cursor: pointer; + border-radius: 4px; + box-sizing: border-box; + border: 1px solid transparent; + color: var(--color-text-1); + &:hover { + border: 1px solid rgb(var(--primary-6)); + } +} +.x6-edge-selected { + path { + &:last-child { + stroke-width: 3; + &:hover { + stroke: #5f95ff; + } + } + } +} +.d-antv-node { + border: 1px solid transparent; +} +.d-antv-node-selected { + border: 1px solid rgb(var(--primary-6)); +} +.antv-panel { + .d-a-tabs { + .arco-tabs-nav-tab-list { + display: flex; + width: 100%; + > div { + flex: 1; + text-align: center; + justify-content: center; + } + } + .arco-tabs-bar { + border-color: rgba(255, 255, 255, .5); + } + .arco-tabs-nav { + .arco-tabs-tab { + padding: 12px 16px; + } + .arco-tabs-tab-active { + font-weight: bold; + &:hover { + .arco-tabs-tab-title { + &::before { + background-color: transparent; + } + } + } + } + .arco-tabs-ink-bar { + background-color: #fff; + } + } + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/types/index.d.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/types/index.d.ts new file mode 100644 index 000000000..a11374548 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/types/index.d.ts @@ -0,0 +1,16 @@ +import { Addon } from '@antv/x6' +export interface PropsOptions extends Addon.Stencil.Options { + containerId: string, + title?: string, + target?: Graph | Scroller; + stencilGraphWidth?: number, + stencilGraphHeight?: number, + getDragNode?: (sourceNode: Node, options: GetDragNodeOptions) => Node, + getDropNode?: (draggingNode: Node, options: GetDropNodeOptions) => Node, + stencil: { + title?: string, + groups?: Array, + nodes: Array<{ name?: string, groupType?: string, icon?: string, child: Array }> + }, + canvasOption?: Partial +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/utils/edgeConfig.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/utils/edgeConfig.ts new file mode 100644 index 000000000..ee22c3ddb --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/utils/edgeConfig.ts @@ -0,0 +1,24 @@ +export const edge1 = { + ports: { + groups: { + top: { position: 'top', + attrs: { circle: { r: 5, magnet: true, stroke: '#5F95FF', strokeWidth: 1, fill: '#fff', style: { visibility: 'hidden' }}} + }, + bottom: { position: 'bottom', + attrs: { circle: { r: 5, magnet: true, stroke: '#5F95FF', strokeWidth: 1, fill: '#fff', style: { visibility: 'hidden' }}} + }, + left: { position: 'left', + attrs: { circle: { r: 5, magnet: true, stroke: '#5F95FF', strokeWidth: 1, fill: '#fff', style: { visibility: 'hidden' }}} + }, + right: { position: 'right', + attrs: { circle: { r: 5, magnet: true, stroke: '#5F95FF', strokeWidth: 1, fill: '#fff', style: { visibility: 'hidden' }}} + } + }, + items: [ + { group: 'top', id: 'port1' }, + { group: 'bottom', id: 'port2' }, + { group: 'left', id: 'port3' }, + { group: 'right', id: 'port4' } + ] + } +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/utils/operateJson.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/utils/operateJson.ts new file mode 100644 index 000000000..0ba2299f0 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/utils/operateJson.ts @@ -0,0 +1,68 @@ + +import { useModelCommonStore } from '@/store/modules/modeling/common' +import { Cell, Graph } from '@antv/x6' +import { KeyValue } from '@antv/x6/lib/types' +import { Notification } from '@arco-design/web-vue' +import i18n from '@/locale/index' +const mCStore = useModelCommonStore() + +const noticeArr: string[] = [] +export const checkData = (data: any): boolean => { + let check = true + let message = `` + if (data && data.cells && Array.isArray(data.cells) && data.cells.length > 0) { + data.cells.forEach((item: Cell) => { + if (item.shape !== 'edge') { + const storeSaveRule = mCStore.getRules[item.data.cells_type] + if (item.data) { + if (item.data.rule && typeof item.data.rule === 'function') { + if (!item.data.rule(item.data)) { + check = false + message += `[${item.data.text}]` + i18n.global.t('modeling.utils.index.5m78yfizcxc0') + } + } else if (storeSaveRule && typeof storeSaveRule === 'function') { + if (!storeSaveRule(item.data)) { + check = false + message += `[${item.data.text}]` + i18n.global.t('modeling.utils.index.5m78yfizcxc0') + } + } + } + } + }) + } + + if (!check && message) { + if (noticeArr.includes(message)) return check + let isDelete = false + Notification.error({ + position: 'bottomRight', + content: message, + closable: true, + duration: 10 * 1000, + onClose: () => { + const index = noticeArr.indexOf(message) + if (index != -1) noticeArr.splice(index, 1) + isDelete = true + } + }) + noticeArr.push(message) + setTimeout(() => { + if (!isDelete) { + const index = noticeArr.indexOf(message) + if (index != -1) noticeArr.splice(index, 1) + } + }, 10 * 1000) + } + + return check +} +export const jsonFormat = (graph: Graph) => { + if (graph) { + const jsonData: { cells: Array } = graph.toJSON() + jsonData.cells.forEach((item: KeyValue) => { + if (item.data && item.data.cells_type) item.cells_type = item.data.cells_type + else if (item.shape === 'edge') item.cells_type = 'line' + }) + return jsonData + } else return '' +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/utils/tools.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/utils/tools.ts new file mode 100644 index 000000000..c81e42ffd --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/utils/tools.ts @@ -0,0 +1,9 @@ +import { Graph } from '@antv/x6' + +export const getCheckedNodes = (graph: Graph) => { + let nodes = graph.getNodes() + nodes = nodes.filter(item => { + return Boolean(item.data && item.data.antvSelected) + }) + return nodes +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/utils/validate.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/utils/validate.ts new file mode 100644 index 000000000..12eda7dc5 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/detail/utils/validate.ts @@ -0,0 +1,37 @@ +import { Edge, Graph } from '@antv/x6' +import { Message } from '@arco-design/web-vue' +const baseNodes = ['query', 'update', 'delete', 'insert'] +import i18n from '@/locale/index' +export const lineConnect = (source: Edge, target: Edge, graph: Graph): boolean => { + const showMessage = (content: string) => { + Message.warning({ content: content, duration: 5 * 1000 }) + } + if (target.data.cells_type === 'dataSource') { + showMessage(i18n.global.t('modeling.utils.index.5m835tc0m9o0')) + return false + } + if (source.data.cells_type === 'dataSource') { + if (!baseNodes.includes(target.data.cells_type)) { + showMessage(i18n.global.t('modeling.utils.index.5m835tc0n8k0')) + return false + } + } + if (baseNodes.includes(source.data.cells_type) && baseNodes.includes(target.data.cells_type)) { + showMessage(i18n.global.t('modeling.utils.index.5m835tc0ndo0')) + return false + } + const edges: Edge[] = graph.getEdges() + if (source.data.cells_type !== 'dataSource') { + const sourceList: Edge[] = edges.filter((item: any) => item.source.cell === source.id) + if (sourceList.length > 1) { + showMessage(i18n.global.t('modeling.utils.index.5m835tc0nh00')) + return false + } + } + const targetList: Edge[] = edges.filter((item: any) => item.target.cell === target.id) + if (targetList.length > 1) { + showMessage(i18n.global.t('modeling.utils.index.5m835tc0njw0')) + return false + } + return true +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/share/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/share/index.vue new file mode 100644 index 000000000..ff9ddc75a --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/child/share/index.vue @@ -0,0 +1,160 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/components/CU.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/components/CU.vue new file mode 100644 index 000000000..18f43c8f3 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/components/CU.vue @@ -0,0 +1,229 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/components/CUUser.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/components/CUUser.vue new file mode 100644 index 000000000..811f6b2dd --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/components/CUUser.vue @@ -0,0 +1,71 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/index.vue new file mode 100644 index 000000000..c446744ff --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/index.vue @@ -0,0 +1,330 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/locale/en-US.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/locale/en-US.ts new file mode 100644 index 000000000..ab2c31c93 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/locale/en-US.ts @@ -0,0 +1,4 @@ + +export default { + 'common.test': 'test' +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/locale/index.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/locale/index.ts new file mode 100644 index 000000000..3d8ce4d47 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/locale/index.ts @@ -0,0 +1,18 @@ +import { createI18n } from 'vue-i18n' +import en from './en-US' +import cn from './zh-CN' +export const LOCALE_OPTIONS = [ + { label: '中文', value: 'zh-CN' }, + { label: 'English', value: 'en-US' } +] +const defaultLocale = localStorage.getItem('arco-locale') || 'zh-CN' +const i18n = createI18n({ + locale: defaultLocale, + fallbackLocale: 'en-US', + allowComposition: true, + messages: { + 'en-US': en, + 'zh-CN': cn + } +}) +export default i18n diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/locale/zh-CN.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/locale/zh-CN.ts new file mode 100644 index 000000000..88ee2335d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/dataflow/locale/zh-CN.ts @@ -0,0 +1,4 @@ + +export default { + 'dataflow.name': 'test' +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/index.vue new file mode 100644 index 000000000..ebb44cdf9 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/index.vue @@ -0,0 +1,8 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/modelDesign/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/modelDesign/index.vue new file mode 100644 index 000000000..3b1cbeee1 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/modelDesign/index.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/utils/base64.ts b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/utils/base64.ts new file mode 100644 index 000000000..437e87b73 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/utils/base64.ts @@ -0,0 +1,95 @@ +class Base64 { + private _keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + encode (input: string) { + let output = '' + let chr1, chr2, chr3, enc1, enc2, enc3, enc4 + let i = 0 + input = this._utf8_encode(input) + while (i < input.length) { + chr1 = input.charCodeAt(i++) + chr2 = input.charCodeAt(i++) + chr3 = input.charCodeAt(i++) + enc1 = chr1 >> 2 + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4) + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6) + enc4 = chr3 & 63 + if (isNaN(chr2)) { + enc3 = enc4 = 64 + } else if (isNaN(chr3)) { + enc4 = 64 + } + output = output + + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4) + } + return output + } + decode (input: string) { + let output = '' + let chr1, chr2, chr3 + let enc1, enc2, enc3, enc4 + let i = 0 + input = input.replace(/[^A-Za-z0-9]/g, '') + while (i < input.length) { + enc1 = this._keyStr.indexOf(input.charAt(i++)) + enc2 = this._keyStr.indexOf(input.charAt(i++)) + enc3 = this._keyStr.indexOf(input.charAt(i++)) + enc4 = this._keyStr.indexOf(input.charAt(i++)) + chr1 = (enc1 << 2) | (enc2 >> 4) + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2) + chr3 = ((enc3 & 3) << 6) | enc4 + output = output + String.fromCharCode(chr1) + if (enc3 != 64) { + output = output + String.fromCharCode(chr2) + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3) + } + } + output = this._utf8_decode(output) + return output + } + _utf8_encode (string: string) { + string = string.replace(/\r\n/g, '\n') + let utftext = '' + for (let n = 0; n < string.length; n++) { + const c = string.charCodeAt(n) + if (c < 128) { + utftext += String.fromCharCode(c) + } else if ((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192) + utftext += String.fromCharCode((c & 63) | 128) + } else { + utftext += String.fromCharCode((c >> 12) | 224) + utftext += String.fromCharCode(((c >> 6) & 63) | 128) + utftext += String.fromCharCode((c & 63) | 128) + } + } + return utftext + } + _utf8_decode (utftext: string) { + let string = '' + let i = 0 + let c = 0 + let c1 = 0 + let c2 = 0 + while (i < utftext.length) { + c = utftext.charCodeAt(i) + if (c < 128) { + string += String.fromCharCode(c) + i++ + } else if ((c > 191) && (c < 224)) { + c1 = utftext.charCodeAt(i + 1) + string += String.fromCharCode(((c & 31) << 6) | (c1 & 63)) + i += 2 + } else { + c1 = utftext.charCodeAt(i + 1) + c2 = utftext.charCodeAt(i + 2) + string += String.fromCharCode(((c & 15) << 12) | ((c1 & 63) << 6) | (c2 & 63)) + i += 3 + } + } + return string + } +} +export default Base64 diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/modeling/workflow/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/workflow/index.vue new file mode 100644 index 000000000..844e15345 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/modeling/workflow/index.vue @@ -0,0 +1,36 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/customControl/HostPwdDlg.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/customControl/HostPwdDlg.vue new file mode 100644 index 000000000..b095e0c7a --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/customControl/HostPwdDlg.vue @@ -0,0 +1,205 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/customControl/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/customControl/index.vue new file mode 100644 index 000000000..7755cfeeb --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/customControl/index.vue @@ -0,0 +1,299 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/indicators/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/indicators/index.vue new file mode 100644 index 000000000..0ba774f17 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/indicators/index.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/logCenter/components/AuditLog.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/logCenter/components/AuditLog.vue new file mode 100644 index 000000000..2d0a3aaa0 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/logCenter/components/AuditLog.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/logCenter/components/FileLog.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/logCenter/components/FileLog.vue new file mode 100644 index 000000000..5993a0d64 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/logCenter/components/FileLog.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/logCenter/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/logCenter/index.vue new file mode 100644 index 000000000..af284ef85 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/logCenter/index.vue @@ -0,0 +1,167 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/monitor/echarts/option.ts b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/monitor/echarts/option.ts new file mode 100644 index 000000000..e2f5865e9 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/monitor/echarts/option.ts @@ -0,0 +1,385 @@ +import * as echarts from 'echarts/core' + +export const cpuOption = { + grid: { + x: 40, + y: 30, + y2: 30 + }, + xAxis: { + type: 'category', + data: [] + }, + yAxis: { + type: 'value', + name: '%' + }, + series: [ + { + data: [], + type: 'line', + smooth: true, + symbolSize: 4, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(115, 33, 212, 1)' + }, + { + offset: 1, + color: '#FFFFFF' + } + ]) + }, + itemStyle: { + normal: { + lineStyle: { + width: 1 + } + } + } + } + ] +} + +export const memoryOption = { + grid: { + x: 40, + y: 30, + y2: 30 + }, + xAxis: { + type: 'category', + data: [] + }, + yAxis: { + type: 'value', + name: '%' + }, + series: [ + { + data: [], + type: 'line', + smooth: true, + symbolSize: 4, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(32, 108, 207, 1)' + }, + { + offset: 1, + color: '#FFFFFF' + } + ]) + }, + itemStyle: { + normal: { + lineStyle: { + width: 1 + } + } + } + } + ] +} + +export const netOption = { + grid: { + x: 40, + y: 30, + y2: 30 + }, + xAxis: { + type: 'category', + data: [] + }, + yAxis: { + type: 'value', + name: 'G' + }, + series: [ + { + data: [], + type: 'line', + smooth: true, + symbolSize: 4, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(114, 46, 209, 1)' + }, + { + offset: 1, + color: '#FFFFFF' + } + ]) + }, + itemStyle: { + normal: { + lineStyle: { + width: 1 + } + } + } + }, + { + data: [], + type: 'line', + smooth: true, + symbolSize: 4, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(255, 125, 0, 1)' + }, + { + offset: 1, + color: '#FFFFFF' + } + ]) + }, + itemStyle: { + normal: { + lineStyle: { + width: 1 + } + } + } + } + ] +} + +export const lockOption = { + grid: { + x: 40, + y: 30, + y2: 30 + }, + xAxis: { + type: 'category', + data: [] + }, + yAxis: { + type: 'value', + name: 'pcs', + interval: 1 + }, + series: [ + { + data: [], + type: 'line', + smooth: true, + symbolSize: 4, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(25, 81, 255, 0.5)' + }, + { + offset: 1, + color: '#FFFFFF' + } + ]) + }, + itemStyle: { + normal: { + lineStyle: { + width: 1 + } + } + } + } + ] +} + +export const sessionOption = { + grid: { + x: 40, + y: 30, + y2: 30 + }, + xAxis: { + type: 'category', + data: [] + }, + yAxis: { + type: 'value', + name: 'pcs' + }, + series: [ + { + data: [], + type: 'line', + smooth: true, + symbolSize: 4, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(209, 24, 24, 1)' + }, + { + offset: 1, + color: '#FFFFFF' + } + ]) + }, + itemStyle: { + normal: { + lineStyle: { + width: 1 + } + } + } + } + ] +} + +export const connectOption = { + grid: { + x: 40, + y: 30, + y2: 30 + }, + xAxis: { + type: 'category', + data: [] + }, + yAxis: { + type: 'value', + name: 'pcs' + }, + series: [ + { + data: [], + type: 'line', + smooth: true, + symbolSize: 4, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: 'rgba(1, 180, 42, 1)' + }, + { + offset: 1, + color: '#FFFFFF' + } + ]) + }, + itemStyle: { + normal: { + lineStyle: { + width: 1 + } + } + } + } + ] +} + +export const sessionTopOption = { + tooltip: { + trigger: 'axis' + }, + grid: { + left: 100 + }, + xAxis: { + type: 'value', + name: 'Byte', + axisLabel: { + formatter: '{value}' + } + }, + yAxis: { + type: 'category', + name: 'SessionID', + axisLabel: { + formatter: '{value}' + }, + data: [] + }, + series: [ + { + name: 'session used memorySize', + type: 'bar', + barWidth: 20, + data: [{ + value: 0, + itemStyle: { + color: '#4EA3FF' + } + }, { + value: 0, + itemStyle: { + color: '#05B088' + } + }, { + value: 0, + itemStyle: { + color: '#F5A43F' + } + }, { + value: 0, + itemStyle: { + color: '#F55857' + } + }, { + value: 0, + itemStyle: { + color: '#24E9C3' + } + }, { + value: 0, + itemStyle: { + color: '#EEABA3' + } + }, { + value: 0, + itemStyle: { + color: '#F5C03E' + } + }, { + value: 0, + itemStyle: { + color: '#009682' + } + }, { + value: 0, + itemStyle: { + color: '#B996FC' + } + }, { + value: 0, + itemStyle: { + color: '#B996FC' + } + }] + } + ] +} + +export const clearData = () => { + cpuOption.xAxis.data = [] + cpuOption.series[0].data = [] + memoryOption.xAxis.data = [] + memoryOption.series[0].data = [] + lockOption.xAxis.data = [] + lockOption.series[0].data = [] + sessionOption.xAxis.data = [] + sessionOption.series[0].data = [] + connectOption.xAxis.data = [] + connectOption.series[0].data = [] + netOption.xAxis.data = [] + netOption.series[0].data = [] + netOption.series[1].data = [] + sessionTopOption.yAxis.data = [] + sessionTopOption.series[0].data = [] +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/monitor/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/monitor/index.vue new file mode 100644 index 000000000..6c831f2c8 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/monitor/index.vue @@ -0,0 +1,353 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/ClusterBackupDlg.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/ClusterBackupDlg.vue new file mode 100644 index 000000000..453511e74 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/ClusterBackupDlg.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/DailyOps.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/DailyOps.vue new file mode 100644 index 000000000..f89d31b96 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/DailyOps.vue @@ -0,0 +1,2608 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/GucSettingDrawer.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/GucSettingDrawer.vue new file mode 100644 index 000000000..4123aa7d8 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/GucSettingDrawer.vue @@ -0,0 +1,373 @@ + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/HostPwdDlg.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/HostPwdDlg.vue new file mode 100644 index 000000000..ce566cc13 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/HostPwdDlg.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/HostPwdForm.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/HostPwdForm.vue new file mode 100644 index 000000000..1e0be5839 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/HostPwdForm.vue @@ -0,0 +1,80 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/index.vue new file mode 100644 index 000000000..c3c0fc937 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/index.vue @@ -0,0 +1,740 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step1/AddPackage.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step1/AddPackage.vue new file mode 100644 index 000000000..8c4cc821e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step1/AddPackage.vue @@ -0,0 +1,505 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step1/PackageManage.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step1/PackageManage.vue new file mode 100644 index 000000000..727230bc4 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step1/PackageManage.vue @@ -0,0 +1,298 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step1/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step1/index.vue new file mode 100644 index 000000000..f14169dfb --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step1/index.vue @@ -0,0 +1,2033 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step2/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step2/index.vue new file mode 100644 index 000000000..46c16a800 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step2/index.vue @@ -0,0 +1,357 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step3/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step3/index.vue new file mode 100644 index 000000000..4134af1a8 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/task/step3/index.vue @@ -0,0 +1,315 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/taskDetails.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/taskDetails.vue new file mode 100644 index 000000000..42fa10bf3 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/taskDetails.vue @@ -0,0 +1,335 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/taskExecute.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/taskExecute.vue new file mode 100644 index 000000000..0f8789c64 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/operation/taskExecute.vue @@ -0,0 +1,131 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/packageManage/AddPackageDlg.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/packageManage/AddPackageDlg.vue new file mode 100644 index 000000000..ce3ef6f12 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/packageManage/AddPackageDlg.vue @@ -0,0 +1,663 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/packageManage/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/packageManage/index.vue new file mode 100644 index 000000000..ee918eba4 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/packageManage/index.vue @@ -0,0 +1,253 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/slowSql/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/slowSql/index.vue new file mode 100644 index 000000000..5ad2e55dc --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/slowSql/index.vue @@ -0,0 +1,209 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/snapshot/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/snapshot/index.vue new file mode 100644 index 000000000..5411020b2 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/snapshot/index.vue @@ -0,0 +1,249 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/wdr/GenerateWdrDlg.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/wdr/GenerateWdrDlg.vue new file mode 100644 index 000000000..fccdfbb45 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/wdr/GenerateWdrDlg.vue @@ -0,0 +1,337 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/monitor/wdr/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/wdr/index.vue new file mode 100644 index 000000000..9d377208f --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/monitor/wdr/index.vue @@ -0,0 +1,313 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/backup/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/backup/index.vue new file mode 100644 index 000000000..f37cf6db6 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/backup/index.vue @@ -0,0 +1,312 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/batchInstall/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/batchInstall/index.vue new file mode 100644 index 000000000..14f367bc2 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/batchInstall/index.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/ClusterItem.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/ClusterItem.vue new file mode 100644 index 000000000..0b08a5d06 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/ClusterItem.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/Install.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/Install.vue new file mode 100644 index 000000000..19296a524 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/Install.vue @@ -0,0 +1,75 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/InstallFailure.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/InstallFailure.vue new file mode 100644 index 000000000..46ddd68c8 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/InstallFailure.vue @@ -0,0 +1,157 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/InstallSuccess.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/InstallSuccess.vue new file mode 100644 index 000000000..2cb7b5c84 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/InstallSuccess.vue @@ -0,0 +1,94 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/Installing.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/Installing.vue new file mode 100644 index 000000000..31b6e5306 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/Installing.vue @@ -0,0 +1,184 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/Prepare.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/Prepare.vue new file mode 100644 index 000000000..127899bcd --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/Prepare.vue @@ -0,0 +1,226 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/constant.ts b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/constant.ts new file mode 100644 index 000000000..9cb4d8890 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/constant.ts @@ -0,0 +1,10 @@ +export const STEP = { + STEP1: 1, + STEP2: 2, +}; + +export enum STATUS { + INSTALLING = "INSTALLING", + SUCCESS = "SUCCESS", + FAILURE = "FAILURE", +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/index.vue new file mode 100644 index 000000000..c30cb154e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/dtClusterInstall/index.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/home/components/OneCheck.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/home/components/OneCheck.vue new file mode 100644 index 000000000..c148ba401 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/home/components/OneCheck.vue @@ -0,0 +1,541 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/home/components/oneCheck.ts b/ER-Model/plugins/base-ops/web-ui/src/views/ops/home/components/oneCheck.ts new file mode 100644 index 000000000..7379b2942 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/home/components/oneCheck.ts @@ -0,0 +1,7 @@ +export const oneCheck = { + cluster: ['CheckClusterState', 'CheckDBParams', 'CheckDebugSwitch', 'CheckDirPermissions', 'CheckEnvProfile', 'CheckReadonlyMode', 'CheckDilateSysTab', 'CheckProStartTime', 'CheckMpprcFile'], + db: ['CheckCurConnCount', 'CheckCursorNum', 'CheckTableSpace', 'CheckSysadminUser', 'CheckHashIndex', 'CheckPgxcRedistb', 'CheckNodeGroupName', 'CheckTDDate', 'CheckPgxcgroup'], + os: ['CheckEncoding', 'CheckFirewall', 'CheckKernelVer', 'CheckMaxHandle', 'CheckNTPD', 'CheckOSVer', 'CheckSysParams', 'CheckTHP', 'CheckTimeZone', 'CheckCPU', 'CheckSshdService', 'CheckSshdConfig', 'CheckCrondService', 'CheckStack', 'CheckSysPortRange', 'CheckMemInfo', 'CheckHyperThread', 'CheckMaxProcMemory', 'CheckBootItems', 'CheckKeyProAdj', 'CheckFilehandle', 'CheckDropCache'], + device: ['CheckBlockdev', 'CheckDiskFormat', 'CheckSpaceUsage', 'CheckInodeUsage', 'CheckSwapMemory', 'CheckLogicalBlock', 'CheckIOrequestqueue', 'CheckMaxAsyIOrequests', 'CheckIOConfigure'], + network: ['CheckMTU', 'CheckPing', 'CheckRXTX', 'CheckNetWorkDrop', 'CheckMultiQueue', 'CheckRouting', 'CheckNICModel'] +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/import/importCluster.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/import/importCluster.vue new file mode 100644 index 000000000..4d3ceb1bc --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/import/importCluster.vue @@ -0,0 +1,614 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/Install.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/Install.vue new file mode 100644 index 000000000..2f4e8f551 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/Install.vue @@ -0,0 +1,199 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/ChooseInstallWay.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/ChooseInstallWay.vue new file mode 100644 index 000000000..a81236fa0 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/ChooseInstallWay.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/ChooseVersion.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/ChooseVersion.vue new file mode 100644 index 000000000..90ac90ba3 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/ChooseVersion.vue @@ -0,0 +1,206 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/ExeImport.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/ExeImport.vue new file mode 100644 index 000000000..df69c9b70 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/ExeImport.vue @@ -0,0 +1,264 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/InstallStep.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/InstallStep.vue new file mode 100644 index 000000000..713d719c3 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/InstallStep.vue @@ -0,0 +1,148 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/OfflineInstall.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/OfflineInstall.vue new file mode 100644 index 000000000..3f84c663d --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/OfflineInstall.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/OnlineInstall.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/OnlineInstall.vue new file mode 100644 index 000000000..472738412 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/OnlineInstall.vue @@ -0,0 +1,343 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/TarUploadModal.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/TarUploadModal.vue new file mode 100644 index 000000000..c2d52a9a1 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/TarUploadModal.vue @@ -0,0 +1,437 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/ClusterConfigAll.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/ClusterConfigAll.vue new file mode 100644 index 000000000..53e81f5fe --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/ClusterConfigAll.vue @@ -0,0 +1,568 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/EnterpriseImport.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/EnterpriseImport.vue new file mode 100644 index 000000000..c174aea1e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/EnterpriseImport.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/EnterpriseInstall.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/EnterpriseInstall.vue new file mode 100644 index 000000000..121b9e5e1 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/EnterpriseInstall.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/EnvMonitor.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/EnvMonitor.vue new file mode 100644 index 000000000..aa9771645 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/EnvMonitor.vue @@ -0,0 +1,343 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/ExeInstall.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/ExeInstall.vue new file mode 100644 index 000000000..640a9e11b --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/ExeInstall.vue @@ -0,0 +1,492 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/InstallPrompt.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/InstallPrompt.vue new file mode 100644 index 000000000..0778ea5b7 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/InstallPrompt.vue @@ -0,0 +1,343 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/NodeConfig.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/NodeConfig.vue new file mode 100644 index 000000000..f45d7a99f --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/NodeConfig.vue @@ -0,0 +1,628 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/components/ClusterForm.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/components/ClusterForm.vue new file mode 100644 index 000000000..ac1dbf85e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/components/ClusterForm.vue @@ -0,0 +1,659 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/components/NodeInfo.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/components/NodeInfo.vue new file mode 100644 index 000000000..c675a10b9 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/components/NodeInfo.vue @@ -0,0 +1,528 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/components/SharingStorage.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/components/SharingStorage.vue new file mode 100644 index 000000000..ad938123e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/components/SharingStorage.vue @@ -0,0 +1,522 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/constant.ts b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/constant.ts new file mode 100644 index 000000000..ff12fdd1e --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/enterprise/constant.ts @@ -0,0 +1,2 @@ +export const ILLEGAL_REGEXP = /[\"\'{}()\[\]^%|,;&$>*<`\\\-!\n]/; +export const LINUX_PATH = /^(\/|~\/?|\/[^\0\s!$`&*()|;'<>\?]*\/?)+$/; diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/hostTerminal/HostTerminal.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/hostTerminal/HostTerminal.vue new file mode 100644 index 000000000..ec8fe9bf5 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/hostTerminal/HostTerminal.vue @@ -0,0 +1,176 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/installConfig.ts b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/installConfig.ts new file mode 100644 index 000000000..61f184833 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/installConfig.ts @@ -0,0 +1,82 @@ +import { markRaw } from 'vue' + +import ChooseVersion from './ChooseVersion.vue' +import ChooseInstallWay from './ChooseInstallWay.vue' +import OfflineInstall from './OfflineInstall.vue' +import OnlineInstall from './OnlineInstall.vue' +import InstallStep from './InstallStep.vue' + +type stringkey = Record + +const firstStep = markRaw(ChooseVersion) + +const chooseStep: stringkey = { + ChooseVersion: { + children: { + name: 'ChooseInstallWay', + comp: markRaw(ChooseInstallWay) + }, + childrenImport: { + name: 'InstallStep', + comp: markRaw(InstallStep) + }, + parent: { + name: 'ChooseVersion', + comp: markRaw(ChooseVersion) + } + }, + ChooseInstallWay: { + children: { + OFF_LINE: { + name: 'OfflineInstall', + comp: markRaw(OfflineInstall) + }, + ON_LINE: { + name: 'OnlineInstall', + comp: markRaw(OnlineInstall) + } + }, + parent: { + name: 'ChooseVersion', + comp: markRaw(ChooseVersion) + } + }, + OfflineInstall: { + children: { + name: 'InstallStep', + comp: markRaw(InstallStep) + }, + parent: { + name: 'ChooseInstallWay', + comp: markRaw(ChooseInstallWay) + } + }, + OnlineInstall: { + children: { + name: 'InstallStep', + comp: markRaw(InstallStep) + }, + parent: { + name: 'ChooseInstallWay', + comp: markRaw(ChooseInstallWay) + } + }, + InstallStep: { + children: null, + parent: { + OFF_LINE: { + name: 'OfflineInstall', + comp: markRaw(OfflineInstall) + }, + ON_LINE: { + name: 'OnlineInstall', + comp: markRaw(OnlineInstall) + } + }, + parentImport: { + name: 'ChooseVersion', + comp: markRaw(ChooseVersion) + } + } +} +export { firstStep, chooseStep } \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/DeployWay.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/DeployWay.vue new file mode 100644 index 000000000..fe95350ed --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/DeployWay.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/EnvMonitor.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/EnvMonitor.vue new file mode 100644 index 000000000..285c14671 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/EnvMonitor.vue @@ -0,0 +1,349 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/ExeInstall.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/ExeInstall.vue new file mode 100644 index 000000000..f740ecbca --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/ExeInstall.vue @@ -0,0 +1,552 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/InstallConfig.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/InstallConfig.vue new file mode 100644 index 000000000..874e140bc --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/InstallConfig.vue @@ -0,0 +1,673 @@ + + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/InstallPrompt.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/InstallPrompt.vue new file mode 100644 index 000000000..c3a5bb292 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/InstallPrompt.vue @@ -0,0 +1,201 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/LightWeightImport.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/LightWeightImport.vue new file mode 100644 index 000000000..e8dd8f814 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/LightWeightImport.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/LightWeightInstall.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/LightWeightInstall.vue new file mode 100644 index 000000000..03cd938ad --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/lightweight/LightWeightInstall.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/BasicInfoConfigPanel.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/BasicInfoConfigPanel.vue new file mode 100644 index 000000000..b67b995ee --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/BasicInfoConfigPanel.vue @@ -0,0 +1,399 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/ConfigView.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/ConfigView.vue new file mode 100644 index 000000000..dee1e6c29 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/ConfigView.vue @@ -0,0 +1,134 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/CustomShell.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/CustomShell.vue new file mode 100644 index 000000000..5ce2cb04a --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/CustomShell.vue @@ -0,0 +1,242 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/ExeInstall.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/ExeInstall.vue new file mode 100644 index 000000000..36064b7cc --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/ExeInstall.vue @@ -0,0 +1,384 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/LookengConfig.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/LookengConfig.vue new file mode 100644 index 000000000..38e11d6e3 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/LookengConfig.vue @@ -0,0 +1,69 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/LookengConfigPanel.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/LookengConfigPanel.vue new file mode 100644 index 000000000..8c1a4f484 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/LookengConfigPanel.vue @@ -0,0 +1,403 @@ + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/LookengInstall.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/LookengInstall.vue new file mode 100644 index 000000000..6f9c4fdd4 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/LookengInstall.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/ProcessFlag.ts b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/ProcessFlag.ts new file mode 100644 index 000000000..53c291cc9 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/ProcessFlag.ts @@ -0,0 +1,17 @@ +export enum ProcessFlag { + END_UPLOAD = 'Uploading Packages Successfully Completed', + END_INSTALL_SHARDING_PROXY = 'Install ShardingProxy On .+ Complete', + END_INSTALL_ZOOKEEPER = 'Install Zookeeper On .+ Complete', + END_INSTALL_OLK = 'Install OpenLooKeng On .+ Complete', + END_INSTALL = 'Install All Packages Complete', + END_SERVICE = 'Start All Service Complete', + START_DESTROY = "Start Destroy OpenLooKeng Service...", + END_DESTROY_IN_ERROR = "Destroy OpenLooKeng Service Process Abort With Error:", + END_DESTROY = "OpenLooKeng Deploy Process Successfully Completed", + START_SERVICE = "Start OpenLooKeng Service...", + END_START_SERVICE_IN_ERROR = "Start OpenLooKeng Service Process Abort With Error:", + END_START_SERVICE = "OpenLooKeng Service Successfully Started", + START_STOP_SERVICE = "Stopping OpenLooKeng Service...", + END_STOP_SERVICE_IN_ERROR = "Stop OpenLooKeng Service Process Abort With Error:", + END_STOP_SERVICE = "OpenLooKeng Service Successfully Stopped" +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/ShardingConfigPanel.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/ShardingConfigPanel.vue new file mode 100644 index 000000000..4d3c01be4 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/openLookeng/ShardingConfigPanel.vue @@ -0,0 +1,490 @@ + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/DeployWay.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/DeployWay.vue new file mode 100644 index 000000000..9b43ab754 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/DeployWay.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/EnvMonitor.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/EnvMonitor.vue new file mode 100644 index 000000000..b6174f753 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/EnvMonitor.vue @@ -0,0 +1,324 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/ExeInstall.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/ExeInstall.vue new file mode 100644 index 000000000..fadd95aa0 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/ExeInstall.vue @@ -0,0 +1,484 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/InstallConfig.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/InstallConfig.vue new file mode 100644 index 000000000..70fe9ae6b --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/InstallConfig.vue @@ -0,0 +1,601 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/InstallPrompt.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/InstallPrompt.vue new file mode 100644 index 000000000..720441dab --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/InstallPrompt.vue @@ -0,0 +1,126 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/SimpleImport.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/SimpleImport.vue new file mode 100644 index 000000000..3c10ec19a --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/SimpleImport.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/SimpleInstall.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/SimpleInstall.vue new file mode 100644 index 000000000..e87e88cc0 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/install/components/simple/SimpleInstall.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/migrate/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/migrate/index.vue new file mode 100644 index 000000000..8c8ef069f --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/migrate/index.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/resource/openLooKeng/expandInfo.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/resource/openLooKeng/expandInfo.vue new file mode 100644 index 000000000..8ea62baea --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/resource/openLooKeng/expandInfo.vue @@ -0,0 +1,66 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/resource/openLooKeng/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/resource/openLooKeng/index.vue new file mode 100644 index 000000000..d9eb59ee0 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/resource/openLooKeng/index.vue @@ -0,0 +1,307 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/simpleInstall/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/simpleInstall/index.vue new file mode 100644 index 000000000..4dd5cddef --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/simpleInstall/index.vue @@ -0,0 +1,875 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/ClusterControlDlg.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/ClusterControlDlg.vue new file mode 100644 index 000000000..b8c131914 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/ClusterControlDlg.vue @@ -0,0 +1,192 @@ + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/EnvCheck.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/EnvCheck.vue new file mode 100644 index 000000000..5061476b0 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/EnvCheck.vue @@ -0,0 +1,222 @@ + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/ExeUpgrade.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/ExeUpgrade.vue new file mode 100644 index 000000000..76ad28c95 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/ExeUpgrade.vue @@ -0,0 +1,733 @@ + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/UpgradeConifg.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/UpgradeConifg.vue new file mode 100644 index 000000000..ea569366f --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/UpgradeConifg.vue @@ -0,0 +1,589 @@ + + + + \ No newline at end of file diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/index.vue new file mode 100644 index 000000000..5f9ee3e50 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/ops/upgrade/index.vue @@ -0,0 +1,125 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/resource/diskArray/Modal.vue b/ER-Model/plugins/base-ops/web-ui/src/views/resource/diskArray/Modal.vue new file mode 100644 index 000000000..7c8a11187 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/resource/diskArray/Modal.vue @@ -0,0 +1,353 @@ + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/resource/diskArray/constant.ts b/ER-Model/plugins/base-ops/web-ui/src/views/resource/diskArray/constant.ts new file mode 100644 index 000000000..510fc5bb1 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/resource/diskArray/constant.ts @@ -0,0 +1,10 @@ +export const CONNECTIVITY_STATUS = { + UNTEST: 0, + SUCCESS: 1, + FAILURE: 2, +}; + +export const MODAL_TYPE = { + CREATE: 0, + UPDATE: 1, +}; diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/resource/diskArray/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/resource/diskArray/index.vue new file mode 100644 index 000000000..32a3e1991 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/resource/diskArray/index.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/resource/dtCluster/ClusterItem.vue b/ER-Model/plugins/base-ops/web-ui/src/views/resource/dtCluster/ClusterItem.vue new file mode 100644 index 000000000..ab08b5b20 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/resource/dtCluster/ClusterItem.vue @@ -0,0 +1,249 @@ + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/resource/dtCluster/DtClusterItem.vue b/ER-Model/plugins/base-ops/web-ui/src/views/resource/dtCluster/DtClusterItem.vue new file mode 100644 index 000000000..80b11de1a --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/resource/dtCluster/DtClusterItem.vue @@ -0,0 +1,400 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/resource/dtCluster/constant.ts b/ER-Model/plugins/base-ops/web-ui/src/views/resource/dtCluster/constant.ts new file mode 100644 index 000000000..922eab961 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/resource/dtCluster/constant.ts @@ -0,0 +1,28 @@ +export enum CLUSER_ROLE { + PRIMARY = "PRIMARY", + STANDBY = "STANDBY", +} + +// 单集群状态参考 https://docs-opengauss.osinfra.cn/zh/docs/latest/docs/DatabaseOMGuide/%E6%9F%A5%E7%9C%8B%E7%8A%B6%E6%80%81.html +export enum CLUSTER_STATE { + NORMAL = "NORMAL", + UNAVAILABLE = "UNAVAILABLE", + DEGRADED = "DEGRADED", + UNKWON = "UNKWON", +} + +// 容灾集群状态参考 https://docs-opengauss.osinfra.cn/zh/docs/latest/docs/ToolandCommandReference/gs_sdr.html +export enum DT_CLUSTER_STATE { + ARCHIVE = "ARCHIVE", + FULL_BACKUP = "FULL_BACKUP", + BACKUP_FAIL = "BACKUP_FAIL", + ARCHIVE_FAIL = "ARCHIVE_FAIL", + SWITCHOVER = "SWITCHOVER", + RESTORE = "RESTORE", + RESTORE_FAIL = "RESTORE_FAIL", + RECOVERY = "RECOVERY", + RECOVERY_FAIL = "RECOVERY_FAIL", + PROMOTE = "PROMOTE", + PROMOTE_FAIL = "PROMOTE_FAIL", + UNKWON = "UNKWON", +} diff --git a/ER-Model/plugins/base-ops/web-ui/src/views/resource/dtCluster/index.vue b/ER-Model/plugins/base-ops/web-ui/src/views/resource/dtCluster/index.vue new file mode 100644 index 000000000..84f680208 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/src/views/resource/dtCluster/index.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/ER-Model/plugins/base-ops/web-ui/tsconfig.json b/ER-Model/plugins/base-ops/web-ui/tsconfig.json new file mode 100644 index 000000000..82e357a3a --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "strict": true, + "jsx": "preserve", + "moduleResolution": "node", + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "useDefineForClassFields": true, + "sourceMap": true, + "resolveJsonModule": true, + "baseUrl": ".", + "types": [ + "webpack-env" + ], + "paths": { + "@/*": [ + "src/*" + ] + }, + "lib": [ + "esnext", + "dom", + "dom.iterable", + "scripthost" + ] + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.vue", + "tests/**/*.ts", + "tests/**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/ER-Model/plugins/base-ops/web-ui/vue.config.js b/ER-Model/plugins/base-ops/web-ui/vue.config.js new file mode 100644 index 000000000..bfa5c5366 --- /dev/null +++ b/ER-Model/plugins/base-ops/web-ui/vue.config.js @@ -0,0 +1,100 @@ +const { defineConfig } = require('@vue/cli-service') +const path = require('path') + +const resolve = dir => path.join(__dirname, './', dir) +const port = process.env.port || process.env.npm_config_port || 80 + +module.exports = defineConfig({ + runtimeCompiler: true, + transpileDependencies: false, + lintOnSave: false, + publicPath: "/static-plugin/base-ops/", + productionSourceMap: false, + css: { + loaderOptions: { + less: { + lessOptions: { + modifyVars: { + 'primary-6': '#E41D1D', + 'primary-5': '#E94842', + 'primary-7': '#BE1118', + 'primary-4': '#FAC4BC', + 'primary-3': '#F49C92', + 'primary-2': '#F49C92', + 'primary-1': '#FFECE8', + 'warning-6': '#FF7D01', + 'success-6': '#00B429', + 'link-6': '#E41D1D', + 'color-text-1': '#1D212A', + 'color-text-2': '#4E5A6A', + 'color-text-3': '#868F9C', + 'color-text-4': '#f2f3f5', + 'dark-color-text-4': '#4E5A6A', + 'dark-primary-6': '#FCEF92', + 'dark-primary-5': '#F8EFAE', + 'dark-link-6': '#FCEF92', + 'cpu-bg': '#F5F2FB', + 'dark-cpu-bg': '#868F9C', + 'lock-bg': '#E9F0FF', + 'dark-lock-bg': '#868F9C', + 'memory-bg': '#ECF9FF', + 'dark-memory-bg': '#868F9C', + 'connect-bg': '#EEFFEC', + 'dark-connect-bg': '#868F9C', + 'net-bg': '#FFF5E5', + 'dark-net-bg': '#868F9C', + 'session-bg': '#FFECEC', + 'dark-session-bg': '#868F9C', + 'session-top-bg': '#F2F8FF', + 'dark-session-top-bg': '#868F9C', + }, + javascriptEnabled: true + } + } + } + }, + chainWebpack(config) { + config.module.rule('svg').exclude.add(resolve('src/assets/icons')).end() + config.module + .rule('icons') + .test(/\.svg$/) + .include.add(resolve('src/assets/icons')) + .end() + .use('svg-sprite-loader') + .loader('svg-sprite-loader') + .options({ + symbolId: 'icon-[name]' + }) + .end() + }, + devServer: { + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Headers": "*", + "Access-Control-Allow-Methods": "*", + }, + port, + proxy: { + // detail: https://cli.vuejs.org/config/#devserver-proxy + [process.env.VUE_APP_BASE_API]: { + target: process.env.API_BASE_URL, + changeOrigin: true, + ws: true + }, + '/base_url': { + target: process.env.API_BASE_URL, + changeOrigin: true, + pathRewrite: { + '^/base_url': '' + }, + ws: true + } + }, + historyApiFallback: { + rewrites: [ + { from: /\/static-plugin\/base-ops/, to: '/static-plugin/base-ops/index.html' } + ] + }, + allowedHosts: 'all' + } +}) diff --git a/ER-Model/plugins/er-model/pom.xml b/ER-Model/plugins/er-model/pom.xml new file mode 100644 index 000000000..5aa9b368c --- /dev/null +++ b/ER-Model/plugins/er-model/pom.xml @@ -0,0 +1,207 @@ + + + 4.0.0 + + org.opengauss + visualtool-parent + ${admin.version} + ../../pom.xml + + + er-model + + + 17 + 17 + UTF-8 + false + build:prod + + + + + org.projectlombok + lombok + provided + + + org.springframework + spring-context + + + com.gitee.starblues + spring-brick + 3.1.0 + compile + + + org.opengauss + visualtool-service + + + org.opengauss + visualtool-common + + + org.opengauss + opengauss-jdbc + 5.0.2-og + + + org.opengauss + visualtool-framework + + + com.gitee.starblues + spring-brick-bootstrap + + + io.github.nilswende + dbml-java + 1.4.0 + + + com.github.jsqlparser + jsqlparser + 4.6 + + + + + + + com.gitee.starblues + spring-brick-maven-packager + ${spring-brick.version} + + prod + + er-model + org.opengauss.admin.plugin.ERModelPluginApplication + ${project.version} + lita + ER建模插件 + + + + org.intarkdb.** + + + + + + + repackage + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.2.0 + + + + web-ui/dist + + **/* + + false + + + ${basedir}/src/main/resources/resources + + **/* + + false + + + false + true + + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + ${web.build.skip} + + + + exec-npm-install + generate-resources + + exec + + + ${web.build.skip} + npm + + install + --frozen-lockfile + --prefer-offline + --no-optional + + web-ui + + + + + exec-npm-run-build + generate-resources + + exec + + + ${web.build.skip} + npm + + run + ${npm.run.script} + + web-ui + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + + copy-web + generate-resources + + copy-resources + + + ${basedir}/src/main/resources + + + web-ui/dist + resources + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/ERModelPluginApplication.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/ERModelPluginApplication.java new file mode 100644 index 000000000..31d790af7 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/ERModelPluginApplication.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin; + +import com.gitee.starblues.bootstrap.SpringPluginBootstrap; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; + +/** + * Entry point for the ER model plugin when running within the host platform. + * + * @version 1.0.0 + * @since 2025-08-15 + */ +@SpringBootApplication(exclude = { + DataSourceAutoConfiguration.class, + DataSourceTransactionManagerAutoConfiguration.class, + HibernateJpaAutoConfiguration.class +}) +public class ERModelPluginApplication extends SpringPluginBootstrap { + public static void main(String[] args) { + SpringApplication.run(ERModelPluginApplication.class, args); + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/aspectj/LogAspect.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/aspectj/LogAspect.java new file mode 100644 index 000000000..3f2013911 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/aspectj/LogAspect.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.aspectj; + +import com.alibaba.fastjson.JSON; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.opengauss.admin.common.annotation.Log; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.enums.BusinessStatus; +import org.opengauss.admin.common.enums.HttpMethod; +import org.opengauss.admin.common.utils.SecurityUtils; +import org.opengauss.admin.common.utils.ServletUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.opengauss.admin.common.utils.ip.IpUtils; +import org.opengauss.admin.framework.manager.AsyncManager; +import org.opengauss.admin.framework.manager.factory.AsyncFactory; +import org.opengauss.admin.system.domain.SysOperLog; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.HandlerMapping; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Aspect that captures controller invocations to assemble audit logs. + * + * @version 1.0.0 + * @since 2025-08-20 + */ +@Slf4j +@Aspect +@Component +public class LogAspect { + private static final int MAX_MESSAGE_LENGTH = 2000; + + /** + * Process controller invocations that completed successfully. + * + * @param joinPoint join point information + * @param controllerLog logging annotation on the method + * @param jsonResult returned payload + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * Process controller invocations that finished with an exception. + * + * @param joinPoint join point information + * @param controllerLog logging annotation on the method + * @param e thrown exception + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) { + handleLog(joinPoint, controllerLog, e, null); + } + + /** + * Build and dispatch an operation log entry. + * + * @param joinPoint join point information + * @param controllerLog logging annotation + * @param error optional exception + * @param jsonResult optional response payload + */ + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception error, Object jsonResult) { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + if (!(attributes instanceof ServletRequestAttributes)) { + log.warn("Skipping log capture because request attributes are unavailable."); + return; + } + ServletRequestAttributes sra = (ServletRequestAttributes) attributes; + HttpServletRequest request = sra.getRequest(); + if (request == null) { + log.warn("Skipping log capture because HttpServletRequest is unavailable."); + return; + } + + SysOperLog operLog = new SysOperLog(); + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (loginUser != null) { + operLog.setOperName(loginUser.getUsername()); + } + operLog.setStatus(error == null ? BusinessStatus.SUCCESS.ordinal() : BusinessStatus.FAIL.ordinal()); + if (error != null) { + operLog.setErrorMsg(StringUtils.substring(error.getMessage(), 0, MAX_MESSAGE_LENGTH)); + } + + operLog.setOperIp(IpUtils.getIpAddr(request)); + operLog.setOperUrl(request.getRequestURI()); + + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + operLog.setRequestMethod(request.getMethod()); + + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + RequestContextHolder.setRequestAttributes(sra, true); + AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + } + + /** + * Populate operation log metadata based on the annotation details. + * + * @param joinPoint current join point + * @param log log annotation + * @param operLog operation log entity + * @param jsonResult response payload + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, + SysOperLog operLog, Object jsonResult) { + operLog.setBusinessType(log.businessType().ordinal()); + operLog.setTitle(log.title()); + operLog.setOperatorType(log.operatorType().ordinal()); + if (log.isSaveRequestData()) { + setRequestValue(joinPoint, operLog); + } + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) { + operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, MAX_MESSAGE_LENGTH)); + } + } + + /** + * Capture request parameters and add them to the operation log. + * + * @param joinPoint current join point + * @param operLog operation log entity + */ + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) { + String requestMethod = operLog.getRequestMethod(); + if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { + String params = argsListToString(Arrays.asList(joinPoint.getArgs())); + operLog.setOperParam(StringUtils.substring(params, 0, MAX_MESSAGE_LENGTH)); + } else { + HttpServletRequest request = ServletUtils.getRequest(); + Object attribute = request != null + ? request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE) + : null; + if (attribute instanceof Map paramsMap) { + operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, MAX_MESSAGE_LENGTH)); + } + } + } + + /** + * Convert method parameters into a printable string. + * + * @param paramsList method arguments + * @return formatted parameter representation + */ + private String argsListToString(List paramsList) { + if (paramsList == null || paramsList.isEmpty()) { + return ""; + } + StringBuilder builder = new StringBuilder(); + for (Object param : paramsList) { + if (StringUtils.isNotNull(param) && !isFilterObject(param)) { + builder.append(JSON.toJSONString(param)).append(' '); + } + } + return builder.toString().trim(); + } + + /** + * Determine whether to filter the object. + * + * @param o object to evaluate + * @return {@code true} if the object should be excluded from logging + */ + public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return MultipartFile.class.isAssignableFrom(clazz.getComponentType()); + } + if (o instanceof Collection collection) { + return collection.stream().anyMatch(MultipartFile.class::isInstance); + } + if (o instanceof Map map) { + return map.values().stream().anyMatch(MultipartFile.class::isInstance); + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/base/BaseController.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/base/BaseController.java new file mode 100644 index 000000000..a83927b1a --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/base/BaseController.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.base; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +import org.opengauss.admin.common.constant.SqlConstants; +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.common.core.domain.model.LoginUser; +import org.opengauss.admin.common.core.page.PageDomain; +import org.opengauss.admin.common.core.page.TableDataInfo; +import org.opengauss.admin.common.core.page.TableSupport; +import org.opengauss.admin.common.enums.ResponseCode; +import org.opengauss.admin.common.utils.DateUtils; +import org.opengauss.admin.common.utils.SecurityUtils; +import org.opengauss.admin.common.utils.StringUtils; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; + +import java.beans.PropertyEditorSupport; +import java.util.Date; + +/** + * Shared controller utilities providing pagination helpers, Ajax response shortcuts, + * and common data binding configuration for plugin REST endpoints. + * + * @since 2025-10-19 + */ +public class BaseController { + /** + * Register a custom date editor that converts string inputs to {@link Date}. + * + * @param binder web data binder that holds registered editors + */ + @InitBinder + public void initBinder(WebDataBinder binder) { + // Date format + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() { + @Override + public void setAsText(String text) { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * Build a MyBatis-Plus {@link Page} object based on the current request context. + * + * @return configured page instance + */ + protected Page startPage() { + Page page = new Page<>(); + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + String orderBy = pageDomain.getOrderBy(); + String orderColumn = pageDomain.getOrderByColumn(); + String isAsc = pageDomain.getIsAsc(); + + if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize)) { + page.setCurrent(pageNum); + page.setSize(pageSize); + page.setOptimizeCountSql(false); + page.setMaxLimit(500L); + } + + addOrder(orderColumn, isAsc, page); + + return page; + } + + /** + * Build a MyBatis-Plus {@link Page} using explicit pagination parameters. + * + * @param pageNum current page index (1-based) + * @param size page size + * @param orderColumn column used for ordering + * @param order order direction (ASC/DESC) + * @return configured page instance + */ + protected Page startPage(Integer pageNum, Integer size, String orderColumn, String order) { + Page page = new Page(); + page.setCurrent(pageNum); + page.setSize(size); + page.setOptimizeCountSql(false); + page.setMaxLimit(500L); + + addOrder(orderColumn, order, page); + return page; + } + + /** + * Append ordering metadata to a page instance if a column is provided. + * + * @param orderColumn column name used for ordering + * @param order order direction (ASC/DESC) + * @param page page instance receiving the ordering configuration + */ + private void addOrder(String orderColumn, String order, Page page) { + if (StringUtils.isNotBlank(orderColumn)) { + if (SqlConstants.ASC.equalsIgnoreCase(order)) { + page.addOrder(OrderItem.asc(orderColumn)); + } else { + page.addOrder(OrderItem.desc(orderColumn)); + } + } + } + + /** + * Wrap an {@link IPage} result into the standardized {@link TableDataInfo}. + * + * @param page underlying page data + * @return table response containing rows and total count + */ + protected TableDataInfo getDataTable(IPage page) { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(ResponseCode.SUCCESS.code()); + rspData.setMsg(ResponseCode.SUCCESS.msg()); + rspData.setRows(page.getRecords()); + rspData.setTotal(page.getTotal()); + return rspData; + } + + /** + * Convert an affected-row count into a standard Ajax response. + * + * @param rows number of affected rows + * @return success when rows > 0; otherwise error + */ + protected AjaxResult toAjax(int rows) { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * Convert a boolean flag into a standard Ajax response. + * + * @param isSuccess operation success indicator + * @return success or error response + */ + protected AjaxResult toAjax(boolean isSuccess) { + return isSuccess ? success() : error(); + } + + /** + * Produce a success Ajax response without payload. + * + * @return success result + */ + public AjaxResult success() { + return AjaxResult.success(); + } + + /** + * Produce an error Ajax response without payload. + * + * @return error result + */ + public AjaxResult error() { + return AjaxResult.error(); + } + + /** + * Produce a success Ajax response with a message. + * + * @param message success message + * @return success result + */ + public AjaxResult success(String message) { + return AjaxResult.success(message); + } + + /** + * Produce an error Ajax response with a message. + * + * @param message error message + * @return error result + */ + public AjaxResult error(String message) { + return AjaxResult.error(message); + } + + /** + * Retrieve the current authenticated user. + * + * @return logged-in user details + */ + public LoginUser getLoginUser() { + return SecurityUtils.getLoginUser(); + } + + /** + * Retrieve the current authenticated user identifier. + * + * @return user id + */ + public Integer getUserId() { + return getLoginUser().getUserId(); + } + + /** + * Retrieve the current authenticated username. + * + * @return username + */ + public String getUsername() { + return getLoginUser().getUsername(); + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/config/DataSourceConfig.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/config/DataSourceConfig.java new file mode 100644 index 000000000..f4b43e75a --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/config/DataSourceConfig.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.config; + +import com.gitee.starblues.bootstrap.PluginContextHolder; +import com.gitee.starblues.spring.environment.EnvironmentProvider; +import org.opengauss.admin.plugin.enums.DbDataLocationEnum; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer; +import org.springframework.boot.sql.init.DatabaseInitializationMode; +import org.springframework.boot.sql.init.DatabaseInitializationSettings; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +import javax.sql.DataSource; +import java.util.Optional; + +/** + * Data source configuration for ER model plugin. + * Retrieves database configuration from the main application. + * + * @version 1.0.0 + * @since 2025-10-28 + */ +@Configuration +public class DataSourceConfig { + + @Bean + @ConfigurationProperties(prefix = "spring.datasource") + public DataSource dataSource() { + EnvironmentProvider environmentProvider = PluginContextHolder.getEnvironmentProvider(); + // read config from dataKit platform + String url = environmentProvider.getString("spring.datasource.url"); + String username = environmentProvider.getString("spring.datasource.username"); + String password = environmentProvider.getString("spring.datasource.password"); + String driverClassName = environmentProvider.getString("spring.datasource.driver-class-name"); + + return DataSourceBuilder.create() + .driverClassName(driverClassName) + .url(url) + .username(username) + .password(password) + .build(); + } + + @Bean("opsDataSourceScriptDatabaseInitializer") + @Profile("!dev") + DataSourceScriptDatabaseInitializer dataSourceScriptDatabaseInitializer(DataSource dataSource) { + EnvironmentProvider environmentProvider = PluginContextHolder.getEnvironmentProvider(); + String driverClassName = environmentProvider.getString("spring.datasource.driver-class-name"); + Optional dbDataLocationEnum = DbDataLocationEnum.of(driverClassName); + DatabaseInitializationSettings settings = new DatabaseInitializationSettings(); + settings.setContinueOnError(true); + settings.setSeparator(";"); + settings.setMode(DatabaseInitializationMode.ALWAYS); + if (dbDataLocationEnum.isEmpty()) { + return new DataSourceScriptDatabaseInitializer(dataSource, new DatabaseInitializationSettings()); + } + settings.setDataLocations(dbDataLocationEnum.get().getLocations()); + return new DataSourceScriptDatabaseInitializer(dataSource, settings); + } +} \ No newline at end of file diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/config/PluginExtensionInfoConfig.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/config/PluginExtensionInfoConfig.java new file mode 100644 index 000000000..34dfcea6f --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/config/PluginExtensionInfoConfig.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.config; + +import com.gitee.starblues.core.PluginExtensionInfo; + +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * Supplies extension metadata for registering the ER model plugin within the host platform. + * + * @version 1.0.0 + * @since 2025-08-10 + */ +@Component +public class PluginExtensionInfoConfig implements PluginExtensionInfo { + private static final String LOGO_BASE64 = (""" +PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3 +LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3Jn +LzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAzMTAg +MzEwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAzMTAgMzEwOyIg +eG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGQ9Ik0zMDAuNTY0LDE3OS4zMTFM +MjgyLjQsMTY4LjgyNGMwLjQ4OS00LjU0MywwLjc0Ny05LjE1NCwwLjc0Ny0xMy44 +MjRzLTAuMjU4LTkuMjgxLTAuNzQ3LTEzLjgyNGwxOC4xNjQtMTAuNDg3CgljMy44 +ODEtMi4yNDEsNi42NTgtNS44NjcsNy44MTYtMTAuMTkxYzEuMTYtNC4zMzwwLjU2 +NC04Ljg1NC0xLjY3Ni0xMi43MzVsLTM0Ljk0My02MC41MjRjLTIuOTktNS4xOC04 +LjU2NC04LjM5Ni0xNC41NTEtOC4zOTYKCWMtMi45MywwLTUuODI2LDAuNzc4LTgu +Mzc3LDIuMjUxTDIzMC42MTksNTAuNjE2Yy03LjQwMi01LjQyOS0xNS40MDYtMTAu +MDgzLTIzLjg5My0xMy44NDJWMzYuNzgzCglDMjA2LjcyNyw3LjUyOSwxOTkuMTk1 +LDAsMTg5Ljk0NSwwaC02OS44OTFjLTkuMjU0LDAtMTYuNzgxLDcuNTI5LTE2Ljc4 +MSwxNi43ODN2MjAuOTg1Yy04LjQ4NiwzLjc1OS0xNi40OSw4LjQxMy0yMy44OTQs +MTMuODQyCglMNjEuMTY0LDQxLjA5NGMtMi41NTEtMS40NzMtNS40NDUtMi4yNS04 +LjM3NS0yLjI1Yy01Ljk4NiwwLTExLjU2MywzLjIxNS0xNC41NTMsOC4zOTVMMy4y +OTUsMTA3Ljc2MgoJYy0yLjI0MiwzLjg4MS0yLjgzNiw4LjQwNi0xLjY3NCwxMi43 +MzZjMS4xNTYsNC4zMiwzLjkzNSw3Ljk0OSw3LjgxNCwxMC4xOTFMIDI3LjYsMTQx +LjE3NmMtMC40ODksNC41NDMtMC43NDcsOS4xNTQtMC43NDcsMTMuODI0CglzMC4y +NTgsOS4yODEsMC43NDcsMTMuODI0TDkuNDM1LDE3OS4zMTFjLTMuODc5LDIuMjQx +LTYuNjU4LDUuODYtNy44MTQsMTAuMTkxYy0xLjE2Miw0LjMzLTAuNTY4LDguODU1 +LDEuNjc0LDEyLjczNWwzNC45NDEsNjAuNTI0CgljMi45OSw1LjE4LDguNTY2LDgu +Mzk1LDE0LjU1Myw4LjM5NWMyLjkzLDAsNS44MjQtMC43NzcsOC4zNzUtMi4yNUw3 +OS4zOCwyNTguMzljNy40MDMsNS40MjksMTUuNDA3LDEwLjA4MywyMy44OTQsMTMu +ODQyCgl2MjAuOTg2YzAsNC40ODIsMS43NDQsOC42OTUsNC45MTQsMTEuODY2YzMu +MTc0LDMuMTY5LDcuMzg1LDQuOTE2LDExLjg2Nyw0LjkxNmg2OS44OTFjOS4yNSww +LDE2Ljc4MS03LjUyOSwxNi43ODEtMTYuNzgyCgl2LTIwLjk4NmM4LjQ4Ni0zLjc1 +OSwxNi40OS04LjQxMywyMy44OTMtMTMuODQybDE4LjIxNSwxMC41MTdjMi41NTEs +MS40NzMsNS40NDcsMi4yNSw4LjM3NywyLjI1YzUuOTg2LDAsMTEuNTYxLTMuMjE1 +LDE0LjU1MS04LjM5NQoJbDM0Ljk0My02MC41MjNjMi4yNC0zLjg4MSwyLjgzNi04 +LjQwNiwxLjY3Ni0xMi43MzZDMzA3LjIyMywxODUuMTcyLDMwNC40NDUsMTgxLjU1 +MywzMDAuNTY0LDE3OS4zMTF6IE0xNTUsMjQ2LjEwMQoJYy0xOC4yMywwLTM1LjIw +Ny01LjM1Ny00OS40NDktMTQuNTc5bDMwLjgwMS0zMC44MDRjNi40NDksMi43NzIs +MTMuNDUsNC4yNCwyMC42NzcsNC4yNDFjMC4wMDIsMCwwLjAwMywwLDAuMDA0LDAK +CWMxNC4wMTEsMCwyNy4xNzUtNS40NjYsMzcuMDY0LTE1LjM5YzEzLjUtMTMuNTM2 +LDE4LjU0MS0zMy4zNjMsMTMuMTU1LTUxLjc0M2MtMC4zMTMtMS4wNjktMS4xNjgt +MS44OTQtMi4yNDgtMi4xNjkKCWMtMS4wNzgtMC4yNzctMi4yMjUsMC4wNC0zLjAx +MSwwLjgyOWwtMzIuOTcsMzMuMDY5Yy0xLjk3OS0wLjgwNC02Ljk2MS0zLjU0Mi0x +Ni4xODYtMTIuNzM2CgljLTkuMjI2LTkuMTk3LTExLjk3Ni0xNC4xNzMtMTIuNzgz +LTE2LjE0OGwzMi45NjYtMzMuMDY5YzAuNzg3LTAuNzg5LDEuMDk4LTEuOTM1LDAu +ODItMy4wMTNjLTAuMjc4LTEuMDc5LTEuMTA1LTEuOTMxLTIuMTc1LTIuMjQxCglj +LTQuNzUxLTEuMzc4LTkuNjc2LTIuMDc4LTE0LjYzNy0yLjA3OGMtMTQuMDE2LDAt +MjcuMTgxLDUuNDY0LTM3LjA2OSwxNS4zODVjLTkuODczLDkuOTAzLTE1LjI5OSwy +My4wNTctMTUuMjgsMzcuMDM5CgljMC4wMSw3LjIwNCwxLjQ3NiwxNC4xOCw0LjI0 +LDIwLjYwNGwtMzAuNzIyLDMwLjcyNEM2OS4xNDUsMTg5Ljg2OCw2My44OTYsMTcz +LjA0Nyw2My44OTYsMTU1CgljMC01MC4zMTMsNDAuNzg3LTkxLjEwMiw5MS4xMDQt +OTEuMTAyczkxLjEwMiw0MC43ODksOTEuMTAyLDkxLjEwMlMyMDUuMzE2LDI0Ni4x +MDEsMTU1LDI0Ni4xMDF6Ii8+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjxn +Pgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8 +L2c+""" + ).replace("\n", ""); + + @Override + public Map extensionInfo() { + Map map = new HashMap<>(); + map.put("logo", LOGO_BASE64); + map.put("descriptionEn", "Basic Operation Plugin"); + return map; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/config/ThreadPoolConfig.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/config/ThreadPoolConfig.java new file mode 100644 index 000000000..45150ed5c --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/config/ThreadPoolConfig.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.config; + +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.opengauss.admin.common.utils.Threads; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * Configures the thread pools used by the ER model plugin for async and scheduled workloads. + * + * @version 1.0.0 + * @since 2025-09-05 + */ +@Configuration +public class ThreadPoolConfig { + private int corePoolSize = 10; + + private int maxPoolSize = 20; + + private int queueCapacity = 2000; + + private int keepAliveSeconds = 300; + + /** + * Primary executor for asynchronous tasks within the plugin. + * + * @return configured {@link ThreadPoolTaskExecutor} + */ + @Bean(name = "threadPoolTaskExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(maxPoolSize); + executor.setCorePoolSize(corePoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * Scheduler for periodic plugin tasks. + * + * @return configured {@link ScheduledExecutorService} + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() { + return new ScheduledThreadPoolExecutor(corePoolSize, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) { + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/DiagramController.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/DiagramController.java new file mode 100644 index 000000000..910c3ed46 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/DiagramController.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.controller; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.dto.TableDTO; +import org.opengauss.admin.plugin.enums.DatabaseDialect; +import org.opengauss.admin.plugin.service.DataSourceService; +import org.opengauss.admin.plugin.service.SchemaIntrospectionService; +import org.opengauss.admin.plugin.translate.SqlToDbmlTranslator; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Controller for ER diagram features. + * Performs reverse engineering to produce the data required for ER diagrams. + * + * @version 1.0.0 + * @since 2025-09-10 + */ +@Slf4j +@RestController +@RequestMapping("/diagram") +public class DiagramController { + private final SchemaIntrospectionService schemaIntrospectionService; + private final DataSourceService dataSourceService; + + public DiagramController(SchemaIntrospectionService schemaIntrospectionService, + DataSourceService dataSourceService) { + this.schemaIntrospectionService = schemaIntrospectionService; + this.dataSourceService = dataSourceService; + } + + /** + * Reverse-engineer the specified database/schema into a DBML string. + * + * @param clusterId cluster identifier + * @param nodeId node identifier + * @param databaseName database to introspect + * @param schemaName schema to introspect + * @return AjaxResult containing DBML text and dialect information + */ + @GetMapping("/get-dbml-schema") + public AjaxResult getSchemaAsDbml( + @RequestParam("clusterId") String clusterId, + @RequestParam("nodeId") String nodeId, + @RequestParam("databaseName") String databaseName, + @RequestParam("schemaName") String schemaName) { + log.info("Received DBML generation request -> clusterId={}, nodeId={}, databaseName={}, schema={}", + clusterId, nodeId, databaseName, schemaName); + try { + // 1) Resolve the dialect + Optional dialectOpt = dataSourceService.getDialectForInstance(clusterId, nodeId); + if (dialectOpt.isEmpty()) { + return AjaxResult.error("Unable to resolve database dialect for the instance."); + } + DatabaseDialect dialect = dialectOpt.get(); + + // 2) Reverse-engineer DTOs + List tables = schemaIntrospectionService.getSchemaAsDTOs( + clusterId, + nodeId, + databaseName, + schemaName); + if (tables == null || tables.isEmpty()) { + log.warn("No tables found in schema: {}", schemaName); + return AjaxResult.success(Map.of("dbmlString", "", "dialect", dialect.getDialectName())); + } + + // 3) Generate DBML (with dialect info) + String dbml = SqlToDbmlTranslator.generate(tables); + return AjaxResult.success(Map.of( + "dbmlString", dbml, + "dialect", dialect.getDialectName() + )); + } catch (java.sql.SQLException | ClassNotFoundException ex) { + log.error("Database access error during reverse engineering", ex); + return AjaxResult.error("Database error: " + ex.getMessage()); + } catch (IllegalArgumentException ex) { + log.warn("Invalid reverse engineering request: {}", ex.getMessage()); + return AjaxResult.error("Invalid request: " + ex.getMessage()); + } catch (RuntimeException ex) { + log.error("Failed to generate DBML: clusterId={}, nodeId={}, schema={}", clusterId, nodeId, schemaName, ex); + return AjaxResult.error("Failed to generate DBML: " + ex.getMessage()); + } + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/ErModelSpaForwardController.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/ErModelSpaForwardController.java new file mode 100644 index 000000000..f863c8f45 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/ErModelSpaForwardController.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * SPA fallback controller so that history-mode routes under /static-plugin/er-model/ + * always serve the compiled index.html artifact. + * + * Without this forwarder the host would return 404 for paths such as + * /static-plugin/er-model/editor because the static resource handler only knows + * about physical files. By forwarding to index.html we let the Vue/Quasar router + * take over on the client side. + * + * @since 2025-10-28 + */ +@Slf4j +@Controller +public class ErModelSpaForwardController { + private static final String FORWARD_INDEX = "forward:/static-plugin/er-model/index.html"; + + @RequestMapping(value = { + "/static-plugin/er-model", + "/static-plugin/er-model/", + "/static-plugin/er-model/{path:[^\\.]*}" + }) + public String forwardToIndex() { + log.debug("Forwarding ER model SPA request to index.html"); + return FORWARD_INDEX; + } +} + diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/ExecuteController.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/ExecuteController.java new file mode 100644 index 000000000..9a9920aea --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/ExecuteController.java @@ -0,0 +1,226 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.controller; + +import com.wn.dbml.compiler.DbmlParser; +import com.wn.dbml.compiler.ParsingException; +import com.wn.dbml.model.Database; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.dto.CreateSchemaRequest; +import org.opengauss.admin.plugin.dto.ExecuteSQLRequestDTO; +import org.opengauss.admin.plugin.dto.ManagedInstanceDTO; +import org.opengauss.admin.plugin.dto.TableDTO; +import org.opengauss.admin.plugin.dto.TestConnectionRequest; +import org.opengauss.admin.plugin.service.DataSourceService; +import org.opengauss.admin.plugin.service.SqlExecutionService; +import org.opengauss.admin.plugin.sql.core.SchemaPreambleBuilder; +import org.opengauss.admin.plugin.sql.router.SqlGeneratorRouter; +import org.opengauss.admin.plugin.translate.DbmlToDtoTranslator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +/** + * REST controller that exposes endpoints for executing DBML-generated SQL against managed instances. + * + * @version 1.0.0 + * @since 2025-09-15 + */ +@Slf4j +@RestController +@RequestMapping("/execute") +public class ExecuteController { + private final DataSourceService dataSourceService; + private final SqlExecutionService sqlExecutionService; + + @Autowired + public ExecuteController(DataSourceService dataSourceService, SqlExecutionService sqlExecutionService) { + this.dataSourceService = dataSourceService; + this.sqlExecutionService = sqlExecutionService; + } + + /** + * Validate whether the platform can obtain a JDBC connection to the requested node. + * + * @param request connection request payload + * @return result of the connection attempt + */ + @PostMapping("/test-connection") + public AjaxResult testConnection(@RequestBody TestConnectionRequest request) { + log.info("Received connection test request -> clusterId={}, nodeId={}", + request.getClusterId(), request.getNodeId()); + try (Connection connection = dataSourceService.getConnection(request.getClusterId(), request.getNodeId())) { + if (connection != null && !connection.isClosed()) { + DatabaseMetaData metaData = connection.getMetaData(); + String successMsg = String.format( + "Connection established. Product: %s, Version: %s, URL: %s", + metaData.getDatabaseProductName(), + metaData.getDatabaseProductVersion(), + metaData.getURL() + ); + log.info("Connection test succeeded: {}", successMsg); + return AjaxResult.success(Map.of("message", successMsg)); + } else { + log.error("Connection test failed: connection is null or closed."); + return AjaxResult.error("Obtained connection is null or already closed."); + } + } catch (SQLException | ClassNotFoundException e) { + log.error("Connection test error -> clusterId={}, nodeId={}", + request.getClusterId(), request.getNodeId(), e); + return AjaxResult.error("Failed to acquire connection: " + e.getMessage()); + } + } + + /** + * List managed database instances available for execution. + * + * @return managed instances + */ + @GetMapping("/list-instances") + public AjaxResult listInstances() { + List instances = dataSourceService.listManagedInstances(); + return AjaxResult.success(Map.of("instances", instances)); + } + + /** + * Execute SQL generated from DBML content on the target instance. + * + * @param request execution request containing DBML payload and target details + * @return success or error outcome + */ + @PostMapping("/from-dbml") + public AjaxResult executeSqlFromDbml(@RequestBody ExecuteSQLRequestDTO request) { + try { + if (request.getDbmlText() == null || request.getDbmlText().isBlank()) { + return AjaxResult.error("Invalid request. Field 'dbmlText' must be provided."); + } + if (request.getDialect() == null) { + return AjaxResult.error("Database dialect must be provided."); + } + if (request.getTargetSchema() == null || request.getTargetSchema().isBlank()) { + return AjaxResult.error("Target schema name (targetSchema) must be provided."); + } + + log.info("Received DBML execution request -> clusterId={}, nodeId={}, dialect={}, schema={}", + request.getClusterId(), request.getNodeId(), + request.getDialect().getDialectName(), request.getTargetSchema()); + + Database database = DbmlParser.parse(request.getDbmlText()); + List tables = DbmlToDtoTranslator.map(database); + + // Build the preamble that creates and switches to the target schema/database + String preamble = SchemaPreambleBuilder.build(request.getDialect(), request.getTargetSchema()); + + // Generate the DDL; it can omit schema prefixes (see MSSQL note) + String ddl = SqlGeneratorRouter.generateSQL(tables, request.getDialect()); + + // Combine into the final script + String sqlToExecute = preamble + ddl; + + log.info("Generated SQL based on DBML:\n{}", sqlToExecute); + + sqlExecutionService.executeSql(request.getClusterId(), request.getNodeId(), sqlToExecute); + + log.info("SQL executed successfully -> clusterId={}, nodeId={}, schema={}", + request.getClusterId(), request.getNodeId(), request.getTargetSchema()); + return AjaxResult.success("SQL executed successfully."); + } catch (ParsingException e) { + log.error("Failed to execute SQL: DBML parsing error", e); + return AjaxResult.error("Failed to parse DBML: " + e.getMessage()); + } catch (IllegalArgumentException e) { + log.error("Failed to execute SQL: invalid argument", e); + return AjaxResult.error(e.getMessage()); + } catch (SQLException | ClassNotFoundException e) { + log.error("Failed to execute SQL -> clusterId={}, nodeId={}", + request.getClusterId(), request.getNodeId(), e); + return AjaxResult.error("SQL execution failed: " + e.getMessage()); + } + } + + /** + * Create a schema on the specified database if it does not yet exist. + * + * @param request schema creation payload + * @return success or error outcome + */ + @PostMapping("/create-schema") + public AjaxResult createSchema(@RequestBody CreateSchemaRequest request) { + if (request == null) { + return AjaxResult.error("Request body must be provided."); + } + String clusterId = request.getClusterId(); + String nodeId = request.getNodeId(); + String databaseName = request.getDatabaseName(); + String schemaName = request.getSchemaName(); + + if (clusterId == null || clusterId.isBlank() + || nodeId == null || nodeId.isBlank() + || databaseName == null || databaseName.isBlank() + || schemaName == null || schemaName.isBlank()) { + return AjaxResult.error("clusterId, nodeId, databaseName, and schemaName are required."); + } + + log.info("Received schema creation request -> clusterId={}, nodeId={}, database={}, schema={}", + clusterId, nodeId, databaseName, schemaName); + try { + dataSourceService.createSchemaIfAbsent(clusterId, nodeId, databaseName, schemaName); + return AjaxResult.success("Schema created or already exists."); + } catch (IllegalArgumentException ex) { + return AjaxResult.error(ex.getMessage()); + } catch (SQLException | ClassNotFoundException ex) { + log.error("Failed to create schema -> clusterId={}, nodeId={}, database={}, schema={}", + clusterId, nodeId, databaseName, schemaName, ex); + return AjaxResult.error("Failed to create schema: " + ex.getMessage()); + } + } + + /** + * Retrieve schema names for a given database on a managed instance. + * + * @param clusterId cluster identifier + * @param nodeId node identifier + * @param databaseName database name within the instance + * @return available schema names + */ + @GetMapping("/list-schemas") + public AjaxResult listSchemas(@RequestParam("clusterId") String clusterId, + @RequestParam("nodeId") String nodeId, + @RequestParam("databaseName") String databaseName) { + log.info("Received schema list request -> clusterId={}, nodeId={}, database={}", + clusterId, nodeId, databaseName); + try { + List schemas = dataSourceService.listSchemas(clusterId, nodeId, databaseName); + return AjaxResult.success(Map.of("schemas", schemas)); + } catch (SQLException | ClassNotFoundException e) { + log.error("Failed to list schemas -> clusterId={}, nodeId={}, database={}", + clusterId, nodeId, databaseName, e); + return AjaxResult.error("Failed to list schemas: " + e.getMessage()); + } + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/ExportController.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/ExportController.java new file mode 100644 index 000000000..2984d7469 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/ExportController.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.controller; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.dto.ExportRequestDTO; +import org.opengauss.admin.plugin.enums.DatabaseDialect; +import org.opengauss.admin.plugin.service.DbmlExportService; +import org.opengauss.admin.plugin.service.ExportService; +import org.opengauss.admin.plugin.service.SchemaIntrospectionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Base64; +import java.util.Map; + +/** + * REST endpoints that convert DBML definitions or live schemas into SQL and PlantUML exports. + * + * @version 1.0.0 + * @since 2025-09-20 + */ +@Slf4j +@RestController +@RequestMapping("/export") +public class ExportController { + private final ExportService exportService; + private final DbmlExportService dbmlExportService; + private final SchemaIntrospectionService schemaIntrospectionService; + + @Autowired + public ExportController(ExportService exportService, + DbmlExportService dbmlExportService, + SchemaIntrospectionService schemaIntrospectionService) { + this.exportService = exportService; + this.dbmlExportService = dbmlExportService; + this.schemaIntrospectionService = schemaIntrospectionService; + } + + /** + * Export SQL content based on the provided export request. + * + * @param dto export configuration including target dialect and tables + * @return Base64-encoded SQL file payload wrapped in {@link AjaxResult} + */ + @PostMapping("/sql") + public AjaxResult exportSql(@RequestBody ExportRequestDTO dto) { + try { + File sqlFile = exportService.generateSqlFile(dto); + byte[] fileBytes = Files.readAllBytes(sqlFile.toPath()); + // Encode the file bytes as Base64 (or adapt to whatever the front end expects) + // Here we assume the front end can consume Base64 data + String base64Data = Base64.getEncoder().encodeToString(fileBytes); + return AjaxResult.success("Export succeeded", base64Data); + } catch (IOException e) { + log.error("Failed to export SQL file", e); + return AjaxResult.error("Failed to export SQL file: " + e.getMessage()); + } + } + + /** + * Export SQL by translating DBML content for the specified dialect. + * + * @param dbmlText DBML script uploaded by the client + * @param dialect target database dialect + * @return generated SQL snippet wrapped in {@link AjaxResult} + */ + @PostMapping(value = "/sql-from-dbml", consumes = "text/plain", produces = "application/json;charset=UTF-8") + public AjaxResult exportSqlFromDbml( + @RequestBody String dbmlText, + @RequestParam("dialect") DatabaseDialect dialect) { + if (dialect == null) { + return AjaxResult.error("Database dialect must be provided."); + } + try { + String generatedSql = dbmlExportService.generateSqlFromDbml(dbmlText, dialect); + return AjaxResult.success(Map.of("sqlContent", generatedSql)); + } catch (IllegalArgumentException e) { + return AjaxResult.error("Invalid parameter: " + e.getMessage()); + } catch (Exception e) { + log.error("Failed to generate SQL from DBML", e); + return AjaxResult.error("Failed to generate SQL from DBML: " + e.getMessage()); + } + } + + /** + * Export PlantUML markup generated from DBML content used in the ER diagram editor. + * + * @param dbmlContent DBML source uploaded by the client + * @return PlantUML markup wrapped in {@link AjaxResult} + */ + @PostMapping("/plantuml-from-dbml") + public AjaxResult exportPlantUMLFromDbml(@RequestBody String dbmlContent) { + try { + String plantUML = dbmlExportService.generatePlantUmlFromDbml(dbmlContent); + return AjaxResult.success("success", plantUML); + } catch (IllegalArgumentException iae) { + log.warn("Invalid DBML content: {}", iae.getMessage()); + return AjaxResult.error("Invalid DBML content: " + iae.getMessage()); + } catch (Exception e) { + log.error("Export PlantUML from DBML failed", e); + return AjaxResult.error("Export PlantUML from DBML failed: " + e.getMessage()); + } + } + + /** + * Export PlantUML markup by introspecting a live database schema. + * + * @param clusterId cluster identifier + * @param nodeId node identifier + * @param databaseName database name to introspect + * @param schemaName schema name to introspect + * @return PlantUML markup wrapped in {@link AjaxResult} + */ + @GetMapping("/plantuml-from-db") + public AjaxResult exportPlantUMLFromDb(@RequestParam String clusterId, + @RequestParam String nodeId, + @RequestParam String databaseName, + @RequestParam String schemaName) { + try { + String plantUML = schemaIntrospectionService.generatePlantUmlFromDb( + clusterId, nodeId, databaseName, schemaName); + return AjaxResult.success("success", plantUML); + } catch (IllegalArgumentException iae) { + log.warn("Invalid params: {}", iae.getMessage()); + return AjaxResult.error("Invalid params: " + iae.getMessage()); + } catch (Exception e) { + log.error("Export PlantUML from DB failed", e); + return AjaxResult.error("Export PlantUML from DB failed: " + e.getMessage()); + } + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/ImportController.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/ImportController.java new file mode 100644 index 000000000..30ed62c92 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/controller/ImportController.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.AjaxResult; +import org.opengauss.admin.plugin.dto.SqlToDbmlRequest; +import org.opengauss.admin.plugin.enums.DatabaseDialect; +import org.opengauss.admin.plugin.service.SqlImportService; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +/** + * REST endpoints for importing SQL DDL and translating it into DBML representations. + * + * @version 1.0.0 + * @since 2025-09-25 + */ +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/import") +public class ImportController { + private final SqlImportService sqlImportService; + + /** + * Import SQL (DDL only) and convert it to DBML. + * + * @param req request payload containing SQL text and dialect metadata + * @return {@link AjaxResult} wrapping the generated DBML content + */ + @PostMapping("/sql-to-dbml") + public AjaxResult sqlToDbml(@RequestBody SqlToDbmlRequest req) { + if (req == null || req.getSqlText() == null || req.getSqlText().isBlank()) { + return AjaxResult.error("SQL text must not be empty."); + } + try { + // Translate front-end dialect string to enum; allow AUTO for backend detection + DatabaseDialect dialect = resolveDialect(req.getDialect(), req.getSqlText()); + boolean shouldSkipData = req.getSkipData() == null || req.getSkipData(); // Skip data by default + + String dbml = sqlImportService.sqlToDbml(req.getSqlText(), dialect, shouldSkipData); + return AjaxResult.success(Map.of("dbmlString", dbml)); + } catch (IllegalArgumentException iae) { + log.warn("SQL-to-DBML parameter error: {}", iae.getMessage()); + return AjaxResult.error("Invalid parameters: " + iae.getMessage()); + } catch (RuntimeException e) { + log.error("SQL-to-DBML processing failed", e); + return AjaxResult.error("SQL-to-DBML failed: " + e.getMessage()); + } + } + + private DatabaseDialect resolveDialect(String atClient, String sql) { + if (atClient != null && !"AUTO".equalsIgnoreCase(atClient.trim())) { + return DatabaseDialect.fromString(atClient); + } + // AUTO mode: apply a lightweight heuristic detection + String s = (sql == null ? "" : sql).toLowerCase(); + if (s.contains("`") || s.contains(" engine=") || s.contains(" unsigned")) { + return DatabaseDialect.MYSQL; + } + // Treat openGauss as close to PostgreSQL + return DatabaseDialect.POSTGRESQL; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/domain/ConnectionRequest.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/domain/ConnectionRequest.java new file mode 100644 index 000000000..48675e6ff --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/domain/ConnectionRequest.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.domain; + +import org.opengauss.admin.plugin.enums.DatabaseDialect; + +/** + * Represents a request for establishing a database connection. + * + * @since 2025-10-10 + */ +public record ConnectionRequest( + DatabaseDialect dialect, + String host, + int port, + String database, + String username, + String password, + String schema) { + + /** + * Creates a new builder for constructing ConnectionRequest instances. + * + * @return a new builder instance + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns a new ConnectionRequest with the specified schema name. + * + * @param schemaName the schema name to use, or null/blank to keep the current schema + * @return a new ConnectionRequest with the specified schema + */ + public ConnectionRequest withSchema(String schemaName) { + String normalizedSchema = schemaName == null || schemaName.isBlank() + ? this.schema + : schemaName; + return new ConnectionRequest(dialect, host, port, database, username, password, normalizedSchema); + } + + /** + * Returns a new ConnectionRequest with the specified database name. + * + * @param databaseName the database name to use, or null/blank to keep the current database + * @return a new ConnectionRequest with the specified database + */ + public ConnectionRequest withDatabase(String databaseName) { + String normalizedDatabase = databaseName == null || databaseName.isBlank() + ? this.database + : databaseName; + return new ConnectionRequest(dialect, host, port, normalizedDatabase, username, password, schema); + } + + /** + * Builder for constructing ConnectionRequest instances. + */ + public static final class Builder { + private DatabaseDialect dialect; + private String host; + private int port; + private String database; + private String username; + private String password; + private String schema; + + /** + * Sets the database dialect. + * + * @param value the database dialect + * @return this builder + */ + public Builder dialect(DatabaseDialect value) { + this.dialect = value; + return this; + } + + /** + * Sets the host. + * + * @param value the host + * @return this builder + */ + public Builder host(String value) { + this.host = value; + return this; + } + + /** + * Sets the port. + * + * @param value the port + * @return this builder + */ + public Builder port(int value) { + this.port = value; + return this; + } + + /** + * Sets the database name. + * + * @param value the database name + * @return this builder + */ + public Builder database(String value) { + this.database = value; + return this; + } + + /** + * Sets the username. + * + * @param value the username + * @return this builder + */ + public Builder username(String value) { + this.username = value; + return this; + } + + /** + * Sets the password. + * + * @param value the password + * @return this builder + */ + public Builder password(String value) { + this.password = value; + return this; + } + + /** + * Sets the schema. + * + * @param value the schema + * @return this builder + */ + public Builder schema(String value) { + this.schema = value; + return this; + } + + /** + * Builds a new ConnectionRequest instance. + * + * @return a new ConnectionRequest + */ + public ConnectionRequest build() { + return new ConnectionRequest(dialect, host, port, database, username, password, schema); + } + } +} \ No newline at end of file diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/domain/InlineReference.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/domain/InlineReference.java new file mode 100644 index 000000000..75ae10d05 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/domain/InlineReference.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.domain; + +import java.util.List; +import java.util.Optional; + +/** + * Represents an inline reference in a column definition. + * + * @since 2025-10-12 + */ +public record InlineReference( + String referencedTable, + List referencedColumns, + Optional onDeleteAction, + Optional onUpdateAction) { +} \ No newline at end of file diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/domain/JdbcNodeRef.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/domain/JdbcNodeRef.java new file mode 100644 index 000000000..4a4693732 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/domain/JdbcNodeRef.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.domain; + +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterNodeVO; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterVO; + +/** + * Represents a reference to a JDBC node along with its cluster information. + * + * @since 2025-10-10 + */ +public record JdbcNodeRef(JdbcDbClusterVO cluster, JdbcDbClusterNodeVO node) { +} \ No newline at end of file diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/domain/ReferenceParseResult.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/domain/ReferenceParseResult.java new file mode 100644 index 000000000..bebb1e217 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/domain/ReferenceParseResult.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.domain; + +import java.util.List; +import java.util.Optional; + +/** + * Represents the result of parsing a reference in a table definition. + * + * @since 2025-10-12 + */ +public record ReferenceParseResult( + String referencedTable, + List referencedColumns, + Optional onDelete, + Optional onUpdate) { +} \ No newline at end of file diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ColumnDTO.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ColumnDTO.java new file mode 100644 index 000000000..4d7c7205e --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ColumnDTO.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.dto; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * Data transfer object representing a single column and its metadata used for SQL generation. + * + * @version 1.0.0 + * @since 2025-08-25 + */ +@Data +public class ColumnDTO { + /* -------------- Basic metadata -------------- */ + private String columnName; // Column name + private String dataType; // Logical data type (varchar / int / enum, already normalised) + + /* -------------- Optional parameters -------------- */ + private Integer length; // Character length or bit width + private Integer precision; // Numeric precision (p) + private Integer scale; // Numeric scale (s) + + /* -------------- Constraints and defaults -------------- */ + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private Boolean isNullable; // Whether the column is nullable + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private Boolean isPrimaryKey; // Whether the column participates in the primary key + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private Boolean isUnique; // Whether the column has a unique constraint + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private Boolean isAutoIncrement; // Whether the column auto-increments + private String defaultValue; // Default value (raw text) + private String comment; // Column comment + + /* -------------- MySQL-specific -------------- */ + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private Boolean isUnsigned; // Whether UNSIGNED/ZEROFILL is applied (integer or floating types) + private List enumValues; // ENUM/SET options + + public Boolean getNullable() { + return isNullable; + } + + public void setNullable(Boolean isNullable) { + this.isNullable = isNullable; + } + + public Boolean getPrimaryKey() { + return isPrimaryKey; + } + + public void setPrimaryKey(Boolean isPrimaryKey) { + this.isPrimaryKey = isPrimaryKey; + } + + public Boolean getUnique() { + return isUnique; + } + + public void setUnique(Boolean isUnique) { + this.isUnique = isUnique; + } + + public Boolean getAutoIncrement() { + return isAutoIncrement; + } + + public void setAutoIncrement(Boolean isAutoIncrement) { + this.isAutoIncrement = isAutoIncrement; + } + + public Boolean getUnsigned() { + return isUnsigned; + } + + public void setUnsigned(Boolean isUnsigned) { + this.isUnsigned = isUnsigned; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/CreateSchemaRequest.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/CreateSchemaRequest.java new file mode 100644 index 000000000..da0bb7ca8 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/CreateSchemaRequest.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.dto; + +import lombok.Data; + +/** + * Request payload for creating a schema on a managed instance. + * + * @since 2025-10-29 + */ +@Data +public class CreateSchemaRequest { + private String clusterId; + private String nodeId; + private String databaseName; + private String schemaName; +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ExecuteResult.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ExecuteResult.java new file mode 100644 index 000000000..4816d15ad --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ExecuteResult.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.dto; + +import java.util.Objects; + +/** + * Immutable DTO describing whether a task succeeded together with a human readable message. + * + * @since 2025-03-10 + */ +public final class ExecuteResult { + private static final String DEFAULT_SUCCESS_MESSAGE = "操作成功"; + + private final boolean isSuccess; + private final String message; + + private ExecuteResult(boolean isSuccessful, String message) { + this.isSuccess = isSuccessful; + this.message = message; + } + + /** + * Create a successful result with a default success message. + * + * @return success result + */ + public static ExecuteResult success() { + return success(DEFAULT_SUCCESS_MESSAGE); + } + + /** + * Create a successful result with a custom message. + * + * @param message success description, sanitized to non-null + * @return success result + */ + public static ExecuteResult success(String message) { + return new ExecuteResult(true, sanitize(message)); + } + + /** + * Create a failed result with the provided message. + * + * @param message failure description, sanitized to non-null + * @return failure result + */ + public static ExecuteResult fail(String message) { + return new ExecuteResult(false, sanitize(message)); + } + + /** + * Whether the operation completed successfully. + * + * @return {@code true} when successful + */ + public boolean isSuccess() { + return isSuccess; + } + + /** + * Human-readable message describing the outcome. + * + * @return outcome message, never {@code null} + */ + public String getMessage() { + return message; + } + + /** + * Create a new result with the same success flag but a different message. + * + * @param newMessage updated message + * @return new result instance + */ + public ExecuteResult withMessage(String newMessage) { + return new ExecuteResult(isSuccess, sanitize(newMessage)); + } + + private static String sanitize(String message) { + return Objects.requireNonNullElse(message, ""); + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ExecuteSQLRequestDTO.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ExecuteSQLRequestDTO.java new file mode 100644 index 000000000..d2d87eee2 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ExecuteSQLRequestDTO.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.dto; + +import lombok.Data; + +import org.opengauss.admin.plugin.enums.DatabaseDialect; + +/** + * Request payload for executing SQL generated from DBML against a target instance. + * + * @version 1.0.0 + * @since 2025-10-15 + */ +@Data +public class ExecuteSQLRequestDTO { + // Target instance identifiers + private String clusterId; + private String nodeId; + + // Target dialect and schema + private DatabaseDialect dialect; + private String targetSchema; + + // Raw DBML payload + private String dbmlText; +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ExportRequestDTO.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ExportRequestDTO.java new file mode 100644 index 000000000..f93e34049 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ExportRequestDTO.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.dto; + +import lombok.Data; + +import org.opengauss.admin.plugin.enums.DatabaseDialect; + +import java.util.ArrayList; +import java.util.List; + +/** + * Request payload describing which tables to export and the target SQL dialect. + * + * @version 1.0.0 + * @since 2025-08-30 + */ +@Data +public class ExportRequestDTO { + /** Target dialect (openGauss / mysql / postgre / mssql). */ + private DatabaseDialect dialect; + + /** Table definitions including columns, types, primary keys, and foreign keys. */ + private List tables = new ArrayList<>(); +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ForeignKeyDTO.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ForeignKeyDTO.java new file mode 100644 index 000000000..3c5a8c2c1 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ForeignKeyDTO.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.dto; + +import lombok.Data; + +import java.util.List; + +/** + * Data transfer object describing a foreign key relationship. + * Contains local column names, referenced table and columns, and optional + * referential actions for update/delete. + * + * @version 1.0.0 + * @since 2025-10-15 + */ +@Data +public class ForeignKeyDTO { + private List columnNames; + private List referencedColumnNames; + private String referencedTable; + private String onUpdateAction; + private String onDeleteAction; +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/IndexDTO.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/IndexDTO.java new file mode 100644 index 000000000..1195ead7c --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/IndexDTO.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.dto; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Data transfer object describing a database index and its attributes. + * + * @version 1.0.0 + * @since 2025-08-25 + */ +@Data +public class IndexDTO { + private String indexName; // Index name + private List columnNames = new ArrayList<>(); // Column list (supports composite indexes) + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private Boolean isUnique; // Whether the index is unique + private String indexType; // Index type, e.g. btree or hash (openGauss supports btree, gist, etc.) + private String comment; // Index comment + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private Boolean isPrimaryKey; // Whether the index represents a primary key + + public Boolean getUnique() { + return isUnique; + } + + public void setUnique(Boolean isUnique) { + this.isUnique = isUnique; + } + + public Boolean getPk() { + return isPrimaryKey; + } + + public void setPk(Boolean isPrimaryKey) { + this.isPrimaryKey = isPrimaryKey; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ManagedInstanceDTO.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ManagedInstanceDTO.java new file mode 100644 index 000000000..ad62dbe67 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/ManagedInstanceDTO.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * DTO that describes an instance managed by the ER-Model plugin, including cluster/node identifiers and display info. + * + * @since 2025-10-19 + */ +@Data +@AllArgsConstructor // Lombok generates a constructor with all fields +public class ManagedInstanceDTO { + private String clusterId; + private String nodeId; + private String displayName; // Friendly name for UI display + private String dbType; // Database type, e.g., "openGauss", "MySQL" + private String databaseName; +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/SqlToDbmlRequest.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/SqlToDbmlRequest.java new file mode 100644 index 000000000..3217f7b39 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/SqlToDbmlRequest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.dto; + +import lombok.Data; + +/** + * Request payload for importing `.sql` content into DBML. + * + * @version 1.0.0 + * @since 2025-09-01 + */ +@Data +public class SqlToDbmlRequest { + /** Dialect: POSTGRESQL | MYSQL | OPENGAUSS | AUTO (detected server-side). */ + private String dialect; + + /** Raw SQL (may include DML; backend filters to retain only DDL). */ + private String sqlText; + + /** Skip data during import (defaults to true). */ + private Boolean shouldSkipData; + + /** + * Compatibility alias for older clients. + * + * @return value of {@link #shouldSkipData} + */ + public Boolean getSkipData() { + return shouldSkipData; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/TableDTO.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/TableDTO.java new file mode 100644 index 000000000..3f9b8ef4f --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/TableDTO.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.dto; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * Data transfer object describing a database table along with its metadata. + * + * @version 1.0.0 + * @since 2025-08-25 + */ +@Data +public class TableDTO { + private String tableName; // Table name + private String comment; // Table comment + private List columns = new ArrayList<>(); // Column metadata + private List> primaryKeys = new ArrayList<>(); // Supports composite primary keys + private List indexes = new ArrayList<>(); // Index metadata + private List foreignKeys = new ArrayList<>(); // Foreign key definitions + + /** + * Default constructor. Now, whenever you call `new TableDTO()`, + * the lists above will be ready to use immediately. + */ + public TableDTO() { + } + + /** + * Convenience constructor that also benefits from the initialized lists. + * + * @param tableName The name of the table. + */ + public TableDTO(String tableName) { + this.tableName = tableName; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/TestConnectionRequest.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/TestConnectionRequest.java new file mode 100644 index 000000000..bfa734be9 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/dto/TestConnectionRequest.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.dto; + +import lombok.Data; + +/** + * Request payload for testing connectivity to a managed cluster node. + * + * @version 1.0.0 + * @since 2025-09-01 + */ +@Data +public class TestConnectionRequest { + private String clusterId; + private String nodeId; +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/enums/DatabaseDialect.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/enums/DatabaseDialect.java new file mode 100644 index 000000000..ffc6eb682 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/enums/DatabaseDialect.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.enums; + +import lombok.Getter; + +import java.util.Arrays; + +/** + * Supported database dialects used when generating or importing SQL. + * + * @version 1.0.0 + * @since 2025-08-20 + */ +@Getter +public enum DatabaseDialect { + // Supported database dialects + MYSQL("mysql"), + POSTGRESQL("postgresql"), + MSSQL("mssql"), + OPENGAUSS("opengauss"); + + private final String dialectName; + + /** + * Constructor. + * + * @param dialectName lowercase representation of the dialect + */ + DatabaseDialect(String dialectName) { + this.dialectName = dialectName; + } + + /** + * Convert a string value into the corresponding enum constant. + * This lookup is case-insensitive. + * + * @param text string representation of the dialect + * @return matching {@link DatabaseDialect} constant + * @throws IllegalArgumentException if no dialect matches the input + */ + public static DatabaseDialect fromString(String text) { + if (text == null || text.isBlank()) { + throw new IllegalArgumentException("数据库方言字符串不能为空。"); + } + return Arrays.stream(values()) + .filter(dialect -> dialect.dialectName.equalsIgnoreCase(text)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("不支持的数据库方言:" + text)); + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/enums/DbDataLocationEnum.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/enums/DbDataLocationEnum.java new file mode 100644 index 000000000..5b37368e1 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/enums/DbDataLocationEnum.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) GBA-NCTI-ISDC. 2022-2024. + * + * openGauss DataKit is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * ------------------------------------------------------------------------- + * + * DbDataLocationEnum.java + * + * IDENTIFICATION + * plugins/er-model/src/main/java/org/opengauss/admin/plugin/enums/DbDataLocationEnum.java + * + * ------------------------------------------------------------------------- + */ + +package org.opengauss.admin.plugin.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +/** + * DbDataLocationEnum + * + * @since 2024/1/15 + */ +@AllArgsConstructor +@Getter +public enum DbDataLocationEnum { + INTARKDB("org.intarkdb.Driver", Arrays.asList("classpath:intarkdb-schema.sql")), + OPENGAUSS("org.opengauss.Driver", Arrays.asList("classpath:opengauss-schema.sql")); + + private String driverClass; + private List locations; + + /** + * get DbDataLocationEnum by driverClass + * + * @param driverClass String + * @return Optional + */ + public static Optional of(String driverClass) { + if (StrUtil.isEmpty(driverClass)) { + return Optional.empty(); + } + for (DbDataLocationEnum enumConstant : DbDataLocationEnum.class.getEnumConstants()) { + if (enumConstant.getDriverClass().equalsIgnoreCase(driverClass)) { + return Optional.of(enumConstant); + } + } + return Optional.empty(); + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/exception/DbmlProcessingException.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/exception/DbmlProcessingException.java new file mode 100644 index 000000000..dae2d94ac --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/exception/DbmlProcessingException.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.exception; + +/** + * Exception thrown when DBML parsing or conversion fails inside the ER model plugin. + * + * @since 2025-10-18 + */ +public class DbmlProcessingException extends RuntimeException { + public DbmlProcessingException(String message) { + super(message); + } + + public DbmlProcessingException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/listener/ERModelPluginListener.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/listener/ERModelPluginListener.java new file mode 100644 index 000000000..610864cd5 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/listener/ERModelPluginListener.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.listener; + +import com.gitee.starblues.spring.MainApplicationContext; +import com.gitee.starblues.spring.SpringBeanFactory; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.vo.MenuVo; +import org.opengauss.admin.system.plugin.facade.MenuFacade; +import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.stereotype.Component; + +/** + * Listener reacting to lifecycle events to manage ER model plugin resources and menu entries. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +@Slf4j +@Component +public class ERModelPluginListener implements ApplicationListener { + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ApplicationEnvironmentPreparedEvent) { + log.info("ERModel init env"); + } else if (event instanceof ApplicationPreparedEvent) { + log.info("ERModel init complete"); + } else if (event instanceof ContextRefreshedEvent) { + log.info("ERModel was refreshed"); + } else if (event instanceof ApplicationReadyEvent) { + MainApplicationContext context = ((ApplicationReadyEvent) event) + .getApplicationContext() + .getBean(MainApplicationContext.class); + SpringBeanFactory factory = context.getSpringBeanFactory(); + MenuFacade menuFacade = factory.getBean(MenuFacade.class); + if (menuFacade != null) { + String pluginId = "er-model"; + // 重新注册菜单前,先清理旧记录,避免多语言切换产生重复 + menuFacade.deletePluginMenu(pluginId); + // Root menu path is an empty string ""; final route is /static-plugin/er-model/ + MenuVo parentMenu = menuFacade.savePluginMenu(pluginId, "ER建模", + "ER diagram modeling", 1, "er-tool"); + + // Child menu path editor -> /static-plugin/er-model/editor + menuFacade.savePluginMenu(pluginId, "设计ER图", "Design ER diagram", + 1, "editor", parentMenu.getMenuId()); + + // Child menu path erShow -> /static-plugin/er-model/erShow + menuFacade.savePluginMenu(pluginId, "展示数据库ER图", "Show ER diagram", + 2, "show-diagram", parentMenu.getMenuId()); + } + log.info("ERModel start complete"); + } else if (event instanceof ContextClosedEvent) { + MainApplicationContext context = ((ContextClosedEvent) event) + .getApplicationContext() + .getBean(MainApplicationContext.class); + SpringBeanFactory factory = context.getSpringBeanFactory(); + MenuFacade menuFacade = factory.getBean(MenuFacade.class); + if (menuFacade != null) { + menuFacade.deletePluginMenu("er-model"); + } + log.info("ERModel is stopped"); + } else { + log.debug("Unhandled plugin event type: {}", event.getClass().getName()); + } + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/DataSourceService.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/DataSourceService.java new file mode 100644 index 000000000..445c7a5bf --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/DataSourceService.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.service; + +import org.opengauss.admin.plugin.dto.ManagedInstanceDTO; +import org.opengauss.admin.plugin.enums.DatabaseDialect; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import java.util.Optional; + +/** + * Contract for obtaining database connections and metadata from the managed platform. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +public interface DataSourceService { + /** + * Obtain a database connection from the base platform using cluster and node identifiers. + * + * @param clusterId cluster identifier + * @param nodeId node identifier + * @return ready-to-use {@link Connection} + * @throws SQLException when database access fails or the instance cannot be located + * @throws ClassNotFoundException when the required JDBC driver is missing + */ + Connection getConnection(String clusterId, String nodeId) throws SQLException, ClassNotFoundException; + + /** + * Obtain a connection scoped to the given schema. + * + * @param clusterId cluster identifier + * @param nodeId node identifier + * @param schemaName schema to target + * @return ready-to-use {@link Connection} + * @throws SQLException when database access fails or the instance cannot be located + * @throws ClassNotFoundException when the required JDBC driver is missing + */ + Connection getConnection(String clusterId, String nodeId, String schemaName) + throws SQLException, ClassNotFoundException; + + /** + * Obtain a connection scoped to the given database. + * + * @param clusterId cluster identifier + * @param nodeId node identifier + * @param databaseName database to target + * @return ready-to-use {@link Connection} + * @throws SQLException when database access fails or the instance cannot be located + * @throws ClassNotFoundException when the required JDBC driver is missing + */ + Connection getDatabaseConnection(String clusterId, String nodeId, String databaseName) + throws SQLException, ClassNotFoundException; + + /** + * List every database instance that the base platform manages. + * + * @return managed instance metadata + */ + List listManagedInstances(); + + /** + * List schemas available for the specified cluster and node. + * + * @param clusterId cluster identifier + * @param nodeId node identifier + * @param databaseName database name to query + * @return schema names + * @throws SQLException when database access fails + * @throws ClassNotFoundException when the JDBC driver is not present + */ + List listSchemas(String clusterId, String nodeId, String databaseName) + throws SQLException, ClassNotFoundException; + + /** + * Create a schema on the specified database if it does not already exist. + * + * @param clusterId cluster identifier + * @param nodeId node identifier + * @param databaseName database within the instance + * @param schemaName schema name to create + * @throws SQLException when database access fails + * @throws ClassNotFoundException when the JDBC driver is not present + */ + void createSchemaIfAbsent(String clusterId, String nodeId, String databaseName, String schemaName) + throws SQLException, ClassNotFoundException; + + /** + * Resolve the dialect associated with a managed cluster node. + * + * @param clusterId cluster identifier + * @param nodeId node identifier + * @return {@link Optional} containing the dialect when available + */ + Optional getDialectForInstance(String clusterId, String nodeId); +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/DbmlExportService.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/DbmlExportService.java new file mode 100644 index 000000000..517a46b82 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/DbmlExportService.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.service; + +import org.opengauss.admin.plugin.enums.DatabaseDialect; + +/** + * Service contract for converting DBML inputs into SQL or PlantUML outputs. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +public interface DbmlExportService { + /** + * Generate SQL for the given DBML text and target dialect. + * + * @param dbmlText DBML definition payload + * @param dialect target database dialect (e.g., "mysql", "postgresql", "opengauss") + * @return SQL script generated from the DBML + */ + String generateSqlFromDbml(String dbmlText, DatabaseDialect dialect); + + /** + * Export PlantUML markup directly from DBML content. + * + * @param dbmlText DBML definition payload + * @return PlantUML text built from the DBML + */ + String generatePlantUmlFromDbml(String dbmlText); +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/ExportService.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/ExportService.java new file mode 100644 index 000000000..ea93c08fc --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/ExportService.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.service; + +import org.opengauss.admin.plugin.dto.ExportRequestDTO; + +import java.io.File; +import java.io.IOException; + +/** + * Service contract for exporting database structures to SQL files. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +public interface ExportService { + /** + * Generate an SQL file based on the provided export request. + * + * @param dto export configuration containing target dialect and tables + * @return generated SQL file + * @throws IOException when file generation fails + */ + File generateSqlFile(ExportRequestDTO dto) throws IOException; +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/SchemaIntrospectionService.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/SchemaIntrospectionService.java new file mode 100644 index 000000000..810aa58df --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/SchemaIntrospectionService.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.service; + +import org.opengauss.admin.plugin.dto.TableDTO; + +import java.sql.SQLException; +import java.util.List; + +/** + * Service interface for database schema introspection. + * Responsible for reverse-engineering table definitions from a given database and schema. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +public interface SchemaIntrospectionService { + /** + * Fetch complete table metadata for the specified database and schema. + * + * @param clusterId cluster identifier + * @param nodeId node identifier + * @param databaseName target database name + * @param schemaName schema name + * @return list of {@link TableDTO} containing table metadata + * @throws SQLException when database access fails + * @throws ClassNotFoundException when the JDBC driver cannot be located + */ + List getSchemaAsDTOs(String clusterId, String nodeId, + String databaseName, String schemaName) + throws SQLException, ClassNotFoundException; + + /** + * Convenience helper that exports PlantUML directly from the live database schema. + * + * @param clusterId cluster identifier + * @param nodeId node identifier + * @param databaseName target database name + * @param schemaName schema name + * @return generated PlantUML markup for the schema + * @throws SQLException when database access fails + * @throws ClassNotFoundException when the JDBC driver cannot be located + */ + default String generatePlantUmlFromDb(String clusterId, String nodeId, + String databaseName, String schemaName) + throws SQLException, ClassNotFoundException { + List tables = getSchemaAsDTOs(clusterId, nodeId, databaseName, schemaName); + return org.opengauss.admin.plugin.util.PlantUMLGenerator.generate(tables); + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/SqlExecutionService.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/SqlExecutionService.java new file mode 100644 index 000000000..bacdeb4f4 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/SqlExecutionService.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.service; + +import java.sql.SQLException; + +/** + * Service interface that executes SQL on managed database instances. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +public interface SqlExecutionService { + /** + * Execute SQL on a database instance managed by the platform. + * + * @param clusterId target cluster identifier + * @param nodeId target node identifier + * @param sql SQL text to execute + * @throws SQLException when database access fails + * @throws ClassNotFoundException when the JDBC driver is not available + */ + void executeSql(String clusterId, String nodeId, String sql) throws SQLException, ClassNotFoundException; +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/SqlImportService.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/SqlImportService.java new file mode 100644 index 000000000..90aaa3fdf --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/SqlImportService.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.service; + +import org.opengauss.admin.plugin.enums.DatabaseDialect; + +/** + * Service responsible for converting SQL text into DBML by parsing DDL and intermediate DTOs. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +public interface SqlImportService { + /** + * Convert SQL text into DBML following the pipeline: SQL → cleaned DDL → DTO → DBML. + * + * @param sqlText raw SQL provided by the client + * @param dialect target database dialect for parsing + * @param shouldSkipData whether INSERT data statements should be ignored + * @return generated DBML content + */ + String sqlToDbml(String sqlText, DatabaseDialect dialect, boolean shouldSkipData); +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/DataSourceServiceImpl.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/DataSourceServiceImpl.java new file mode 100644 index 000000000..9ccd7d08b --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/DataSourceServiceImpl.java @@ -0,0 +1,515 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.service.impl; + +import com.gitee.starblues.bootstrap.annotation.AutowiredType; +import jakarta.annotation.Resource; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterNodeVO; +import org.opengauss.admin.common.core.domain.model.ops.OpsClusterVO; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterNodeVO; +import org.opengauss.admin.common.core.domain.model.ops.jdbc.JdbcDbClusterVO; +import org.opengauss.admin.plugin.domain.ConnectionRequest; +import org.opengauss.admin.plugin.domain.JdbcNodeRef; +import org.opengauss.admin.plugin.dto.ManagedInstanceDTO; +import org.opengauss.admin.plugin.enums.DatabaseDialect; +import org.opengauss.admin.plugin.service.DataSourceService; +import org.opengauss.admin.system.plugin.facade.JdbcDbClusterFacade; +import org.opengauss.admin.system.plugin.facade.OpsFacade; +import org.opengauss.admin.system.service.ops.impl.EncryptionUtils; +import org.springframework.stereotype.Service; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Set; +import java.util.ArrayList; + +/** + * Retrieves connection information for managed database instances and exposes utility operations. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +@Slf4j +@Service +public class DataSourceServiceImpl implements DataSourceService { + private static final String DEFAULT_SCHEMA = "public"; + private static final Set MYSQL_SYSTEM_DATABASES = Set.of( + "information_schema", "mysql", "performance_schema", "sys"); + + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private OpsFacade opsFacade; + + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private JdbcDbClusterFacade jdbcDbClusterFacade; + + @Resource + @AutowiredType(AutowiredType.Type.PLUGIN_MAIN) + private EncryptionUtils encryptionUtils; + + @Override + public Connection getConnection(String clusterId, String nodeId) + throws SQLException, ClassNotFoundException { + return getConnection(clusterId, nodeId, null); + } + + @Override + public Connection getConnection(String clusterId, String nodeId, String schemaName) + throws SQLException, ClassNotFoundException { + ConnectionRequest request = findConnectionRequest(clusterId, nodeId) + .map(details -> details.withSchema(schemaName)) + .orElseThrow(() -> new SQLException(String.format(Locale.ROOT, + "Unable to locate database instance for clusterId='%s', nodeId='%s'.", + clusterId, nodeId))); + return openConnection(request); + } + + @Override + public Connection getDatabaseConnection(String clusterId, String nodeId, String databaseName) + throws SQLException, ClassNotFoundException { + ConnectionRequest request = findConnectionRequest(clusterId, nodeId) + .map(details -> details.withDatabase(databaseName).withSchema(DEFAULT_SCHEMA)) + .orElseThrow(() -> new SQLException(String.format(Locale.ROOT, + "Unable to locate database instance for clusterId='%s', nodeId='%s'.", + clusterId, nodeId))); + return openConnection(request); + } + + @Override + public List listManagedInstances() { + List instances = new ArrayList<>(); + instances.addAll(buildManagedInstancesFromOps()); + instances.addAll(buildManagedInstancesFromJdbc()); + return instances; + } + + @Override + public List listSchemas(String clusterId, String nodeId, String databaseName) + throws SQLException, ClassNotFoundException { + DatabaseDialect dialect = getDialectForInstance(clusterId, nodeId) + .orElseThrow(() -> new SQLException(String.format(Locale.ROOT, + "Unable to determine dialect for clusterId='%s', nodeId='%s'.", + clusterId, nodeId))); + String query = buildSchemaQuery(dialect); + if (query == null) { + log.warn("Schema listing is not implemented for dialect '{}'.", dialect.name()); + return Collections.emptyList(); + } + try (Connection connection = getDatabaseConnection(clusterId, nodeId, databaseName); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(query)) { + List schemas = new ArrayList<>(); + while (resultSet.next()) { + schemas.add(resultSet.getString(1)); + } + if (dialect == DatabaseDialect.MYSQL) { + schemas.removeIf(MYSQL_SYSTEM_DATABASES::contains); + } + if ((dialect == DatabaseDialect.POSTGRESQL || dialect == DatabaseDialect.OPENGAUSS) + && schemas.isEmpty()) { + schemas.add(DEFAULT_SCHEMA); + } + return schemas; + } + } + + @Override + public void createSchemaIfAbsent(String clusterId, String nodeId, String databaseName, String schemaName) + throws SQLException, ClassNotFoundException { + String normalized = normalizeSchemaName(schemaName); + if (normalized == null) { + throw new IllegalArgumentException("Schema name must not be empty."); + } + if (!normalized.matches("[A-Za-z_][A-Za-z0-9_\\$]*")) { + throw new IllegalArgumentException("Schema name must start with a letter or underscore and contain only alphanumeric characters, underscores, or '$'."); + } + DatabaseDialect dialect = getDialectForInstance(clusterId, nodeId) + .orElseThrow(() -> new SQLException(String.format(Locale.ROOT, + "Unable to determine dialect for clusterId='%s', nodeId='%s'.", + clusterId, nodeId))); + String sql = buildCreateSchemaSql(dialect, normalized); + if (sql == null || sql.isBlank()) { + throw new SQLException(String.format(Locale.ROOT, + "Schema creation is not supported for dialect '%s'.", dialect.name())); + } + try (Connection connection = getDatabaseConnection(clusterId, nodeId, databaseName); + Statement statement = connection.createStatement()) { + log.info("Creating schema if absent: clusterId='{}', nodeId='{}', database='{}', schema='{}'", + clusterId, nodeId, databaseName, normalized); + statement.execute(sql); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Optional getDialectForInstance(String clusterId, String nodeId) { + Optional opsDialect = locateOpsNode(clusterId, nodeId) + .map(node -> DatabaseDialect.OPENGAUSS); + if (opsDialect.isPresent()) { + return opsDialect; + } + + for (DatabaseDialect dialect : DatabaseDialect.values()) { + boolean isMatch = jdbcDbClusterFacade.listAll(dialect.getDialectName()).stream() + .filter(cluster -> matchesCluster(cluster, clusterId)) + .flatMap(cluster -> safeList(cluster.getNodes()).stream()) + .anyMatch(node -> nodeId.equals(node.getClusterNodeId())); + if (isMatch) { + return Optional.of(dialect); + } + } + return Optional.empty(); + } + + private Optional findConnectionRequest(String clusterId, String nodeId) { + Optional opsRequest = locateOpsNode(clusterId, nodeId) + .map(node -> ConnectionRequest.builder() + .dialect(DatabaseDialect.OPENGAUSS) + .host(node.getPublicIp()) + .port(node.getDbPort()) + .database(node.getDbName()) + .username(node.getDbUser()) + .password(encryptionUtils.decrypt(node.getDbUserPassword())) + .schema(DEFAULT_SCHEMA) + .build()); + if (opsRequest.isPresent()) { + return opsRequest; + } + + for (DatabaseDialect dialect : DatabaseDialect.values()) { + Optional jdbcMatch = locateJdbcNode(dialect, clusterId, nodeId) + .flatMap(node -> buildJdbcConnectionRequest(dialect, node)); + if (jdbcMatch.isPresent()) { + return jdbcMatch; + } + } + return Optional.empty(); + } + + private Optional locateOpsNode(String clusterId, String nodeId) { + return opsFacade.listCluster().stream() + .filter(cluster -> clusterId.equals(cluster.getClusterId())) + .map(OpsClusterVO::getClusterNodes) + .filter(nodes -> nodes != null) + .flatMap(Collection::stream) + .filter(node -> nodeId.equals(node.getNodeId())) + .findFirst(); + } + + private Optional locateJdbcNode(DatabaseDialect dialect, String clusterId, String nodeId) { + return jdbcDbClusterFacade.listAll(dialect.getDialectName()).stream() + .filter(cluster -> matchesCluster(cluster, clusterId)) + .flatMap(cluster -> safeList(cluster.getNodes()).stream() + .filter(node -> nodeId.equals(node.getClusterNodeId())) + .map(node -> new JdbcNodeRef(cluster, node))) + .findFirst(); + } + + private boolean matchesCluster(JdbcDbClusterVO cluster, String clusterId) { + String identifier = cluster.getClusterId() != null ? cluster.getClusterId() : cluster.getName(); + return clusterId.equals(identifier) || clusterId.equals(cluster.getName()); + } + + private Optional buildJdbcConnectionRequest(DatabaseDialect dialect, JdbcNodeRef ref) { + JdbcDbClusterNodeVO node = ref.node(); + String rawPort = node.getPort(); + if (rawPort == null || rawPort.isBlank()) { + log.warn("Port is missing for JDBC node '{}' in cluster '{}'.", + node.getClusterNodeId(), ref.cluster().getName()); + return Optional.empty(); + } + int port; + try { + port = Integer.parseInt(rawPort); + } catch (NumberFormatException ex) { + log.warn("Port '{}' is not numeric for JDBC node '{}' in cluster '{}'.", + rawPort, node.getClusterNodeId(), ref.cluster().getName(), ex); + return Optional.empty(); + } + String database = extractDatabaseName(node.getUrl()).orElse(DEFAULT_SCHEMA); + String decryptedPassword = encryptionUtils.decrypt(node.getPassword()); + return Optional.of(ConnectionRequest.builder() + .dialect(dialect) + .host(node.getIp()) + .port(port) + .database(database) + .username(node.getUsername()) + .password(decryptedPassword) + .schema(DEFAULT_SCHEMA) + .build()); + } + + private Connection openConnection(ConnectionRequest request) + throws SQLException, ClassNotFoundException { + String driverClass = getDriverClassName(request.dialect()); + String jdbcUrl = buildJdbcUrl(request); + log.debug("Opening JDBC connection using URL {}", jdbcUrl); + Class.forName(driverClass); + return DriverManager.getConnection(jdbcUrl, request.username(), request.password()); + } + + private String getDriverClassName(DatabaseDialect dialect) { + return switch (dialect) { + case OPENGAUSS, POSTGRESQL -> "org.postgresql.Driver"; + case MYSQL -> "com.mysql.cj.jdbc.Driver"; + case MSSQL -> "com.microsoft.sqlserver.jdbc.SQLServerDriver"; + }; + } + + private String buildJdbcUrl(ConnectionRequest request) { + String schema = Optional.ofNullable(request.schema()) + .filter(s -> !s.isBlank()) + .orElse(DEFAULT_SCHEMA); + return switch (request.dialect()) { + case OPENGAUSS, POSTGRESQL -> String.format(Locale.ROOT, + "jdbc:postgresql://%s:%d/%s?currentSchema=%s", + request.host(), request.port(), request.database(), schema); + case MYSQL -> String.format(Locale.ROOT, + "jdbc:mysql://%s:%d/%s?useSSL=false&serverTimezone=UTC", + request.host(), request.port(), request.database()); + case MSSQL -> String.format(Locale.ROOT, + "jdbc:sqlserver://%s:%d;databaseName=%s", + request.host(), request.port(), request.database()); + }; + } + + private List buildManagedInstancesFromOps() { + List instances = new ArrayList<>(); + for (OpsClusterVO cluster : safeList(opsFacade.listCluster())) { + for (OpsClusterNodeVO node : safeList(cluster.getClusterNodes())) { + appendOpsInstances(instances, cluster, node); + } + } + return instances; + } + + private List buildManagedInstancesFromJdbc() { + List instances = new ArrayList<>(); + for (DatabaseDialect dialect : DatabaseDialect.values()) { + List clusters = jdbcDbClusterFacade.listAll(dialect.getDialectName()); + for (JdbcDbClusterVO cluster : safeList(clusters)) { + String identifier = cluster.getClusterId() != null ? cluster.getClusterId() : cluster.getName(); + for (JdbcDbClusterNodeVO node : safeList(cluster.getNodes())) { + appendJdbcInstances(instances, dialect, cluster, identifier, node); + } + } + } + return instances; + } + + private void appendOpsInstances(List instances, OpsClusterVO cluster, OpsClusterNodeVO node) { + List databases = listDatabasesForNode(cluster.getClusterId(), node.getNodeId()); + if (databases.isEmpty()) { + String displayName = String.format( + Locale.ROOT, + "%s - %s (%s) [node]", + cluster.getClusterName(), + node.getHostname(), + node.getPublicIp()); + instances.add(new ManagedInstanceDTO( + cluster.getClusterId(), + node.getNodeId(), + displayName, + DatabaseDialect.OPENGAUSS.getDialectName(), + node.getDbName())); + return; + } + for (String database : databases) { + String displayName = String.format( + Locale.ROOT, + "%s - %s / %s", + cluster.getClusterName(), + node.getHostname(), + database); + instances.add(new ManagedInstanceDTO( + cluster.getClusterId(), + node.getNodeId(), + displayName, + DatabaseDialect.OPENGAUSS.getDialectName(), + database)); + } + } + + private void appendJdbcInstances( + List instances, + DatabaseDialect dialect, + JdbcDbClusterVO cluster, + String identifier, + JdbcDbClusterNodeVO node) { + List databases = listDatabasesForNode(identifier, node.getClusterNodeId()); + if (databases.isEmpty()) { + String displayName = String.format(Locale.ROOT, "%s (%s) [node]", cluster.getName(), node.getIp()); + String database = extractDatabaseName(node.getUrl()).orElse(DEFAULT_SCHEMA); + instances.add(new ManagedInstanceDTO( + identifier, + node.getClusterNodeId(), + displayName, + dialect.getDialectName(), + database)); + return; + } + for (String database : databases) { + String displayName = String.format( + Locale.ROOT, + "%s (%s) / %s", + cluster.getName(), + node.getIp(), + database); + instances.add(new ManagedInstanceDTO( + identifier, + node.getClusterNodeId(), + displayName, + dialect.getDialectName(), + database)); + } + } + + private List listDatabasesForNode(String clusterId, String nodeId) { + Optional dialectOpt = getDialectForInstance(clusterId, nodeId); + if (dialectOpt.isEmpty()) { + log.warn("Unable to determine dialect for clusterId='{}', nodeId='{}'.", clusterId, nodeId); + return Collections.emptyList(); + } + DatabaseDialect dialect = dialectOpt.get(); + String query = buildDatabaseQuery(dialect); + if (query == null) { + log.warn("Database listing is not implemented for dialect '{}'.", dialect.name()); + return Collections.emptyList(); + } + try (Connection connection = getConnection(clusterId, nodeId); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(query)) { + List databases = new ArrayList<>(); + while (resultSet.next()) { + databases.add(resultSet.getString(1)); + } + if (dialect == DatabaseDialect.MYSQL) { + databases.removeIf(MYSQL_SYSTEM_DATABASES::contains); + } + getDefaultDatabaseForNode(clusterId, nodeId) + .filter(db -> !databases.contains(db)) + .ifPresent(db -> databases.add(0, db)); + return databases; + } catch (SQLException | ClassNotFoundException ex) { + log.warn("Failed to obtain database list for clusterId='{}', nodeId='{}'.", clusterId, nodeId, ex); + return Collections.emptyList(); + } + } + + private String buildDatabaseQuery(DatabaseDialect dialect) { + return switch (dialect) { + case MYSQL -> "SHOW DATABASES;"; + case POSTGRESQL, OPENGAUSS -> + "SELECT datname FROM pg_database WHERE datistemplate = false AND datname <> 'postgres';"; + case MSSQL -> "SELECT name FROM sys.databases;"; + }; + } + + private String buildSchemaQuery(DatabaseDialect dialect) { + return switch (dialect) { + case MYSQL -> "SHOW DATABASES;"; + case POSTGRESQL, OPENGAUSS -> + "SELECT nspname FROM pg_namespace " + + "WHERE nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') " + + "AND nspname NOT LIKE 'pg_temp_%' AND nspname NOT LIKE 'pg_toast_temp_%' " + + "ORDER BY nspname;"; + case MSSQL -> "SELECT name FROM sys.schemas WHERE principal_id = 1;"; + }; + } + + private Optional getDefaultDatabaseForNode(String clusterId, String nodeId) { + Optional opsDefault = locateOpsNode(clusterId, nodeId) + .map(OpsClusterNodeVO::getDbName); + if (opsDefault.isPresent()) { + return opsDefault; + } + for (DatabaseDialect dialect : DatabaseDialect.values()) { + Optional jdbcDefault = locateJdbcNode(dialect, clusterId, nodeId) + .flatMap(ref -> extractDatabaseName(ref.node().getUrl())); + if (jdbcDefault.isPresent()) { + return jdbcDefault; + } + } + return Optional.empty(); + } + + private Optional extractDatabaseName(String url) { + if (url == null || url.isBlank()) { + return Optional.empty(); + } + int databaseNameMarker = url.indexOf(";databaseName="); + if (databaseNameMarker >= 0) { + int start = databaseNameMarker + 15; + if (start < url.length()) { + int end = url.indexOf(';', start); + String candidate = end == -1 ? url.substring(start) : url.substring(start, end); + return candidate.isBlank() ? Optional.empty() : Optional.of(candidate); + } + return Optional.empty(); + } + int slash = url.lastIndexOf('/'); + if (slash >= 0 && slash + 1 < url.length()) { + int queryIndex = url.indexOf('?', slash); + String candidate = queryIndex == -1 ? url.substring(slash + 1) : url.substring(slash + 1, queryIndex); + return candidate.isBlank() ? Optional.empty() : Optional.of(candidate); + } + return Optional.empty(); + } + + private String normalizeSchemaName(String schemaName) { + if (schemaName == null) { + return null; + } + String trimmed = schemaName.trim(); + return trimmed.isEmpty() ? null : trimmed; + } + + private String buildCreateSchemaSql(DatabaseDialect dialect, String schemaName) { + return switch (dialect) { + case POSTGRESQL, OPENGAUSS -> "CREATE SCHEMA IF NOT EXISTS " + quoteIdentifier(schemaName); + case MYSQL -> "CREATE SCHEMA IF NOT EXISTS `" + schemaName.replace("`", "``") + "`"; + case MSSQL -> """ + IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = '%s') + BEGIN + EXEC('CREATE SCHEMA [%s]'); + END + """.formatted(schemaName.replace("'", "''"), schemaName.replace("]", "]]")); + }; + } + + private String quoteIdentifier(String identifier) { + return "\"" + identifier.replace("\"", "\"\"") + "\""; + } + + private List safeList(List list) { + return list == null ? Collections.emptyList() : list; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/DbmlExportServiceImpl.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/DbmlExportServiceImpl.java new file mode 100644 index 000000000..d8d5a574e --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/DbmlExportServiceImpl.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.service.impl; + +import com.wn.dbml.compiler.DbmlParser; +import com.wn.dbml.compiler.ParsingException; +import com.wn.dbml.model.Database; + +import org.opengauss.admin.plugin.dto.TableDTO; +import org.opengauss.admin.plugin.enums.DatabaseDialect; +import org.opengauss.admin.plugin.exception.DbmlProcessingException; +import org.opengauss.admin.plugin.service.DbmlExportService; +import org.opengauss.admin.plugin.sql.router.SqlGeneratorRouter; +import org.opengauss.admin.plugin.translate.DbmlToDtoTranslator; +import org.opengauss.admin.plugin.util.PlantUMLGenerator; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * Service implementation that converts DBML inputs into SQL or PlantUML outputs. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +@Service +public class DbmlExportServiceImpl implements DbmlExportService { + @Override + public String generateSqlFromDbml(String dbmlText, DatabaseDialect dialect) { + if (dbmlText == null || dbmlText.isBlank()) { + throw new IllegalArgumentException("DBML content cannot be empty."); + } + + try { + // 1. Parse the DBML text via the dbml-java library + Database database = DbmlParser.parse(dbmlText); + + // 2. Map dbml-java models into the project's DTOs + List tableDTOs = DbmlToDtoTranslator.map(database); + + // 3. Use the existing generator to produce SQL + return SqlGeneratorRouter.generateSQL(tableDTOs, dialect); + } catch (ParsingException e) { + // Convert library-specific exceptions into a generic runtime failure + throw new DbmlProcessingException("Failed to parse DBML: " + e.getMessage(), e); + } + } + + @Override + public String generatePlantUmlFromDbml(String dbmlText) { + if (dbmlText == null || dbmlText.isBlank()) { + throw new IllegalArgumentException("DBML content cannot be empty."); + } + Database database = DbmlParser.parse(dbmlText); + List tableDTOs = DbmlToDtoTranslator.map(database); + return PlantUMLGenerator.generate(tableDTOs); + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/ExportServiceImpl.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/ExportServiceImpl.java new file mode 100644 index 000000000..f251c13b5 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/ExportServiceImpl.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.service.impl; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.plugin.dto.ExportRequestDTO; +import org.opengauss.admin.plugin.service.ExportService; +import org.opengauss.admin.plugin.sql.router.SqlGeneratorRouter; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +/** + * Default implementation for generating SQL exports based on table metadata. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +@Slf4j +@Service +public class ExportServiceImpl implements ExportService { + @Override + public File generateSqlFile(ExportRequestDTO dto) throws IOException { + String sql = SqlGeneratorRouter.generateSQL(dto.getTables(), dto.getDialect()); + File file = File.createTempFile("export-", ".sql"); + Files.write(file.toPath(), sql.getBytes(StandardCharsets.UTF_8)); + return file; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/SchemaIntrospectionServiceImpl.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/SchemaIntrospectionServiceImpl.java new file mode 100644 index 000000000..4baa1dc9b --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/SchemaIntrospectionServiceImpl.java @@ -0,0 +1,517 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.service.impl; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.plugin.dto.ColumnDTO; +import org.opengauss.admin.plugin.dto.ForeignKeyDTO; +import org.opengauss.admin.plugin.dto.IndexDTO; +import org.opengauss.admin.plugin.dto.TableDTO; +import org.opengauss.admin.plugin.enums.DatabaseDialect; +import org.opengauss.admin.plugin.service.DataSourceService; +import org.opengauss.admin.plugin.service.SchemaIntrospectionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.sql.Array; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Introspects database schemas across supported dialects and translates them into {@link TableDTO} structures. + * + * @since 2025-01-07 + */ +@Slf4j +@Service +public class SchemaIntrospectionServiceImpl implements SchemaIntrospectionService { + private final DataSourceService dataSourceService; + + @Autowired + public SchemaIntrospectionServiceImpl(DataSourceService dataSourceService) { + this.dataSourceService = dataSourceService; + } + + @Override + public List getSchemaAsDTOs( + String clusterId, + String nodeId, + String databaseName, + String schemaName) throws SQLException, ClassNotFoundException { + Optional dialectOpt = dataSourceService.getDialectForInstance(clusterId, nodeId); + DatabaseDialect dialect = dialectOpt.orElseThrow( + () -> new UnsupportedOperationException( + "无法确定实例的数据库方言: " + clusterId + "/" + nodeId)); + + try (Connection connection = dataSourceService.getDatabaseConnection(clusterId, nodeId, databaseName)) { + Map tablesByName = new HashMap<>(); + + switch (dialect) { + case POSTGRESQL: + case OPENGAUSS: + introspectPostgresSchema(connection, schemaName, tablesByName); + break; + case MYSQL: + introspectMySqlSchema(connection, databaseName, tablesByName); + break; + case MSSQL: + throw new UnsupportedOperationException("尚未实现对 MSSQL 的模式内省。"); + default: + throw new UnsupportedOperationException("不支持的数据库类型: " + dialect.name()); + } + + for (TableDTO table : tablesByName.values()) { + table.getColumns() + .sort(Comparator.comparing((ColumnDTO column) -> + Boolean.TRUE.equals(column.getPrimaryKey())).reversed()); + } + + return new ArrayList<>(tablesByName.values()); + } + } + + private void introspectPostgresSchema( + Connection conn, + String schema, + Map tablesMap) throws SQLException { + queryPgTablesAndColumns(conn, schema, tablesMap); + queryPgPrimaryKeysAndUniqueConstraints(conn, schema, tablesMap); + queryPgForeignKeys(conn, schema, tablesMap); + queryPgIndexes(conn, schema, tablesMap); + } + + private void introspectMySqlSchema( + Connection conn, + String databaseName, + Map tablesMap) throws SQLException { + queryMySqlTablesAndColumns(conn, databaseName, tablesMap); + queryMySqlPrimaryKeysAndUniqueConstraints(conn, databaseName, tablesMap); + queryMySqlForeignKeys(conn, databaseName, tablesMap); + queryMySqlIndexes(conn, databaseName, tablesMap); + } + + + // --- PostgreSQL / openGauss introspection --- + + private void queryPgTablesAndColumns( + Connection conn, + String schema, + Map tablesMap) throws SQLException { + String sql = """ + SELECT + t.relname AS table_name, + tbl_d.description AS table_comment, + a.attname AS column_name, + format_type(a.atttypid, a.atttypmod) AS data_type, + NOT a.attnotnull AS is_nullable, + pg_get_expr(ad.adbin, ad.adrelid) AS column_default, + col_d.description AS column_comment + FROM pg_class t + JOIN pg_namespace s ON t.relnamespace = s.oid + JOIN pg_attribute a ON a.attrelid = t.oid + LEFT JOIN pg_description tbl_d ON tbl_d.objoid = t.oid AND tbl_d.objsubid = 0 + LEFT JOIN pg_description col_d ON col_d.objoid = a.attrelid AND col_d.objsubid = a.attnum + LEFT JOIN pg_attrdef ad ON ad.adrelid = t.oid AND ad.adnum = a.attnum + WHERE t.relkind IN ('r', 'p') + AND a.attnum > 0 + AND NOT a.attisdropped + AND s.nspname = ? -- schema + """; + + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, schema); + try (ResultSet rs = pstmt.executeQuery()) { + while (rs.next()) { + String tableName = rs.getString("table_name"); + TableDTO table = tablesMap.computeIfAbsent(tableName, k -> new TableDTO()); + table.setTableName(tableName); + table.setComment(rs.getString("table_comment")); + ColumnDTO column = new ColumnDTO(); + column.setColumnName(rs.getString("column_name")); + column.setDataType(rs.getString("data_type")); + column.setNullable(rs.getBoolean("is_nullable")); + column.setDefaultValue(rs.getString("column_default")); + column.setComment(rs.getString("column_comment")); + + table.getColumns().add(column); + } + } + } + } + + private void queryPgPrimaryKeysAndUniqueConstraints( + Connection conn, + String schema, + Map tablesMap) throws SQLException { + String sql = """ + SELECT + t.relname AS table_name, + c.contype AS constraint_type, + ( + SELECT array_agg(a.attname) + FROM pg_attribute a + WHERE a.attrelid = t.oid AND a.attnum = ANY(c.conkey) + ) AS columns + FROM pg_constraint c + JOIN pg_class t ON c.conrelid = t.oid + JOIN pg_namespace s ON t.relnamespace = s.oid + WHERE c.contype IN ('p', 'u') + AND s.nspname = ? -- schema + """; + + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, schema); + try (ResultSet rs = pstmt.executeQuery()) { + while (rs.next()) { + String tableName = rs.getString("table_name"); + TableDTO table = tablesMap.get(tableName); + if (table == null) { + continue; + } + + String constraintType = rs.getString("constraint_type"); + Array columnsArray = rs.getArray("columns"); + if (columnsArray == null) { + continue; + } + String[] columns = (String[]) columnsArray.getArray(); + + for (String colName : columns) { + table.getColumns().stream() + .filter(c -> c.getColumnName().equals(colName)) + .findFirst() + .ifPresent(c -> { + if ("p".equals(constraintType)) { + c.setPrimaryKey(true); + } else if ("u".equals(constraintType)) { + c.setUnique(true); + } else { + log.debug( + "Unsupported constraint type {} on table {}", + constraintType, + tableName); + } + }); + } + } + } + } + } + + private void queryPgForeignKeys( + Connection conn, + String schema, + Map tablesMap) throws SQLException { + String sql = """ + SELECT + (SELECT c.relname FROM pg_class c WHERE c.oid = con.conrelid) AS source_table, + ( + SELECT array_agg(a.attname) + FROM pg_attribute a + WHERE a.attrelid = con.conrelid AND a.attnum = ANY(con.conkey) + ) AS source_columns, + (SELECT c.relname FROM pg_class c WHERE c.oid = con.confrelid) AS target_table, + ( + SELECT array_agg(a.attname) + FROM pg_attribute a + WHERE a.attrelid = con.confrelid AND a.attnum = ANY(con.confkey) + ) AS target_columns + FROM pg_constraint con + JOIN pg_namespace s ON con.connamespace = s.oid + WHERE con.contype = 'f' + AND s.nspname = ? -- schema + """; + + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, schema); + try (ResultSet rs = pstmt.executeQuery()) { + while (rs.next()) { + String sourceTable = rs.getString("source_table"); + TableDTO table = tablesMap.get(sourceTable); + if (table == null) { + continue; + } + + Array sourceColumnsArray = rs.getArray("source_columns"); + Array targetColumnsArray = rs.getArray("target_columns"); + if (sourceColumnsArray == null || targetColumnsArray == null) { + log.debug("Skipping FK on {} due to missing column metadata", sourceTable); + continue; + } + String[] srcCols = (String[]) sourceColumnsArray.getArray(); + String tgtTable = rs.getString("target_table"); + String[] tgtCols = (String[]) targetColumnsArray.getArray(); + + if (srcCols.length != tgtCols.length) { + log.debug("Skipping FK on {} due to column mismatch", sourceTable); + continue; + } + + ForeignKeyDTO fk = new ForeignKeyDTO(); + fk.setReferencedTable(tgtTable); + fk.setColumnNames(Arrays.asList(srcCols)); + fk.setReferencedColumnNames(Arrays.asList(tgtCols)); + table.getForeignKeys().add(fk); + } + } + } + } + + private void queryPgIndexes(Connection conn, String schema, Map tablesMap) throws SQLException { + String sql = """ + SELECT + t.relname AS table_name, + i.relname AS index_name, + a.attname AS column_name, + ix.indisunique AS is_unique + FROM pg_class t + JOIN pg_index ix ON t.oid = ix.indrelid + JOIN pg_class i ON i.oid = ix.indexrelid + JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey) + JOIN pg_namespace s ON t.relnamespace = s.oid + WHERE s.nspname = ? -- schema + AND ix.indisprimary = false + """; + + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, schema); + try (ResultSet rs = pstmt.executeQuery()) { + Map indexesMap = new HashMap<>(); + while (rs.next()) { + String tableName = rs.getString("table_name"); + TableDTO table = tablesMap.get(tableName); + if (table == null) { + continue; + } + + String indexName = rs.getString("index_name"); + boolean isUniqueIndex = rs.getBoolean("is_unique"); + String columnName = rs.getString("column_name"); + + IndexDTO index = indexesMap.computeIfAbsent(tableName + "." + indexName, key -> { + IndexDTO newIndex = new IndexDTO(); + newIndex.setIndexName(indexName); + newIndex.setUnique(isUniqueIndex); + table.getIndexes().add(newIndex); + return newIndex; + }); + index.getColumnNames().add(columnName); + } + } + } + } + + // --- MySQL introspection --- + + private void queryMySqlTablesAndColumns( + Connection conn, + String database, + Map tablesMap) throws SQLException { + String sql = """ + SELECT + t.TABLE_NAME, + t.TABLE_COMMENT, + c.COLUMN_NAME, + c.COLUMN_TYPE, + c.IS_NULLABLE, + c.COLUMN_DEFAULT, + c.COLUMN_COMMENT, + c.EXTRA + FROM information_schema.TABLES t + JOIN information_schema.COLUMNS c + ON t.TABLE_SCHEMA = c.TABLE_SCHEMA + AND t.TABLE_NAME = c.TABLE_NAME + WHERE t.TABLE_SCHEMA = ? -- database + AND t.TABLE_TYPE = 'BASE TABLE' + ORDER BY c.ORDINAL_POSITION + """; + + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, database); + try (ResultSet rs = pstmt.executeQuery()) { + while (rs.next()) { + String tableName = rs.getString("TABLE_NAME"); + TableDTO table = tablesMap.computeIfAbsent(tableName, k -> new TableDTO()); + table.setTableName(tableName); + table.setComment(rs.getString("TABLE_COMMENT")); + + ColumnDTO column = new ColumnDTO(); + column.setColumnName(rs.getString("COLUMN_NAME")); + column.setDataType(rs.getString("COLUMN_TYPE")); + column.setNullable("YES".equalsIgnoreCase(rs.getString("IS_NULLABLE"))); + column.setDefaultValue(rs.getString("COLUMN_DEFAULT")); + column.setComment(rs.getString("COLUMN_COMMENT")); + if ("auto_increment".equalsIgnoreCase(rs.getString("EXTRA"))) { + column.setAutoIncrement(true); + } + table.getColumns().add(column); + } + } + } + } + + private void queryMySqlPrimaryKeysAndUniqueConstraints( + Connection conn, + String database, + Map tablesMap) throws SQLException { + String sql = """ + SELECT + kcu.TABLE_NAME, + kcu.COLUMN_NAME, + tc.CONSTRAINT_TYPE + FROM information_schema.KEY_COLUMN_USAGE kcu + JOIN information_schema.TABLE_CONSTRAINTS tc + ON kcu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME + AND kcu.TABLE_SCHEMA = tc.TABLE_SCHEMA + AND kcu.TABLE_NAME = tc.TABLE_NAME + WHERE tc.TABLE_SCHEMA = ? -- database + AND tc.CONSTRAINT_TYPE IN ('PRIMARY KEY', 'UNIQUE') + """; + + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, database); + try (ResultSet rs = pstmt.executeQuery()) { + while (rs.next()) { + String tableName = rs.getString("TABLE_NAME"); + TableDTO table = tablesMap.get(tableName); + if (table == null) { + continue; + } + + String colName = rs.getString("COLUMN_NAME"); + String constraintType = rs.getString("CONSTRAINT_TYPE"); + + table.getColumns().stream() + .filter(c -> c.getColumnName().equals(colName)) + .findFirst().ifPresent(c -> { + if ("PRIMARY KEY".equalsIgnoreCase(constraintType)) { + c.setPrimaryKey(true); + } else if ("UNIQUE".equalsIgnoreCase(constraintType)) { + c.setUnique(true); + } else { + log.debug( + "Unhandled MySQL constraint type {} on table {}", + constraintType, + tableName); + } + }); + } + } + } + } + + private void queryMySqlForeignKeys( + Connection conn, + String database, + Map tablesMap) throws SQLException { + String sql = """ + SELECT + kcu.TABLE_NAME, + kcu.COLUMN_NAME, + kcu.REFERENCED_TABLE_NAME, + kcu.REFERENCED_COLUMN_NAME + FROM information_schema.KEY_COLUMN_USAGE kcu + WHERE kcu.TABLE_SCHEMA = ? -- database + AND kcu.REFERENCED_TABLE_NAME IS NOT NULL + """; + + Map cache = new HashMap<>(); + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, database); + try (ResultSet rs = pstmt.executeQuery()) { + while (rs.next()) { + String tableName = rs.getString("TABLE_NAME"); + TableDTO table = tablesMap.get(tableName); + if (table == null) { + continue; + } + + String referencedTable = rs.getString("REFERENCED_TABLE_NAME"); + + String key = tableName + "->" + referencedTable; + ForeignKeyDTO fk = cache.computeIfAbsent(key, cacheKey -> { + ForeignKeyDTO dto = new ForeignKeyDTO(); + dto.setReferencedTable(referencedTable); + table.getForeignKeys().add(dto); + return dto; + }); + + fk.getColumnNames().add(rs.getString("COLUMN_NAME")); + fk.getReferencedColumnNames().add(rs.getString("REFERENCED_COLUMN_NAME")); + } + } + } + } + + private void queryMySqlIndexes( + Connection conn, + String database, + Map tablesMap) throws SQLException { + String sql = """ + SELECT + TABLE_NAME, + INDEX_NAME, + COLUMN_NAME, + NON_UNIQUE + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = ? -- database + AND INDEX_NAME != 'PRIMARY' + ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX + """; + + try (PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, database); + try (ResultSet rs = pstmt.executeQuery()) { + Map indexesMap = new HashMap<>(); + while (rs.next()) { + String tableName = rs.getString("TABLE_NAME"); + String indexName = rs.getString("INDEX_NAME"); + String columnName = rs.getString("COLUMN_NAME"); + boolean isUniqueIndex = rs.getInt("NON_UNIQUE") == 0; + + TableDTO table = tablesMap.get(tableName); + if (table == null) { + continue; + } + + boolean isLikelyUniqueConstraintIndex = isUniqueIndex && indexName.equals(columnName); + if (isLikelyUniqueConstraintIndex) { + continue; + } + + IndexDTO index = indexesMap.computeIfAbsent(tableName + "." + indexName, key -> { + IndexDTO newIndex = new IndexDTO(); + newIndex.setIndexName(indexName); + newIndex.setUnique(isUniqueIndex); + table.getIndexes().add(newIndex); + return newIndex; + }); + index.getColumnNames().add(columnName); + } + } + } + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/SqlExecutionServiceImpl.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/SqlExecutionServiceImpl.java new file mode 100644 index 000000000..f55c81dd0 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/SqlExecutionServiceImpl.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.service.impl; + +import lombok.extern.slf4j.Slf4j; + +import org.opengauss.admin.plugin.service.DataSourceService; +import org.opengauss.admin.plugin.service.SqlExecutionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * Service that executes SQL scripts against managed cluster connections. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +@Slf4j +@Service +public class SqlExecutionServiceImpl implements SqlExecutionService { + private final DataSourceService dataSourceService; + + @Autowired + public SqlExecutionServiceImpl(DataSourceService dataSourceService) { + this.dataSourceService = dataSourceService; + } + + @Override + public void executeSql(String clusterId, String nodeId, String sqlScript) + throws SQLException, ClassNotFoundException { + // Use try-with-resources to ensure the connection closes automatically + try (Connection connection = dataSourceService.getConnection(clusterId, nodeId)) { + connection.setAutoCommit(false); + + try (Statement statement = connection.createStatement()) { + for (String raw : sqlScript.split(";")) { + String sql = raw.trim(); + if (sql.isEmpty()) { + continue; + } + log.debug("Executing SQL: [{}]", sql); + statement.execute(sql); + } + log.info("SQL script executed successfully and transaction committed."); + connection.commit(); + } catch (SQLException e) { + log.error("Error executing SQL script, rolling back transaction.", e); + connection.rollback(); + throw e; + } + } + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/SqlImportServiceImpl.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/SqlImportServiceImpl.java new file mode 100644 index 000000000..a2cea08c4 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/service/impl/SqlImportServiceImpl.java @@ -0,0 +1,740 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.service.impl; + +import lombok.extern.slf4j.Slf4j; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.Statements; +import net.sf.jsqlparser.statement.alter.Alter; +import net.sf.jsqlparser.statement.create.index.CreateIndex; +import net.sf.jsqlparser.statement.create.table.ColDataType; +import net.sf.jsqlparser.statement.create.table.ColumnDefinition; +import net.sf.jsqlparser.statement.create.table.CreateTable; +import net.sf.jsqlparser.statement.create.table.Index; +import net.sf.jsqlparser.statement.create.table.Index.ColumnParams; +import org.opengauss.admin.plugin.domain.InlineReference; +import org.opengauss.admin.plugin.domain.ReferenceParseResult; +import org.opengauss.admin.plugin.dto.ColumnDTO; +import org.opengauss.admin.plugin.dto.ForeignKeyDTO; +import org.opengauss.admin.plugin.dto.IndexDTO; +import org.opengauss.admin.plugin.dto.TableDTO; +import org.opengauss.admin.plugin.enums.DatabaseDialect; +import org.opengauss.admin.plugin.service.SqlImportService; +import org.opengauss.admin.plugin.translate.SqlToDbmlTranslator; +import org.opengauss.admin.plugin.util.SqlDumpCleaner; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Converts SQL DDL text into DBML by parsing statements with JSqlParser and mapping them to DTOs. + * + * @since 2025-01-07 + */ +@Slf4j +@Service +public class SqlImportServiceImpl implements SqlImportService { + private static final Pattern ALTER_ADD_FOREIGN = Pattern.compile( + "(?i)ADD\\s+(CONSTRAINT\\s+\\S+\\s+)?FOREIGN\\s+KEY\\s*\\(([^)]+)\\)\\s*REFERENCES\\s+([\\w\\.\\\"`]+)" + + "\\s*\\(([^)]+)\\)([^;]*)"); + private static final Pattern ALTER_ADD_PRIMARY = Pattern.compile( + "(?i)ADD\\s+(CONSTRAINT\\s+\\S+\\s+)?PRIMARY\\s+KEY\\s*\\(([^)]+)\\)"); + private static final Pattern ALTER_ADD_UNIQUE = Pattern.compile( + "(?i)ADD\\s+(CONSTRAINT\\s+\\S+\\s+)?UNIQUE\\s*(KEY\\s+\\S+\\s*)?\\(([^)]+)\\)"); + private static final Set BOUNDARY_KEYWORDS = Set.of( + "not", "null", "unique", "primary", "key", "references", + "constraint", "check", "comment"); + private static final Pattern COLUMN_COMMENT_PATTERN = Pattern.compile( + "(?is)COMMENT\\s+ON\\s+COLUMN\\s+((?:[\\w\\\"`]+\\.)?)([\\w\\\"`]+)\\.([\\w\\\"`]+)" + + "\\s+IS\\s+'((?:''|[^'])*)'"); + private static final Pattern FK_ON_DELETE_PATTERN = Pattern.compile( + "(?i)ON\\s+DELETE\\s+(CASCADE|SET\\s+NULL|SET\\s+DEFAULT|RESTRICT|NO\\s+ACTION)"); + private static final Pattern FK_ON_UPDATE_PATTERN = Pattern.compile( + "(?i)ON\\s+UPDATE\\s+(CASCADE|SET\\s+NULL|SET\\s+DEFAULT|RESTRICT|NO\\s+ACTION)"); + private static final Pattern INDEX_COLUMNS_PATTERN = Pattern.compile("\\((.+)\\)"); + private static final Pattern TABLE_COMMENT_PATTERN = Pattern.compile( + "(?is)COMMENT\\s+ON\\s+TABLE\\s+([\\w\\.\\\"`]+)\\s+IS\\s+'((?:''|[^'])*)'"); + private static final Pattern TABLE_LEVEL_FK_PATTERN = Pattern.compile( + "(?i)REFERENCES\\s+([\\w\\.\\\"`]+)\\s*\\(([^\\)]+)\\)([^;]*)"); + private static final List UNSUPPORTED_CREATE_PATTERNS = List.of( + Pattern.compile("(?is)CREATE\\s+EXTENSION[\\s\\S]*?;"), + Pattern.compile("(?is)CREATE\\s+TRIGGER[\\s\\S]*?;"), + Pattern.compile("(?is)CREATE\\s+SCHEMA[\\s\\S]*?;"), + Pattern.compile("(?is)CREATE\\s+SEQUENCE[\\s\\S]*?;"), + Pattern.compile("(?is)CREATE\\s+TYPE[\\s\\S]*?;") + ); + + private static final class InlineReferenceBuilder { + private final String referencedTable; + private final List referencedColumns; + private Optional onDelete = Optional.empty(); + private Optional onUpdate = Optional.empty(); + + InlineReferenceBuilder(String referencedTable, List referencedColumns) { + this.referencedTable = referencedTable; + this.referencedColumns = referencedColumns; + } + + void onDelete(Optional action) { + this.onDelete = action; + } + + void onUpdate(Optional action) { + this.onUpdate = action; + } + + InlineReference build() { + return new InlineReference(referencedTable, referencedColumns, onDelete, onUpdate); + } + } + + private static final class CommentBucket { + private final Map tableNotes = new HashMap<>(); + private final Map> columnNotes = new HashMap<>(); + } + + @Override + public String sqlToDbml(String sqlText, DatabaseDialect dialect, boolean shouldSkipData) { + String ddlOnly = shouldSkipData ? SqlDumpCleaner.keepDdlOnly(sqlText, dialect) : sqlText; + String sanitized = removeUnsupportedCreateStatements(preprocessPostgresCasts(ddlOnly)); + + CommentBucket comments = extractComments(sanitized); + Map tablesByName = new LinkedHashMap<>(); + parseStatements(sanitized, dialect, tablesByName); + applyComments(comments, tablesByName); + + return SqlToDbmlTranslator.generate(new ArrayList<>(tablesByName.values())); + } + + private void parseStatements(String ddl, DatabaseDialect dialect, Map tablesByName) { + try { + Statements statements = CCJSqlParserUtil.parseStatements(ddl); + for (Statement statement : statements.getStatements()) { + if (statement instanceof CreateTable) { + handleCreateTable((CreateTable) statement, dialect, tablesByName); + } else if (statement instanceof CreateIndex) { + handleCreateIndex((CreateIndex) statement, tablesByName); + } else if (statement instanceof Alter) { + handleAlterFallback((Alter) statement, tablesByName); + } else { + log.debug("Skipping unsupported statement: {}", statement); + } + } + } catch (JSQLParserException ex) { + throw new IllegalArgumentException("SQL 解析失败:" + ex.getMessage(), ex); + } + } + + private void handleCreateTable( + CreateTable createTable, + DatabaseDialect dialect, + Map tablesByName) { + String tableName = unquote(createTable.getTable()); + TableDTO table = tablesByName.computeIfAbsent(tableName, TableDTO::new); + table.setTableName(tableName); + + for (ColumnDefinition definition : safe(createTable.getColumnDefinitions())) { + ColumnDTO column = buildColumn(definition, dialect); + table.getColumns().add(column); + parseInlineReferences(definition, table); + } + + for (Index index : safe(createTable.getIndexes())) { + applyTableConstraint(table, index); + } + } + + private ColumnDTO buildColumn(ColumnDefinition definition, DatabaseDialect dialect) { + ColumnDTO column = new ColumnDTO(); + column.setColumnName(unquote(definition.getColumnName())); + + ColDataType type = definition.getColDataType(); + String rawType = type == null || type.getDataType() == null ? "varchar" : type.getDataType(); + List args = type == null || type.getArgumentsStringList() == null + ? List.of() + : type.getArgumentsStringList(); + normalizeTypeAndLength(rawType, args, dialect, column); + + parseColumnSpecs(definition.getColumnSpecs(), dialect, column); + return column; + } + + private void normalizeTypeAndLength( + String rawType, + List args, + DatabaseDialect dialect, + ColumnDTO column) { + String normalized = Optional.ofNullable(rawType) + .orElse("varchar") + .trim() + .toLowerCase(Locale.ROOT); + + if ("serial".equals(normalized) || "bigserial".equals(normalized) || "smallserial".equals(normalized)) { + column.setAutoIncrement(true); + if ("serial".equals(normalized)) { + column.setDataType("int"); + } else if ("bigserial".equals(normalized)) { + column.setDataType("bigint"); + } else { + column.setDataType("smallint"); + } + return; + } + + if ("integer".equals(normalized)) { + normalized = "int"; + } + if (normalized.startsWith("timestamp")) { + normalized = "timestamp"; + } + + boolean isUnsigned = normalized.contains("unsigned"); + if (isUnsigned) { + normalized = normalized.replace("unsigned", "").trim(); + } + column.setUnsigned(isUnsigned); + column.setDataType(normalized); + + if (args.isEmpty()) { + return; + } + if (args.size() == 1) { + parseIntSafe(args.get(0)).ifPresent(value -> column.setLength(value)); + return; + } + if (args.size() >= 2) { + parseIntSafe(args.get(0)).ifPresent(value -> column.setPrecision(value)); + parseIntSafe(args.get(1)).ifPresent(value -> column.setScale(value)); + } + } + + private OptionalInt parseIntSafe(String text) { + if (text == null) { + return OptionalInt.empty(); + } + String t = text.trim(); + if (t.isEmpty()) { + return OptionalInt.empty(); + } + try { + return OptionalInt.of(Integer.parseInt(t)); + } catch (NumberFormatException ex) { + return OptionalInt.empty(); + } + } + + private void applyTableConstraint(TableDTO table, Index index) { + String type = Optional.ofNullable(index.getType()).orElse("").toUpperCase(Locale.ROOT); + List columns = index.getColumnsNames() == null + ? Collections.emptyList() + : index.getColumnsNames(); + + if (type.contains("PRIMARY")) { + if (!columns.isEmpty()) { + table.getPrimaryKeys().add(new ArrayList<>(stripQuotes(columns))); + } + return; + } + + if (type.contains("UNIQUE")) { + IndexDTO dto = new IndexDTO(); + dto.setIndexName(unquote(index.getName())); + dto.setUnique(true); + dto.getColumnNames().addAll(stripQuotes(columns)); + table.getIndexes().add(dto); + return; + } + + if (type.contains("FOREIGN")) { + parseTableLevelForeignKey(index) + .ifPresent(fk -> { + if (fk.getColumnNames().isEmpty()) { + fk.setColumnNames(new ArrayList<>(stripQuotes(columns))); + } + table.getForeignKeys().add(fk); + }); + return; + } + + if (!columns.isEmpty()) { + IndexDTO dto = new IndexDTO(); + dto.setIndexName(unquote(index.getName())); + dto.setUnique(false); + dto.getColumnNames().addAll(stripQuotes(columns)); + table.getIndexes().add(dto); + } + } + + private void handleCreateIndex(CreateIndex createIndex, Map tablesByName) { + Table tableRef = createIndex.getTable(); + if (tableRef == null) { + return; + } + String tableName = unquote(tableRef.getName()); + TableDTO table = tablesByName.computeIfAbsent(tableName, TableDTO::new); + table.setTableName(tableName); + + Index index = createIndex.getIndex(); + if (index == null) { + return; + } + + IndexDTO dto = new IndexDTO(); + dto.setIndexName(unquote(index.getName())); + dto.setUnique(isUniqueIndex(createIndex, index)); + dto.getColumnNames().addAll(stripQuotes(extractIndexColumns(createIndex, index))); + table.getIndexes().add(dto); + } + + private boolean isUniqueIndex(CreateIndex createIndex, Index index) { + String type = Optional.ofNullable(index.getType()).orElse(""); + if (type.toUpperCase(Locale.ROOT).contains("UNIQUE")) { + return true; + } + return createIndex.toString().toUpperCase(Locale.ROOT).contains("CREATE UNIQUE INDEX"); + } + + private List extractIndexColumns(CreateIndex createIndex, Index index) { + if (index.getColumnsNames() != null && !index.getColumnsNames().isEmpty()) { + return index.getColumnsNames(); + } + List columnParams = index.getColumns(); + if (columnParams != null && !columnParams.isEmpty()) { + List columns = new ArrayList<>(columnParams.size()); + for (ColumnParams params : columnParams) { + columns.add(params.getColumnName()); + } + return columns; + } + Matcher matcher = INDEX_COLUMNS_PATTERN.matcher(createIndex.toString()); + if (!matcher.find()) { + return List.of(); + } + return splitColumns(matcher.group(1)); + } + + private void handleAlterFallback(Alter alter, Map tablesByName) { + Table tableRef = alter.getTable(); + if (tableRef == null) { + return; + } + String tableName = unquote(tableRef.getName()); + TableDTO table = tablesByName.computeIfAbsent(tableName, TableDTO::new); + table.setTableName(tableName); + + String alterSql = alter.toString(); + applyPrimaryKeyRegex(table, alterSql); + applyUniqueRegex(table, alterSql); + applyForeignKeyRegex(table, alterSql); + } + + private void applyPrimaryKeyRegex(TableDTO table, String alterSql) { + Matcher matcher = ALTER_ADD_PRIMARY.matcher(alterSql); + while (matcher.find()) { + List columns = stripQuotes(splitColumns(matcher.group(2))); + if (!columns.isEmpty()) { + table.getPrimaryKeys().add(columns); + } + } + } + + private void applyUniqueRegex(TableDTO table, String alterSql) { + Matcher matcher = ALTER_ADD_UNIQUE.matcher(alterSql); + while (matcher.find()) { + List columns = stripQuotes(splitColumns(matcher.group(3))); + if (columns.isEmpty()) { + continue; + } + IndexDTO dto = new IndexDTO(); + dto.setUnique(true); + dto.getColumnNames().addAll(columns); + table.getIndexes().add(dto); + } + } + + private void applyForeignKeyRegex(TableDTO table, String alterSql) { + Matcher matcher = ALTER_ADD_FOREIGN.matcher(alterSql); + while (matcher.find()) { + List sourceColumns = stripQuotes(splitColumns(matcher.group(2))); + String referencedTable = unquote(matcher.group(3)); + List targetColumns = stripQuotes(splitColumns(matcher.group(4))); + String tail = matcher.group(5); + + if (sourceColumns.isEmpty() || targetColumns.isEmpty()) { + continue; + } + + ForeignKeyDTO fk = new ForeignKeyDTO(); + fk.setColumnNames(sourceColumns); + fk.setReferencedTable(referencedTable); + fk.setReferencedColumnNames(targetColumns); + findMatch(FK_ON_DELETE_PATTERN, tail).ifPresent(fk::setOnDeleteAction); + findMatch(FK_ON_UPDATE_PATTERN, tail).ifPresent(fk::setOnUpdateAction); + table.getForeignKeys().add(fk); + } + } + + private void parseColumnSpecs(List specs, DatabaseDialect dialect, ColumnDTO column) { + List tokens = safe(specs); + List lowerTokens = toLowerCase(tokens); + for (int i = 0; i < lowerTokens.size(); i++) { + String lower = lowerTokens.get(i); + switch (lower) { + case "not": + if (i + 1 < lowerTokens.size() && "null".equals(lowerTokens.get(i + 1))) { + column.setNullable(false); + } + break; + case "null": + column.setNullable(true); + break; + case "unique": + column.setUnique(true); + break; + case "primary": + if (i + 1 < lowerTokens.size() && "key".equals(lowerTokens.get(i + 1))) { + column.setPrimaryKey(true); + } + break; + case "auto_increment": + case "auto": + column.setAutoIncrement(true); + break; + case "unsigned": + column.setUnsigned(true); + break; + case "default": + collectDefault(tokens, lowerTokens, i) + .map(this::normalizeDefaultValue) + .ifPresent(column::setDefaultValue); + break; + case "comment": + collectDefault(tokens, lowerTokens, i) + .ifPresent(column::setComment); + break; + default: + log.trace("Unsupported column spec '{}' for {}", tokens.get(i), column.getColumnName()); + } + } + } + + private Optional collectDefault( + List tokens, + List lowerTokens, + int index) { + StringBuilder builder = new StringBuilder(); + for (int i = index + 1; i < lowerTokens.size(); i++) { + String lower = lowerTokens.get(i); + if (isBoundaryKeyword(lower)) { + break; + } + if (builder.length() > 0) { + builder.append(' '); + } + builder.append(tokens.get(i)); + } + return builder.length() == 0 ? Optional.empty() : Optional.of(builder.toString()); + } + + private List splitColumns(String raw) { + if (raw == null || raw.isBlank()) { + return List.of(); + } + return Arrays.stream(raw.split(",")) + .map(String::trim) + .map(s -> s.replace("(`", "(").replace("`)", ")")) + .map(s -> s.replace("`", "")) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); + } + + private void parseInlineReferences(ColumnDefinition definition, TableDTO table) { + List specs = safe(definition.getColumnSpecs()); + List lower = toLowerCase(specs); + for (int i = 0; i < lower.size(); i++) { + if (!"references".equals(lower.get(i))) { + continue; + } + InlineReference reference = extractInlineReference(specs, i); + if (reference.referencedTable().isEmpty() || reference.referencedColumns().isEmpty()) { + continue; + } + ForeignKeyDTO fk = new ForeignKeyDTO(); + fk.setReferencedTable(reference.referencedTable()); + fk.setColumnNames(List.of(unquote(definition.getColumnName()))); + fk.setReferencedColumnNames(reference.referencedColumns()); + fk.setOnDeleteAction(reference.onDeleteAction().orElse(null)); + fk.setOnUpdateAction(reference.onUpdateAction().orElse(null)); + table.getForeignKeys().add(fk); + } + } + + private InlineReference extractInlineReference(List specs, int startIndex) { + InlineReferenceBuilder builder = new InlineReferenceBuilder( + startIndex + 1 < specs.size() ? unquote(specs.get(startIndex + 1)) : "", + parseParenthesizedColumns(specs, startIndex + 2)); + + for (int i = startIndex + 2; i < specs.size(); i++) { + if (!"on".equalsIgnoreCase(specs.get(i)) || i + 2 >= specs.size()) { + continue; + } + String actionType = specs.get(i + 1).toLowerCase(Locale.ROOT); + String action = specs.get(i + 2); + switch (actionType) { + case "delete" -> builder.onDelete(Optional.ofNullable(action)); + case "update" -> builder.onUpdate(Optional.ofNullable(action)); + default -> log.debug("Unsupported inline reference action '{}'", actionType); + } + } + return builder.build(); + } + + private List parseParenthesizedColumns(List tokens, int startIndex) { + StringBuilder builder = new StringBuilder(); + int depth = 0; + boolean hasStarted = false; + for (int i = startIndex; i < tokens.size(); i++) { + String token = tokens.get(i); + if (token.contains("(")) { + depth++; + hasStarted = true; + } + if (hasStarted) { + builder.append(token).append(' '); + } + if (token.contains(")")) { + depth--; + if (depth <= 0) { + break; + } + } + } + if (builder.length() == 0) { + return List.of(); + } + return stripQuotes(splitColumns(builder.toString())); + } + + private Optional parseTableLevelForeignKey(Index index) { + ReferenceParseResult result = parseReferenceTokens(safe(index.getIndexSpec())); + if (result.referencedTable() == null || result.referencedColumns().isEmpty()) { + Matcher matcher = TABLE_LEVEL_FK_PATTERN.matcher(index.toString()); + if (matcher.find()) { + String referencedTable = unquote(matcher.group(1)); + List referencedColumns = stripQuotes(splitColumns(matcher.group(2))); + String tail = matcher.group(3); + Optional onDelete = findMatch(FK_ON_DELETE_PATTERN, tail); + Optional onUpdate = findMatch(FK_ON_UPDATE_PATTERN, tail); + result = new ReferenceParseResult(referencedTable, referencedColumns, onDelete, onUpdate); + } + } + + if (result.referencedTable() == null || result.referencedColumns().isEmpty()) { + return Optional.empty(); + } + + ForeignKeyDTO fk = new ForeignKeyDTO(); + List sourceColumns = index.getColumnsNames() == null + ? Collections.emptyList() + : index.getColumnsNames(); + fk.setColumnNames(stripQuotes(sourceColumns)); + fk.setReferencedTable(result.referencedTable()); + fk.setReferencedColumnNames(result.referencedColumns()); + fk.setOnDeleteAction(result.onDelete().orElse(null)); + fk.setOnUpdateAction(result.onUpdate().orElse(null)); + return Optional.of(fk); + } + + private ReferenceParseResult parseReferenceTokens(List tokens) { + String referencedTable = null; + List referencedColumns = new ArrayList<>(); + Optional onDelete = Optional.empty(); + Optional onUpdate = Optional.empty(); + + for (int i = 0; i < tokens.size(); i++) { + String token = tokens.get(i); + String lower = token == null ? "" : token.toLowerCase(Locale.ROOT); + if ("references".equals(lower) && i + 1 < tokens.size()) { + referencedTable = unquote(tokens.get(i + 1)); + referencedColumns = stripQuotes(parseParenthesizedColumns(tokens, i + 2)); + } else if ("on".equals(lower) && i + 2 < tokens.size()) { + String actionType = tokens.get(i + 1).toLowerCase(Locale.ROOT); + String action = tokens.get(i + 2); + switch (actionType) { + case "delete" -> onDelete = Optional.ofNullable(action); + case "update" -> onUpdate = Optional.ofNullable(action); + default -> log.debug("Unsupported reference action '{}'", actionType); + } + } else { + continue; + } + } + return new ReferenceParseResult(referencedTable, referencedColumns, onDelete, onUpdate); + } + + private List stripQuotes(List values) { + List cleaned = new ArrayList<>(values.size()); + for (String value : values) { + cleaned.add(unquote(value)); + } + return cleaned; + } + + private String normalizeDefaultValue(String raw) { + if (raw == null) { + return ""; + } + String value = raw.trim(); + while (value.startsWith("(") && value.endsWith(")")) { + value = value.substring(1, value.length() - 1).trim(); + } + if (value.length() >= 2 && value.startsWith("'") && value.endsWith("'")) { + return "'" + value.substring(1, value.length() - 1).replace("'", "''") + "'"; + } + if (value.matches("[-+]?\\d+(\\.\\d+)?")) { + return value; + } + if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { + return value.toLowerCase(Locale.ROOT); + } + if (value.equalsIgnoreCase("null")) { + return "NULL"; + } + value = value.replaceAll("\\s*\\(\\s*", "(").replaceAll("\\s*\\)\\s*", ")"); + return "`" + value + "`"; + } + + private String preprocessPostgresCasts(String sql) { + if (sql == null) { + return ""; + } + String sanitized = sql.replaceAll("'([^']+)'::regclass", "'$1'"); + return sanitized.replaceAll("::[a-zA-Z0-9_ ]+", ""); + } + + private String removeUnsupportedCreateStatements(String sql) { + if (sql == null) { + return ""; + } + String sanitized = sql; + for (Pattern pattern : UNSUPPORTED_CREATE_PATTERNS) { + sanitized = pattern.matcher(sanitized).replaceAll(""); + } + return sanitized; + } + + private CommentBucket extractComments(String sql) { + CommentBucket bucket = new CommentBucket(); + + Matcher tableMatcher = TABLE_COMMENT_PATTERN.matcher(sql); + while (tableMatcher.find()) { + String tableName = unquote(tableMatcher.group(1)); + String note = unescapeSqlString(tableMatcher.group(2)); + bucket.tableNotes.put(tableName, note); + } + + Matcher columnMatcher = COLUMN_COMMENT_PATTERN.matcher(sql); + while (columnMatcher.find()) { + String tableName = unquote(columnMatcher.group(2)); + String columnName = unquote(columnMatcher.group(3)); + String note = unescapeSqlString(columnMatcher.group(4)); + bucket.columnNotes + .computeIfAbsent(tableName, key -> new HashMap<>()) + .put(columnName, note); + } + return bucket; + } + + private void applyComments(CommentBucket comments, Map tablesByName) { + for (Map.Entry entry : comments.tableNotes.entrySet()) { + TableDTO table = tablesByName.get(entry.getKey()); + if (table != null && (table.getComment() == null || table.getComment().isBlank())) { + table.setComment(entry.getValue()); + } + } + for (Map.Entry> entry : comments.columnNotes.entrySet()) { + TableDTO table = tablesByName.get(entry.getKey()); + if (table == null) { + continue; + } + Map columnNotes = entry.getValue(); + for (ColumnDTO column : table.getColumns()) { + String note = columnNotes.get(column.getColumnName()); + if (note != null && (column.getComment() == null || column.getComment().isBlank())) { + column.setComment(note); + } + } + } + } + + private String unquote(Table table) { + return table == null ? "" : unquote(table.getName()); + } + + private String unquote(String value) { + if (value == null) { + return ""; + } + String trimmed = value.trim(); + if ((trimmed.startsWith("`") && trimmed.endsWith("`")) + || (trimmed.startsWith("\"") && trimmed.endsWith("\""))) { + return trimmed.substring(1, trimmed.length() - 1); + } + return trimmed; + } + + private String unescapeSqlString(String value) { + return value.replace("''", "'"); + } + + private Optional findMatch(Pattern pattern, String input) { + Matcher matcher = pattern.matcher(input); + return matcher.find() ? Optional.ofNullable(matcher.group(1)) : Optional.empty(); + } + + private static List safe(List list) { + return list == null ? List.of() : list; + } + + private List toLowerCase(List tokens) { + List lower = new ArrayList<>(tokens.size()); + for (String token : tokens) { + lower.add(token == null ? "" : token.toLowerCase(Locale.ROOT)); + } + return lower; + } + + private boolean isBoundaryKeyword(String token) { + return BOUNDARY_KEYWORDS.contains(token); + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/core/SchemaPreambleBuilder.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/core/SchemaPreambleBuilder.java new file mode 100644 index 000000000..2350d30d8 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/core/SchemaPreambleBuilder.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.sql.core; + +import org.opengauss.admin.plugin.enums.DatabaseDialect; + +/** + * Utility class that generates dialect-specific SQL snippets for ensuring a schema or + * database exists and is selected before running further DDL statements. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +public final class SchemaPreambleBuilder { + private SchemaPreambleBuilder() { + // utility class + } + + /** + * Produce the SQL preamble that ensures the target schema exists and is active. + * + * @param dialect database dialect + * @param schema schema or database name + * @return SQL preamble string (may be empty) + */ + public static String build(DatabaseDialect dialect, String schema) { + if (schema == null || schema.isBlank()) { + return ""; + } + + String preamble; + switch (dialect) { + case MYSQL: + // MySQL: schema equals database + preamble = "CREATE DATABASE IF NOT EXISTS " + backtick(schema) + + ";\nUSE " + backtick(schema) + ";\n"; + break; + case POSTGRESQL: + case OPENGAUSS: + // PostgreSQL/openGauss: schema within current database + preamble = "CREATE SCHEMA IF NOT EXISTS " + doubleQuote(schema) + + ";\nSET search_path TO " + doubleQuote(schema) + ", public;\n"; + break; + case MSSQL: + // SQL Server: create database then ensure default schema + preamble = "IF DB_ID('" + escapeSingle(schema) + "') IS NULL BEGIN\n" + + " CREATE DATABASE " + bracket(schema) + "\nEND\n" + + "USE " + bracket(schema) + ";\n" + + "IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = '" + + escapeSingle(schema) + "') BEGIN\n" + + " EXEC('CREATE SCHEMA " + doubleQuote(schema) + "');\nEND\n"; + break; + default: + preamble = ""; + break; + } + return preamble; + } + + private static String doubleQuote(String value) { + return "\"" + value.replace("\"", "\"\"") + "\""; + } + + private static String backtick(String value) { + return "`" + value.replace("`", "``") + "`"; + } + + private static String bracket(String value) { + return "[" + value.replace("]", "]]") + "]"; + } + + private static String escapeSingle(String value) { + return value.replace("'", "''"); + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/core/SqlGenerator.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/core/SqlGenerator.java new file mode 100644 index 000000000..410e08311 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/core/SqlGenerator.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.sql.core; + +import org.opengauss.admin.plugin.dto.TableDTO; + +import java.util.List; + +/** + * Defines the contract for generating dialect-specific SQL from table metadata. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +public interface SqlGenerator { + /** + * Generate SQL statements that create or alter the provided tables. + * + * @param tables table definitions + * @return SQL text + */ + String generate(List tables); +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/dialect/MySQLGenerator.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/dialect/MySQLGenerator.java new file mode 100644 index 000000000..8b7b1b017 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/dialect/MySQLGenerator.java @@ -0,0 +1,433 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.sql.dialect; + +import org.opengauss.admin.plugin.dto.ColumnDTO; +import org.opengauss.admin.plugin.dto.ForeignKeyDTO; +import org.opengauss.admin.plugin.dto.IndexDTO; +import org.opengauss.admin.plugin.dto.TableDTO; +import org.opengauss.admin.plugin.sql.core.SqlGenerator; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * SQL generator for the MySQL dialect (supports enum/set, precision/scale, unsigned columns, and self-referencing FKs). + * + * @since 2025-09-07 + */ +public class MySQLGenerator implements SqlGenerator { + private static final Set GEOMETRY_TYPES = + new HashSet<>(Arrays.asList("geometry", "point", "linestring", "polygon", + "multipoint", "multilinestring", "multipolygon", "geometrycollection")); + + /* -------- 0. Entry point -------- */ + @Override + public String generate(List tables) { + StringBuilder ddl = new StringBuilder(); + + for (TableDTO table : tables) { + /* 0-A Resolve primary keys (column flags take precedence) */ + List pkCols = resolvePrimaryKey(table); + + /* 1) CREATE TABLE header */ + ddl.append("CREATE TABLE `").append(table.getTableName()).append("` (\n"); + + /* 2) Columns / PK / indexes / inline FK */ + List defs = new ArrayList<>(); + table.getColumns().forEach(c -> defs.add(buildColumnDef(c))); + + if (!pkCols.isEmpty()) { + String pk = pkCols.stream().map(this::wrapBacktick).collect(Collectors.joining(", ")); + defs.add(" PRIMARY KEY (" + pk + ")"); + } + + if (table.getIndexes() != null) { + for (IndexDTO idx : table.getIndexes()) { + defs.add(buildIndexDef(table.getTableName(), idx)); + } + } + + Set sigSet = new HashSet<>(); + for (ForeignKeyDTO fk : table.getForeignKeys()) { + if (!isValidForeignKey(table, fk)) { + continue; + } + String sig = fk.getColumnNames() + + "->" + + fk.getReferencedTable() + + "." + + fk.getReferencedColumnNames(); + if (!sigSet.add(sig)) { + continue; + } + defs.add(buildForeignKeyDef(table.getTableName(), fk)); + } + + ddl.append(String.join(",\n", defs)).append("\n)") + .append(" ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"); + if (StringUtils.hasText(table.getComment())) { + ddl.append(" COMMENT='").append(escapeSqlLiteral(table.getComment())).append("'"); + } + ddl.append(";\n\n"); + } + return ddl.toString(); + } + + /* -------- 1. Foreign key validation -------- */ + private boolean isValidForeignKey(TableDTO table, ForeignKeyDTO fk) { + // Allow self-references; only verify that referenced columns exist locally + return fk.getColumnNames().stream() + .allMatch(cn -> table.getColumns().stream() + .anyMatch(col -> col.getColumnName().equals(cn))); + } + + /* -------- 2. Column definitions -------- */ + private String buildColumnDef(ColumnDTO c) { + StringBuilder sb = new StringBuilder(" `").append(c.getColumnName()).append("` ") + .append(resolveDataType(c)); + + if (Boolean.FALSE.equals(c.getNullable())) { + sb.append(" NOT NULL"); + } + if (Boolean.TRUE.equals(c.getAutoIncrement())) { + sb.append(" AUTO_INCREMENT"); + } + + if (c.getDefaultValue() != null && !c.getDefaultValue().isBlank()) { + sb.append(" DEFAULT ").append(formatDefaultValue(c.getDefaultValue())); + } else if (Boolean.TRUE.equals(c.getNullable())) { + sb.append(" DEFAULT NULL"); + } else { + ; // No Default value needed for nullable columns + } + + if (Boolean.TRUE.equals(c.getUnique())) { + sb.append(" UNIQUE"); + } + + if (StringUtils.hasText(c.getComment())) { + sb.append(" COMMENT '").append(escapeSqlLiteral(c.getComment())).append("'"); + } + return sb.toString(); + } + + /* -- 2-A Type mapping (length / precision / scale / enum / unsigned) -- */ + private String resolveDataType(ColumnDTO column) { + String type = column.getDataType().toLowerCase(Locale.ROOT); + boolean isUnsigned = Boolean.TRUE.equals(column.getUnsigned()); + + if ("enum".equals(type) || "set".equals(type)) { + return buildEnumOrSetType(type, column); + } + if (isIntegerType(type)) { + return withUnsigned(type.toUpperCase(Locale.ROOT), isUnsigned); + } + if (isDecimalType(type)) { + return buildDecimalType(type, column, isUnsigned); + } + if (isFloatingType(type)) { + return buildFloatingType(type, column, isUnsigned); + } + Optional lengthBased = resolveLengthBasedType(type, column); + if (lengthBased.isPresent()) { + return lengthBased.get(); + } + Optional temporal = resolveTemporalType(type, column.getScale()); + if (temporal.isPresent()) { + return temporal.get(); + } + if (GEOMETRY_TYPES.contains(type)) { + return type.toUpperCase(Locale.ROOT); + } + return "TEXT"; + } + + private boolean isIntegerType(String type) { + switch (type) { + case "tinyint": + case "smallint": + case "mediumint": + case "int": + case "bigint": + return true; + default: + return false; + } + } + + private boolean isDecimalType(String type) { + return "decimal".equals(type) || "numeric".equals(type); + } + + private boolean isFloatingType(String type) { + return "float".equals(type) || "double".equals(type); + } + + private String buildDecimalType(String type, ColumnDTO column, boolean isUnsigned) { + String keyword = "decimal".equals(type) ? "DECIMAL" : "NUMERIC"; + return withUnsigned(buildNumericType(keyword, column.getPrecision(), column.getScale()), isUnsigned); + } + + private String buildFloatingType(String type, ColumnDTO column, boolean isUnsigned) { + String keyword = type.toUpperCase(Locale.ROOT); + return withUnsigned(buildNumericType(keyword, column.getPrecision(), column.getScale()), isUnsigned); + } + + private Optional resolveLengthBasedType(String type, ColumnDTO column) { + switch (type) { + case "char": + return Optional.of(buildLengthType("CHAR", column.getLength(), 1)); + case "varchar": + return Optional.of(buildLengthType("VARCHAR", column.getLength(), 255)); + case "binary": + return Optional.of(buildLengthType("BINARY", column.getLength(), 1)); + case "varbinary": + return Optional.of(buildLengthType("VARBINARY", column.getLength(), 255)); + case "bit": + return Optional.of(buildLengthType("BIT", column.getLength(), 1)); + default: + return Optional.empty(); + } + } + + private Optional resolveTemporalType(String type, Integer scale) { + switch (type) { + case "date": + return Optional.of("DATE"); + case "year": + return Optional.of("YEAR"); + case "time": + return Optional.of(buildTemporalType("TIME", scale)); + case "datetime": + return Optional.of(buildTemporalType("DATETIME", scale)); + case "timestamp": + return Optional.of(buildTemporalType("TIMESTAMP", scale)); + default: + return Optional.empty(); + } + } + + private String buildEnumOrSetType(String type, ColumnDTO column) { + List values = column.getEnumValues(); + if (values == null || values.isEmpty()) { + return type.toUpperCase(Locale.ROOT); + } + String joined = values.stream() + .map(value -> "'" + escapeSqlLiteral(value) + "'") + .collect(Collectors.joining(",")); + return type.toUpperCase(Locale.ROOT) + "(" + joined + ")"; + } + + private String withUnsigned(String sqlType, boolean isUnsigned) { + if (isUnsigned) { + return sqlType + " UNSIGNED"; + } + return sqlType; + } + + private String buildNumericType(String keyword, Integer precision, Integer scale) { + StringBuilder sb = new StringBuilder(keyword); + if (precision != null) { + sb.append("(").append(precision); + if (scale != null) { + sb.append(",").append(scale); + } + sb.append(")"); + } + return sb.toString(); + } + + private String buildLengthType(String keyword, Integer length, int defaultLength) { + int size = length != null ? length.intValue() : defaultLength; + return keyword + "(" + size + ")"; + } + + private String buildTemporalType(String keyword, Integer scale) { + if (scale == null) { + return keyword; + } + return keyword + "(" + scale + ")"; + } + + /* -------- 3. Index definitions -------- */ + private String buildIndexDef(String table, IndexDTO idx) { + String idxName = idx.getIndexName(); + if (!StringUtils.hasText(idxName)) { + String cols = String.join("_", idx.getColumnNames()).replaceAll("[`()\\s]", ""); + idxName = "idx_" + table + "_" + cols; + } + + StringBuilder sb = new StringBuilder(" "); + if (Boolean.TRUE.equals(idx.getUnique())) { + sb.append("UNIQUE "); + } + sb.append("KEY `").append(idxName).append("` ("); + + String colsExpr = idx.getColumnNames().stream() + .map(this::renderIndexKey) + .collect(Collectors.joining(", ")); + sb.append(colsExpr).append(")"); + + if (StringUtils.hasText(idx.getIndexType())) { + sb.append(" USING ").append(idx.getIndexType().toUpperCase(Locale.ROOT)); + } + return sb.toString(); + } + + /* -------- 4. Foreign key definitions -------- */ + private String buildForeignKeyDef(String table, ForeignKeyDTO fk) { + String fkName = "fk_" + table + "_" + fk.getColumnNames().get(0) + "_" + fk.getReferencedTable(); + + String localCols = fk.getColumnNames().stream() + .map(this::wrapBacktick).collect(Collectors.joining(", ")); + String refCols = fk.getReferencedColumnNames().stream() + .map(this::wrapBacktick).collect(Collectors.joining(", ")); + + StringBuilder sb = new StringBuilder(" CONSTRAINT ") + .append(wrapBacktick(fkName)) + .append(" FOREIGN KEY (").append(localCols).append(")") + .append(" REFERENCES `").append(fk.getReferencedTable()).append("`(").append(refCols).append(")"); + + if (fk.getOnUpdateAction() != null) { + sb.append(" ON UPDATE ").append(fk.getOnUpdateAction()); + } + if (fk.getOnDeleteAction() != null) { + sb.append(" ON DELETE ").append(fk.getOnDeleteAction()); + } + + return sb.toString(); + } + + /* -------- 5. Helper methods -------- */ + private List resolvePrimaryKey(TableDTO table) { + List pk = table.getColumns().stream() + .filter(c -> Boolean.TRUE.equals(c.getPrimaryKey())) + .map(ColumnDTO::getColumnName).collect(Collectors.toList()); + + if (pk.isEmpty() && table.getIndexes() != null) { + for (Iterator it = table.getIndexes().iterator(); it.hasNext(); ) { + IndexDTO idx = it.next(); + if (Boolean.TRUE.equals(idx.getPk())) { + pk.addAll(idx.getColumnNames()); + it.remove(); // PK already handled; avoid duplication + break; + } + } + } + return pk; + } + + private String wrapBacktick(String id) { + return "`" + id + "`"; + } + + private String escapeSqlLiteral(String txt) { + return txt.replace("'", "''"); + } + + private String formatDefaultValue(String v) { + if (v == null) { + return "NULL"; + } + if (v.matches("^[a-zA-Z_]+\\(.*\\)$")) { + return v; + } + if (isNumericLiteral(v)) { + return v; + } + return "'" + escapeSqlLiteral(v) + "'"; + } + + private boolean isNumericLiteral(String value) { + try { + Double.parseDouble(value); + return true; + } catch (NumberFormatException ex) { + return false; + } + } + + // Determine whether the value is an expression (or a DBML backtick-wrapped expression) + // Also exclude MySQL prefix-length indexes such as column(10) + private boolean isExpression(String s) { + if (s == null) { + return false; + } + String x = s.trim(); + + // Backticks wrap expressions in DBML conventions: `lower(title)`, `date(created_at)`, etc. + if (x.length() >= 2 && x.startsWith("`") && x.endsWith("`")) { + return true; + } + + // Column prefix notation: col(10) indicates a column, not an expression + if (x.matches("^[A-Za-z_][A-Za-z0-9_]*\\(\\d+\\)$")) { + return false; + } + + // Heuristically treat parentheses, whitespace, operators, JSON arrows, or casts as expressions + return x.startsWith("(") || x.endsWith(")") + || x.contains("(") || x.contains(")") || x.contains(" ") + || x.contains("+") || x.contains("-") || x.contains("*") || x.contains("/") + || x.contains("->") || x.contains("->>") || x.contains("::") || x.contains(","); + } + + // MySQL 8 requires function/expression index key parts to be wrapped in parentheses: (expr) + // Do not add another pair if it already exists + private String ensureParenWrapped(String expr) { + String x = expr.trim(); + if (x.startsWith("(") && x.endsWith(")")) { + return x; + } + return "(" + x + ")"; + } + + // Render index keys: keep expression parentheses, backtick identifiers, preserve prefix length `col`(len) + private String renderIndexKey(String s) { + if (s == null) { + return ""; + } + String x = s.trim(); + + // Remove DBML backticks used to mark expressions + if (x.length() >= 2 && x.startsWith("`") && x.endsWith("`")) { + x = x.substring(1, x.length() - 1); + } + + // Prefix length: name(20) + if (x.matches("^([A-Za-z_][A-Za-z0-9_]*)\\((\\d+)\\)$")) { + String col = x.replaceAll("^([A-Za-z_][A-Za-z0-9_]*)\\((\\d+)\\)$", "$1"); + String len = x.replaceAll("^([A-Za-z_][A-Za-z0-9_]*)\\((\\d+)\\)$", "$2"); + return "`" + col + "`(" + len + ")"; + } + + if (isExpression(x)) { + // Expressions: MySQL 8 requires (expr) + return ensureParenWrapped(x); + } + // Plain column name + return "`" + x + "`"; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/dialect/OpenGaussSqlGenerator.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/dialect/OpenGaussSqlGenerator.java new file mode 100644 index 000000000..9f2f19dbd --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/dialect/OpenGaussSqlGenerator.java @@ -0,0 +1,514 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.sql.dialect; + +import org.opengauss.admin.plugin.dto.ColumnDTO; +import org.opengauss.admin.plugin.dto.ForeignKeyDTO; +import org.opengauss.admin.plugin.dto.IndexDTO; +import org.opengauss.admin.plugin.dto.TableDTO; +import org.opengauss.admin.plugin.sql.core.SqlGenerator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * SQL generator for the openGauss dialect (supports Dolphin unsigned/enum/self-referencing FK features). + * + * @version 1.0.0 + * @since 2025-09-07 + */ +public class OpenGaussSqlGenerator implements SqlGenerator { + private static final Pattern PRECISION_PATTERN = Pattern.compile("\\((\\d+)\\)"); + private static final Set INTEGER_TYPES = Set.of( + "tinyint", "uint1", "smallint", "uint2", "mediumint", "int", "integer", "binary_integer", "uint4", + "bigint", "uint8", "int16"); + private static final Set NUMERIC_TYPES = Set.of("numeric", "decimal", "dec", "number"); + private static final Set REAL_TYPES = Set.of("real", "float4"); + private static final Set DOUBLE_TYPES = Set.of("double precision", "float8", "binary_double"); + private static final Set CHAR_TYPES = Set.of("char", "character", "nchar"); + private static final Set VARCHAR_TYPES = Set.of("varchar", "character varying", "varchar2", "nvarchar2"); + private static final Set TEXT_TYPES = Set.of("text", "clob"); + private static final Set BINARY_TYPES = Set.of("binary", "varbinary", "bytea"); + private static final Set BIT_VARYING_TYPES = Set.of("bit varying", "varbit"); + private static final Set DIRECT_UPPERCASE_TYPES = Set.of( + "json", "jsonb", "uuid", "tsvector", "tsquery", "hll", + "point", "lseg", "box", "path", "polygon", "circle", + "cidr", "inet", "macaddr", + "int4range", "int8range", "numrange", "tsrange", "tstzrange", "daterange"); + + @Override + public String generate(List tables) { + StringBuilder builder = new StringBuilder(); + builder.append(buildEnumTypeSection(tables)); + for (TableDTO table : safeList(tables)) { + builder.append(buildTableSection(table)); + } + return builder.toString(); + } + + private String buildEnumTypeSection(List tables) { + Set emitted = new LinkedHashSet<>(); + StringBuilder builder = new StringBuilder(); + for (TableDTO table : safeList(tables)) { + for (ColumnDTO column : safeList(table.getColumns())) { + List enumValues = column.getEnumValues(); + if (enumValues == null || enumValues.isEmpty()) { + continue; + } + String typeName = buildEnumTypeName(table, column); + if (emitted.add(typeName)) { + String values = enumValues.stream() + .map(v -> "'" + v.replace("'", "''") + "'") + .collect(Collectors.joining(", ")); + builder.append("DO $$ BEGIN\n") + .append(" IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = '") + .append(typeName) + .append("') THEN\n") + .append(" CREATE TYPE ").append(escape(typeName)) + .append(" AS ENUM (").append(values).append(");\n") + .append(" END IF;\n") + .append("END $$;\n\n"); + } + } + } + return builder.toString(); + } + + private String buildTableSection(TableDTO table) { + StringBuilder builder = new StringBuilder(); + String tableName = escape(table.getTableName()); + + List> primaryKeys = collectPrimaryKeys(table); + Set pkColumnNames = extractPrimaryKeyColumns(primaryKeys); + builder.append("CREATE TABLE ").append(tableName).append(" (\n"); + builder.append(String.join(",\n", buildColumnDefinitions(table, pkColumnNames))); + appendPrimaryKey(builder, primaryKeys, table.getTableName()); + builder.append("\n);\n\n"); + appendIndexes(builder, table); + appendComments(builder, tableName, table); + appendForeignKeys(builder, tableName, table); + return builder.toString(); + } + + private List buildColumnDefinitions(TableDTO table, Set pkColumnNames) { + List lines = new ArrayList<>(); + for (ColumnDTO column : safeList(table.getColumns())) { + StringBuilder builder = new StringBuilder(" ") + .append(escape(column.getColumnName())) + .append(' ') + .append(resolveDataType(table, column)); + if (pkColumnNames.contains(column.getColumnName()) + || Boolean.FALSE.equals(column.getNullable())) { + builder.append(" NOT NULL"); + } else { + builder.append(" NULL"); + } + if (Boolean.TRUE.equals(column.getAutoIncrement())) { + builder.append(" GENERATED BY DEFAULT AS IDENTITY"); + } + if (column.getDefaultValue() != null && !column.getDefaultValue().isBlank()) { + builder.append(" DEFAULT ").append(formatDefaultValue(column.getDefaultValue())); + } + if (Boolean.TRUE.equals(column.getUnique())) { + builder.append(" UNIQUE"); + } + lines.add(builder.toString()); + } + return lines; + } + + private void appendPrimaryKey(StringBuilder builder, List> primaryKeys, String tableName) { + if (primaryKeys.isEmpty()) { + return; + } + List keyColumns = primaryKeys.get(0); + String joinedColumns = keyColumns.stream() + .map(this::expressOrEscape) + .collect(Collectors.joining(", ")); + builder.append(",\n CONSTRAINT ") + .append(escape("pk_" + tableName)) + .append(" PRIMARY KEY (") + .append(joinedColumns) + .append(')'); + } + + private void appendIndexes(StringBuilder builder, TableDTO table) { + for (IndexDTO index : safeList(table.getIndexes())) { + if (Boolean.TRUE.equals(index.getPk())) { + continue; + } + String name = index.getIndexName(); + if (name == null || name.isBlank()) { + String columns = String.join("_", safeList(index.getColumnNames())) + .replaceAll("[`()\\s]", ""); + name = "idx_" + table.getTableName() + "_" + columns; + } + String columnsClause = safeList(index.getColumnNames()).stream() + .map(this::expressOrEscape) + .collect(Collectors.joining(", ")); + builder.append("CREATE "); + if (Boolean.TRUE.equals(index.getUnique())) { + builder.append("UNIQUE "); + } + builder.append("INDEX ") + .append(escape(name)) + .append(" ON ") + .append(escape(table.getTableName())) + .append(" (") + .append(columnsClause) + .append(");\n"); + } + if (!safeList(table.getIndexes()).isEmpty()) { + builder.append('\n'); + } + } + + private void appendComments(StringBuilder builder, String tableName, TableDTO table) { + if (table.getComment() != null && !table.getComment().isBlank()) { + builder.append("COMMENT ON TABLE ") + .append(tableName) + .append(" IS '") + .append(escapeSqlLiteral(table.getComment())) + .append("';\n"); + } + for (ColumnDTO column : safeList(table.getColumns())) { + if (column.getComment() == null || column.getComment().isBlank()) { + continue; + } + builder.append("COMMENT ON COLUMN ") + .append(tableName).append('.') + .append(escape(column.getColumnName())) + .append(" IS '") + .append(escapeSqlLiteral(column.getComment())) + .append("';\n"); + } + if (builder.length() > 0) { + builder.append('\n'); + } + } + + private void appendForeignKeys(StringBuilder builder, String tableName, TableDTO table) { + Set signatures = new HashSet<>(); + for (ForeignKeyDTO fk : safeList(table.getForeignKeys())) { + if (!isValidForeignKey(table, fk)) { + continue; + } + String signature = fk.getColumnNames() + + "->" + fk.getReferencedTable() + + "." + fk.getReferencedColumnNames(); + if (!signatures.add(signature)) { + continue; + } + String constraintName = "fk_" + table.getTableName() + "_" + fk.getReferencedTable(); + String localColumns = safeList(fk.getColumnNames()).stream() + .map(this::expressOrEscape) + .collect(Collectors.joining(", ")); + String referencedColumns = safeList(fk.getReferencedColumnNames()).stream() + .map(this::expressOrEscape) + .collect(Collectors.joining(", ")); + builder.append("ALTER TABLE ") + .append(tableName) + .append(" ADD CONSTRAINT ") + .append(escape(constraintName)) + .append(" FOREIGN KEY (") + .append(localColumns) + .append(") REFERENCES ") + .append(escape(fk.getReferencedTable())) + .append(" (") + .append(referencedColumns) + .append(')'); + if (fk.getOnUpdateAction() != null) { + builder.append(" ON UPDATE ").append(fk.getOnUpdateAction()); + } + if (fk.getOnDeleteAction() != null) { + builder.append(" ON DELETE ").append(fk.getOnDeleteAction()); + } + builder.append(";\n\n"); + } + } + + private List> collectPrimaryKeys(TableDTO table) { + List> keys = new ArrayList<>(); + if (table.getPrimaryKeys() != null) { + keys.addAll(table.getPrimaryKeys()); + } + for (IndexDTO index : safeList(table.getIndexes())) { + if (Boolean.TRUE.equals(index.getPk())) { + keys.add(new ArrayList<>(safeList(index.getColumnNames()))); + } + } + return keys; + } + + private Set extractPrimaryKeyColumns(List> primaryKeys) { + Set columns = new HashSet<>(); + for (List group : primaryKeys) { + for (String value : group) { + if (!value.contains("`")) { + columns.add(value); + } + } + } + return columns; + } + + private boolean isValidForeignKey(TableDTO table, ForeignKeyDTO fk) { + if (fk == null + || safeList(fk.getColumnNames()).isEmpty() + || safeList(fk.getReferencedColumnNames()).isEmpty()) { + return false; + } + Set columnNames = safeList(table.getColumns()).stream() + .map(ColumnDTO::getColumnName) + .collect(Collectors.toSet()); + return columnNames.containsAll(fk.getColumnNames()); + } + + private String buildEnumTypeName(TableDTO table, ColumnDTO column) { + return "enum_" + table.getTableName() + "_" + column.getColumnName(); + } + + private String expressOrEscape(String value) { + if (value == null) { + return ""; + } + if (value.startsWith("`") && value.endsWith("`")) { + return value.substring(1, value.length() - 1); + } + return escape(value); + } + + private String escape(String identifier) { + return "\"" + identifier.replace("\"", "\"\"") + "\""; + } + + private String escapeSqlLiteral(String text) { + return text.replace("'", "''"); + } + + private String resolveDataType(TableDTO table, ColumnDTO column) { + String type = column.getDataType().toLowerCase(Locale.ROOT); + if (Boolean.TRUE.equals(column.getAutoIncrement())) { + return resolveIdentityBaseType(type); + } + + Optional scalarType = resolveScalarType(type, column); + if (scalarType.isPresent()) { + return scalarType.get(); + } + + Optional temporalType = resolveTemporalType(type, column); + if (temporalType.isPresent()) { + return temporalType.get(); + } + + if (DIRECT_UPPERCASE_TYPES.contains(type)) { + return type.toUpperCase(Locale.ROOT); + } + if ("enum".equals(type)) { + return escape(buildEnumTypeName(table, column)); + } + if ("set".equals(type)) { + return "TEXT"; + } + return "TEXT"; + } + + private Optional resolveScalarType(String type, ColumnDTO column) { + boolean isUnsigned = Boolean.TRUE.equals(column.getUnsigned()); + if (isIntegerType(type)) { + return Optional.of(mapIntegerType(type)); + } + if (NUMERIC_TYPES.contains(type)) { + return Optional.of(formatNumericType(column)); + } + if ("float".equals(type)) { + return Optional.of(formatFloatType(column, isUnsigned)); + } + if (REAL_TYPES.contains(type)) { + return Optional.of("REAL"); + } + if (DOUBLE_TYPES.contains(type)) { + return Optional.of("DOUBLE PRECISION"); + } + if ("boolean".equals(type)) { + return Optional.of("BOOLEAN"); + } + if (CHAR_TYPES.contains(type)) { + return Optional.of(formatCharacterType(column, "CHAR")); + } + if (VARCHAR_TYPES.contains(type)) { + return Optional.of(formatCharacterType(column, "VARCHAR")); + } + if (TEXT_TYPES.contains(type)) { + return Optional.of("TEXT"); + } + if (BINARY_TYPES.contains(type)) { + return Optional.of("BYTEA"); + } + if ("bit".equals(type)) { + return Optional.of(formatBitType(column, false)); + } + if (BIT_VARYING_TYPES.contains(type)) { + return Optional.of(formatBitType(column, true)); + } + return Optional.empty(); + } + + private boolean isIntegerType(String type) { + return INTEGER_TYPES.contains(type); + } + + private String mapIntegerType(String type) { + switch (type) { + case "tinyint": + case "uint1": + case "smallint": + case "uint2": + return "SMALLINT"; + case "mediumint": + case "int": + case "integer": + case "binary_integer": + case "uint4": + return "INTEGER"; + case "bigint": + case "uint8": + case "int16": + return "BIGINT"; + default: + return "INTEGER"; + } + } + + private String resolveIdentityBaseType(String type) { + return switch (type) { + case "tinyint", "smallint" -> "SMALLINT"; + case "bigint" -> "BIGINT"; + default -> "INTEGER"; + }; + } + + private Optional resolveTemporalType(String type, ColumnDTO column) { + switch (type) { + case "date": + return Optional.of("DATE"); + case "time": + case "time without time zone": + return Optional.of(formatTemporalType("TIME", column, "WITHOUT TIME ZONE")); + case "time with time zone": + case "timetz": + return Optional.of(formatTemporalType("TIME", column, "WITH TIME ZONE")); + case "timestamp": + case "timestamp without time zone": + return Optional.of(formatTemporalType("TIMESTAMP", column, "WITHOUT TIME ZONE")); + case "timestamp with time zone": + case "timestamptz": + return Optional.of(formatTemporalType("TIMESTAMP", column, "WITH TIME ZONE")); + default: + return Optional.empty(); + } + } + + private String formatNumericType(ColumnDTO column) { + if (column.getPrecision() == null) { + return "NUMERIC"; + } + StringBuilder builder = new StringBuilder("NUMERIC(") + .append(column.getPrecision()); + if (column.getScale() != null) { + builder.append(", ").append(column.getScale()); + } + builder.append(')'); + return builder.toString(); + } + + private String formatFloatType(ColumnDTO column, boolean isUnsigned) { + StringBuilder builder = new StringBuilder("FLOAT"); + if (column.getPrecision() != null) { + builder.append("(").append(column.getPrecision()).append(")"); + } + if (isUnsigned) { + builder.append(" UNSIGNED"); + } + return builder.toString(); + } + + private String formatCharacterType(ColumnDTO column, String base) { + Integer length = column.getLength(); + if (length == null || length <= 0) { + length = 1; + } + return base + "(" + length + ")"; + } + + private String formatBitType(ColumnDTO column, boolean isVarying) { + Integer length = column.getLength(); + if (length == null || length <= 0) { + length = 1; + } + return isVarying ? "BIT VARYING(" + length + ")" : "BIT(" + length + ")"; + } + + private String formatTemporalType(String base, ColumnDTO column, String zoneClause) { + StringBuilder builder = new StringBuilder(base); + if (column.getScale() != null) { + builder.append("(").append(column.getScale()).append(")"); + } + builder.append(' ').append(zoneClause); + return builder.toString(); + } + + private String parsePrecisionType(String targetType, String originalType) { + if (originalType == null) { + return targetType; + } + Matcher matcher = PRECISION_PATTERN.matcher(originalType); + if (matcher.find()) { + return targetType + "(" + matcher.group(1) + ")"; + } + return targetType; + } + + private String formatDefaultValue(String value) { + if (value == null || value.isBlank() || "null".equalsIgnoreCase(value)) { + return "NULL"; + } + String trimmed = value.trim(); + if (trimmed.matches("^[a-zA-Z_]+\\(.*\\)$")) { + return trimmed; + } + try { + Double.parseDouble(trimmed); + return trimmed; + } catch (NumberFormatException ex) { + return "'" + escapeSqlLiteral(trimmed) + "'"; + } + } + + private List safeList(List list) { + return list == null ? Collections.emptyList() : list; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/dialect/PostgreSQLGenerator.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/dialect/PostgreSQLGenerator.java new file mode 100644 index 000000000..cdaabae26 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/dialect/PostgreSQLGenerator.java @@ -0,0 +1,424 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.sql.dialect; + +import org.opengauss.admin.plugin.dto.ColumnDTO; +import org.opengauss.admin.plugin.dto.ForeignKeyDTO; +import org.opengauss.admin.plugin.dto.IndexDTO; +import org.opengauss.admin.plugin.dto.TableDTO; +import org.opengauss.admin.plugin.sql.core.SqlGenerator; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * SQL generator for PostgreSQL. + * Mirrors openGauss behaviour but omits Dolphin-specific extensions and unsigned types. + * + * @version 1.0.0 + * @since 2025-09-07 + */ +public class PostgreSQLGenerator implements SqlGenerator { + @Override + public String generate(List tables) { + List safeTables = safeList(tables); + + StringBuilder enumBuilder = new StringBuilder(); + Set generatedEnumTypes = new HashSet<>(); + + for (TableDTO table : safeTables) { + for (ColumnDTO column : safeList(table.getColumns())) { + if (column.getEnumValues() == null || column.getEnumValues().isEmpty()) { + continue; + } + String typeName = buildEnumTypeName(table, column); + if (generatedEnumTypes.add(typeName)) { + enumBuilder.append(genCreateEnumTypeSql(typeName, column.getEnumValues())).append('\n'); + } + } + } + + StringBuilder tableBuilder = new StringBuilder(); + StringBuilder fkBuilder = new StringBuilder(); + + for (TableDTO table : safeTables) { + appendCreateTableSql(table, tableBuilder); + appendIndexes(table, tableBuilder); + appendComments(table, tableBuilder); + appendForeignKeys(table, fkBuilder); + } + + return enumBuilder.append('\n').append(tableBuilder).append(fkBuilder).toString(); + } + + /* -------- Table definition -------- */ + + private void appendCreateTableSql(TableDTO table, StringBuilder out) { + List pkCols = resolvePrimaryKey(table); + + out.append("CREATE TABLE ").append(escape(table.getTableName())).append(" (\n"); + + List columnDefinitions = new ArrayList<>(); + for (ColumnDTO column : safeList(table.getColumns())) { + columnDefinitions.add(buildColumnDefinition(table, column)); + } + + if (!pkCols.isEmpty()) { + String pk = pkCols.stream().map(this::escape).collect(Collectors.joining(", ")); + columnDefinitions.add(" PRIMARY KEY (" + pk + ")"); + } + + out.append(String.join(",\n", columnDefinitions)).append("\n);\n\n"); + } + + private List resolvePrimaryKey(TableDTO table) { + List pk = table.getColumns().stream() + .filter(c -> Boolean.TRUE.equals(c.getPrimaryKey())) + .map(ColumnDTO::getColumnName) + .collect(Collectors.toList()); + + if (pk.isEmpty() && table.getIndexes() != null) { + for (Iterator iterator = table.getIndexes().iterator(); iterator.hasNext(); ) { + IndexDTO index = iterator.next(); + if (Boolean.TRUE.equals(index.getPk())) { + pk.addAll(index.getColumnNames()); + iterator.remove(); + break; + } + } + } + return pk; + } + + private String buildColumnDefinition(TableDTO table, ColumnDTO column) { + StringBuilder builder = new StringBuilder(" ") + .append(escape(column.getColumnName())) + .append(' ') + .append(resolveDataType(table, column)); + + if (Boolean.FALSE.equals(column.getNullable())) { + builder.append(" NOT NULL"); + } + if (Boolean.TRUE.equals(column.getUnique())) { + builder.append(" UNIQUE"); + } + if (column.getDefaultValue() != null && !column.getDefaultValue().isBlank()) { + builder.append(" DEFAULT ").append(formatDefaultValue(column.getDefaultValue())); + } + return builder.toString(); + } + + private String resolveDataType(TableDTO table, ColumnDTO column) { + String originalType = column.getDataType(); + String normalizedType = originalType == null ? "" : originalType.toLowerCase(Locale.ROOT); + + if (Boolean.TRUE.equals(column.getAutoIncrement())) { + return resolveAutoIncrementType(normalizedType); + } + + return resolveSpecialType(table, column, normalizedType) + .orElseGet(() -> resolvePrimitiveType(column, normalizedType, originalType)); + } + + private String resolveAutoIncrementType(String dataType) { + switch (dataType) { + case "smallint": + case "tinyint": + return "SMALLSERIAL"; + case "bigint": + return "BIGSERIAL"; + default: + return "SERIAL"; + } + } + + private String resolvePrimitiveType(ColumnDTO column, String dataType, String originalType) { + return switch (dataType) { + case "real", "float4" -> "REAL"; + case "double precision", "float8", "binary_double" -> "DOUBLE PRECISION"; + case "float" -> column.getPrecision() != null + ? "FLOAT(" + column.getPrecision() + ")" + : "FLOAT"; + case "boolean" -> "BOOLEAN"; + default -> { + String resolved = resolveIntegerOrStringType(column, dataType); + if (resolved != null) { + yield resolved; + } + yield resolveFallbackType(dataType, originalType); + } + }; + } + + private String resolveIntegerOrStringType(ColumnDTO column, String dataType) { + return switch (dataType) { + case "tinyint", "uint1", "smallint", "uint2" -> "SMALLINT"; + case "mediumint", "int", "integer", "binary_integer", "uint4" -> "INTEGER"; + case "bigint", "uint8", "int16" -> "BIGINT"; + case "numeric", "decimal", "dec", "number" -> formatWithPrecision("NUMERIC", column); + case "char", "character", "nchar" -> "CHAR(" + (column.getLength() != null ? column.getLength() : 1) + ")"; + case "varchar", "character varying", "varchar2", "nvarchar2" -> + "VARCHAR(" + (column.getLength() != null ? column.getLength() : 255) + ")"; + case "text", "clob" -> "TEXT"; + case "name" -> "NAME"; + case "\"char\"" -> "\"char\""; + case "binary", "varbinary", "bytea", + "byteawithoutorderwithequalcol", "byteawithoutordercol", + "_byteawithoutorderwithequalcol", "_byteawithoutordercol" -> "BYTEA"; + case "bit" -> "BIT(" + (column.getLength() != null ? column.getLength() : 1) + ")"; + case "bit varying", "varbit" -> { + int len = (column.getLength() != null ? column.getLength() : 1); + yield "BIT VARYING(" + len + ")"; + } + default -> null; + }; + } + + private String resolveFallbackType(String normalizedType, String originalType) { + return switch (normalizedType) { + case "xml", "json", "jsonb" -> normalizedType.toUpperCase(Locale.ROOT); + case "uuid" -> "UUID"; + default -> deriveFallbackType(normalizedType, originalType); + }; + } + + private String deriveFallbackType(String normalizedType, String originalType) { + if (originalType != null && !originalType.isBlank()) { + return originalType; + } + if (!normalizedType.isBlank()) { + return normalizedType.toUpperCase(Locale.ROOT); + } + return "TEXT"; + } + + private Optional resolveSpecialType(TableDTO table, ColumnDTO column, String dataType) { + return switch (dataType) { + case "date" -> Optional.of("DATE"); + case "time", "time without time zone" -> Optional.of( + appendScale("TIME", column).append(" WITHOUT TIME ZONE").toString()); + case "time with time zone", "timetz" -> Optional.of( + appendScale("TIME", column).append(" WITH TIME ZONE").toString()); + case "timestamp", "timestamp without time zone" -> Optional.of( + appendScale("TIMESTAMP", column).append(" WITHOUT TIME ZONE").toString()); + case "timestamp with time zone", "timestamptz" -> Optional.of( + appendScale("TIMESTAMP", column).append(" WITH TIME ZONE").toString()); + case "interval" -> Optional.of("INTERVAL"); + case "json", "jsonb", "uuid", "tsvector", "tsquery" -> + Optional.of(dataType.toUpperCase(Locale.ROOT)); + case "point", "lseg", "box", "path", "polygon", "circle", "cidr", "inet", "macaddr", + "int4range", "int8range", "numrange", "tsrange", "tstzrange", "daterange" -> + Optional.of(dataType.toUpperCase(Locale.ROOT)); + case "enum" -> Optional.of(escape(buildEnumTypeName(table, column))); + case "set" -> Optional.of("TEXT"); + default -> Optional.empty(); + }; + } + + private String formatWithPrecision(String base, ColumnDTO column) { + if (column.getPrecision() == null) { + return base; + } + StringBuilder sb = new StringBuilder(base).append('(').append(column.getPrecision()); + if (column.getScale() != null) { + sb.append(',').append(column.getScale()); + } + return sb.append(')').toString(); + } + + private StringBuilder appendScale(String base, ColumnDTO column) { + StringBuilder sb = new StringBuilder(base); + if (column.getScale() != null) { + sb.append('(').append(column.getScale()).append(')'); + } + return sb; + } + + private String buildEnumTypeName(TableDTO table, ColumnDTO column) { + return "enum_" + table.getTableName() + "_" + column.getColumnName(); + } + + private String genCreateEnumTypeSql(String typeName, List values) { + String vals = values.stream() + .map(v -> "'" + v.replace("'", "''") + "'") + .collect(Collectors.joining(", ")); + return "CREATE TYPE " + escape(typeName) + " AS ENUM (" + vals + ");"; + } + + /* -------- Indexes -------- */ + + private void appendIndexes(TableDTO table, StringBuilder out) { + if (table.getIndexes() == null) { + return; + } + + String tableEscaped = escape(table.getTableName()); + + for (IndexDTO index : table.getIndexes()) { + String indexName = index.getIndexName(); + if (indexName == null || indexName.isBlank()) { + String colsForName = String.join("_", index.getColumnNames()).replaceAll("[`()\\s]", ""); + indexName = "idx_" + table.getTableName() + "_" + colsForName; + } + + out.append("CREATE "); + if (Boolean.TRUE.equals(index.getUnique())) { + out.append("UNIQUE "); + } + out.append("INDEX ").append(escape(indexName)); + + String cols = index.getColumnNames().stream() + .map(this::renderIndexKey) + .collect(Collectors.joining(", ")); + + if (index.getIndexType() != null && !index.getIndexType().isBlank()) { + out.append(" ON ").append(tableEscaped) + .append(" USING ").append(index.getIndexType().toUpperCase(Locale.ROOT)) + .append(" (").append(cols).append(");\n"); + } else { + out.append(" ON ").append(tableEscaped) + .append(" (").append(cols).append(");\n"); + } + } + out.append('\n'); + } + + /* -------- Comments -------- */ + + private void appendComments(TableDTO table, StringBuilder out) { + String tableEscaped = escape(table.getTableName()); + + if (table.getComment() != null && !table.getComment().isBlank()) { + out.append("COMMENT ON TABLE ").append(tableEscaped) + .append(" IS '").append(escapeSqlLiteral(table.getComment())).append("';\n"); + } + + for (ColumnDTO column : safeList(table.getColumns())) { + if (column.getComment() == null || column.getComment().isBlank()) { + continue; + } + out.append("COMMENT ON COLUMN ").append(tableEscaped).append('.') + .append(escape(column.getColumnName())) + .append(" IS '").append(escapeSqlLiteral(column.getComment())).append("';\n"); + } + out.append('\n'); + } + + /* -------- Foreign keys -------- */ + + private void appendForeignKeys(TableDTO table, StringBuilder out) { + String tableEscaped = escape(table.getTableName()); + Set signatures = new HashSet<>(); + + for (ForeignKeyDTO fk : safeList(table.getForeignKeys())) { + boolean isForeignKeyValid = fk.getColumnNames().stream() + .allMatch(cn -> table.getColumns().stream() + .anyMatch(col -> col.getColumnName().equals(cn))); + if (!isForeignKeyValid) { + continue; + } + + String signature = fk.getColumnNames() + + "->" + + fk.getReferencedTable() + + "." + + fk.getReferencedColumnNames(); + if (!signatures.add(signature)) { + continue; + } + + String fkName = "fk_" + table.getTableName() + "_" + fk.getColumnNames().get(0) + + "_" + fk.getReferencedTable(); + + String localCols = fk.getColumnNames().stream().map(this::escape).collect(Collectors.joining(", ")); + String refCols = fk.getReferencedColumnNames().stream().map(this::escape).collect(Collectors.joining(", ")); + + out.append("ALTER TABLE ").append(tableEscaped) + .append(" ADD CONSTRAINT ").append(escape(fkName)) + .append(" FOREIGN KEY (").append(localCols).append(")") + .append(" REFERENCES ").append(escape(fk.getReferencedTable())) + .append("(").append(refCols).append(")"); + + if (fk.getOnUpdateAction() != null) { + out.append(" ON UPDATE ").append(fk.getOnUpdateAction()); + } + if (fk.getOnDeleteAction() != null) { + out.append(" ON DELETE ").append(fk.getOnDeleteAction()); + } + + out.append(";\n"); + } + } + + /* -------- Helpers -------- */ + + private List safeList(List list) { + return list == null ? List.of() : list; + } + + private String escape(String identifier) { + return "\"" + identifier.replace("\"", "\"\"") + "\""; + } + + private String escapeSqlLiteral(String text) { + return text.replace("'", "''"); + } + + private String formatDefaultValue(String value) { + if (value == null) { + return "NULL"; + } + if (value.matches("^[a-zA-Z_]+\\(.*\\)$")) { + return value; + } + try { + Double.parseDouble(value); + return value; + } catch (NumberFormatException ignore) { + // fall through + } + return "'" + escapeSqlLiteral(value) + "'"; + } + + private boolean isExpression(String text) { + if (text == null) { + return false; + } + String trimmed = text.trim(); + if (trimmed.startsWith("`") && trimmed.endsWith("`") && trimmed.length() >= 2) { + return true; + } + return trimmed.contains("(") || trimmed.contains(")") || trimmed.contains(" ") + || trimmed.contains("+") || trimmed.contains("-") || trimmed.contains("::"); + } + + private String renderIndexKey(String raw) { + if (raw == null) { + return ""; + } + String value = raw.trim(); + if (value.startsWith("`") && value.endsWith("`") && value.length() >= 2) { + return value.substring(1, value.length() - 1); + } + return isExpression(value) ? value : escape(value); + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/dialect/SqlServerSQLGenerator.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/dialect/SqlServerSQLGenerator.java new file mode 100644 index 000000000..0a5984def --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/dialect/SqlServerSQLGenerator.java @@ -0,0 +1,450 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.sql.dialect; + +import org.opengauss.admin.plugin.dto.ColumnDTO; +import org.opengauss.admin.plugin.dto.ForeignKeyDTO; +import org.opengauss.admin.plugin.dto.IndexDTO; +import org.opengauss.admin.plugin.dto.TableDTO; +import org.opengauss.admin.plugin.sql.core.SqlGenerator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * SQL Server implementation of {@link SqlGenerator}. + * + * @version 1.0.0 + * @since 2025-09-07 + */ +public class SqlServerSQLGenerator implements SqlGenerator { + private static final int MAX_IDENTIFIER_LENGTH = 128; + private static final Pattern PRECISION_PATTERN = Pattern.compile("\\((\\d+)\\)"); + private static final String LINE_SEPARATOR = System.lineSeparator(); + private static final String COLUMN_INDENT = " "; + + @Override + public String generate(List tables) { + StringBuilder builder = new StringBuilder(); + for (TableDTO table : safeList(tables)) { + builder.append(buildTableSql(table)); + } + return builder.toString(); + } + + private String buildTableSql(TableDTO table) { + StringBuilder builder = new StringBuilder(); + String escapedTableName = escapeIdentifier(table.getTableName()); + + List> primaryKeys = collectPrimaryKeys(table); + Set pkColumns = extractPrimaryKeyColumns(primaryKeys); + List extendedProperties = new ArrayList<>(); + + builder.append("CREATE TABLE ") + .append(escapedTableName) + .append(" (") + .append(LINE_SEPARATOR) + .append(String.join("," + LINE_SEPARATOR, + buildColumnDefinitions(table, pkColumns, escapedTableName, extendedProperties))); + appendPrimaryKey(builder, primaryKeys, table.getTableName()); + builder.append(LINE_SEPARATOR) + .append(");") + .append(LINE_SEPARATOR) + .append(LINE_SEPARATOR); + + appendIndexes(builder, table, escapedTableName); + appendTableComment(table, escapedTableName, extendedProperties); + appendForeignKeys(builder, table, escapedTableName); + appendExtendedProperties(builder, extendedProperties); + + return builder.toString(); + } + + private List buildColumnDefinitions(TableDTO table, Set pkColumns, String escapedTableName, + List extendedProperties) { + List definitions = new ArrayList<>(); + for (ColumnDTO column : safeList(table.getColumns())) { + definitions.add(buildColumnDefinition(column, pkColumns)); + if (column.getComment() != null && !column.getComment().isBlank()) { + extendedProperties.add(addExtendedProperty(escapedTableName, column.getComment(), + "COLUMN", escapeIdentifier(column.getColumnName()))); + } + } + return definitions; + } + + private String buildColumnDefinition(ColumnDTO column, Set pkColumns) { + StringBuilder builder = new StringBuilder(); + String columnName = escapeIdentifier(column.getColumnName()); + builder.append(COLUMN_INDENT) + .append(columnName) + .append(' ') + .append(buildSqlServerDataType(column)); + + if (pkColumns.contains(column.getColumnName()) || Boolean.FALSE.equals(column.getNullable())) { + builder.append(" NOT NULL"); + } else { + builder.append(" NULL"); + } + + if (Boolean.TRUE.equals(column.getAutoIncrement())) { + builder.append(" IDENTITY(1,1)"); + } + + if (column.getDefaultValue() != null && !column.getDefaultValue().isBlank()) { + builder.append(" DEFAULT ") + .append(formatSqlServerDefaultValue(column.getDefaultValue(), column.getDataType())); + } + + return builder.toString(); + } + + private void appendPrimaryKey(StringBuilder builder, List> primaryKeys, String tableName) { + if (primaryKeys.isEmpty()) { + return; + } + List keyGroup = primaryKeys.get(0); + String pkColumns = keyGroup.stream() + .map(this::expressOrEscape) + .collect(Collectors.joining(", ")); + String pkName = normalizeConstraintName("PK_" + tableName); + builder.append(LINE_SEPARATOR) + .append(",") + .append(LINE_SEPARATOR) + .append(COLUMN_INDENT) + .append("CONSTRAINT ") + .append(pkName) + .append(" PRIMARY KEY (") + .append(pkColumns) + .append(")"); + } + + private void appendIndexes(StringBuilder builder, TableDTO table, String escapedTableName) { + for (IndexDTO index : safeList(table.getIndexes())) { + if (Boolean.TRUE.equals(index.getPk())) { + continue; + } + String indexNamePart = index.getIndexName(); + if (indexNamePart == null || indexNamePart.isBlank()) { + indexNamePart = String.join("_", safeList(index.getColumnNames())); + } + String indexName = normalizeConstraintName("IDX_" + table.getTableName() + "_" + indexNamePart); + String columns = safeList(index.getColumnNames()).stream() + .map(this::expressOrEscape) + .collect(Collectors.joining(", ")); + + builder.append("CREATE "); + if (Boolean.TRUE.equals(index.getUnique())) { + builder.append("UNIQUE "); + } + builder.append("INDEX ") + .append(indexName) + .append(" ON ") + .append(escapedTableName) + .append(" (") + .append(columns) + .append(");") + .append(LINE_SEPARATOR); + + if (index.getComment() != null && !index.getComment().isBlank()) { + builder.append(addExtendedProperty(escapedTableName, index.getComment(), "INDEX", indexName)) + .append(LINE_SEPARATOR); + } + builder.append(LINE_SEPARATOR); + } + } + + private void appendTableComment(TableDTO table, String escapedTableName, List extendedProperties) { + if (table.getComment() != null && !table.getComment().isBlank()) { + extendedProperties.add(addExtendedProperty(escapedTableName, table.getComment(), null, null)); + } + } + + private void appendExtendedProperties(StringBuilder builder, List extendedProperties) { + for (String property : extendedProperties) { + builder.append(property) + .append(LINE_SEPARATOR); + } + if (!extendedProperties.isEmpty()) { + builder.append(LINE_SEPARATOR); + } + } + + private void appendForeignKeys(StringBuilder builder, TableDTO table, String escapedTableName) { + for (ForeignKeyDTO fk : safeList(table.getForeignKeys())) { + if (!isValidForeignKey(table, fk)) { + continue; + } + String fkNameSeed = "FK_" + table.getTableName() + "_" + fk.getReferencedTable(); + String constraintName = normalizeConstraintName(fkNameSeed); + String localColumns = safeList(fk.getColumnNames()).stream() + .map(this::expressOrEscape) + .collect(Collectors.joining(", ")); + String referencedColumns = safeList(fk.getReferencedColumnNames()).stream() + .map(this::expressOrEscape) + .collect(Collectors.joining(", ")); + + builder.append("ALTER TABLE ") + .append(escapedTableName) + .append(" ADD CONSTRAINT ") + .append(constraintName) + .append(" FOREIGN KEY (") + .append(localColumns) + .append(") REFERENCES ") + .append(escapeIdentifier(fk.getReferencedTable())) + .append(" (") + .append(referencedColumns) + .append(")"); + + if (fk.getOnUpdateAction() != null) { + builder.append(" ON UPDATE ") + .append(mapSqlServerReferentialAction(fk.getOnUpdateAction())); + } + if (fk.getOnDeleteAction() != null) { + builder.append(" ON DELETE ") + .append(mapSqlServerReferentialAction(fk.getOnDeleteAction())); + } + builder.append(";") + .append(LINE_SEPARATOR) + .append(LINE_SEPARATOR); + } + } + + private List> collectPrimaryKeys(TableDTO table) { + List> keys = new ArrayList<>(); + if (table.getPrimaryKeys() != null) { + keys.addAll(table.getPrimaryKeys()); + } + for (IndexDTO index : safeList(table.getIndexes())) { + if (Boolean.TRUE.equals(index.getPk())) { + keys.add(new ArrayList<>(safeList(index.getColumnNames()))); + } + } + return keys; + } + + private Set extractPrimaryKeyColumns(List> primaryKeys) { + Set columns = new HashSet<>(); + for (List group : primaryKeys) { + for (String value : group) { + if (!value.contains("`")) { + columns.add(value); + } + } + } + return columns; + } + + private boolean isValidForeignKey(TableDTO table, ForeignKeyDTO fk) { + if (fk == null + || safeList(fk.getColumnNames()).isEmpty() + || safeList(fk.getReferencedColumnNames()).isEmpty()) { + return false; + } + Set columnNames = safeList(table.getColumns()).stream() + .map(ColumnDTO::getColumnName) + .collect(Collectors.toSet()); + return columnNames.containsAll(fk.getColumnNames()); + } + + private String expressOrEscape(String value) { + if (value == null) { + return ""; + } + if (value.startsWith("`") && value.endsWith("`")) { + return value.substring(1, value.length() - 1); + } + return escapeIdentifier(value); + } + + private static String addExtendedProperty(String tableName, String comment, String level2Type, String level2Name) { + String unescapedTableName = tableName.replace("[", "").replace("]", ""); + String unescapedLevel2Name = level2Name != null ? level2Name.replace("[", "").replace("]", "") : null; + String baseSql = "EXEC sp_addextendedproperty @name = N'MS_Description', @value = N'%s', " + + "@level0type = N'SCHEMA', @level0name = N'dbo', @level1type = N'TABLE', " + + "@level1name = N'%s'%s;"; + String level2Part = ""; + if (level2Type != null && unescapedLevel2Name != null) { + level2Part = String.format(", @level2type = N'%s', @level2name = N'%s'", level2Type, unescapedLevel2Name); + } + return String.format(baseSql, comment.replace("'", "''"), unescapedTableName, level2Part); + } + + private String normalizeConstraintName(String name) { + if (name == null) { + return ""; + } + String normalized = name.replaceAll("[^a-zA-Z0-9_]", "_"); + if (normalized.length() > MAX_IDENTIFIER_LENGTH) { + normalized = normalized.substring(0, MAX_IDENTIFIER_LENGTH); + } + return escapeIdentifier(normalized); + } + + private String escapeIdentifier(String identifier) { + if (identifier == null) { + return "[]"; + } + return "[" + identifier.replace("]", "]]") + "]"; + } + + private String buildSqlServerDataType(ColumnDTO column) { + String type = column.getDataType().toLowerCase(Locale.ROOT); + if ("timestamp".equals(type)) { + return parsePrecisionType("datetime2", column.getDataType()); + } + if ("double".equals(type)) { + return "FLOAT"; + } + if (type.contains("bool")) { + return "BIT"; + } + String charType = mapCharOrText(type, column).orElse(null); + if (charType != null) { + return charType; + } + String intType = mapInteger(type).orElse(null); + if (intType != null) { + return intType; + } + String numeric = mapNumeric(type, column).orElse(null); + if (numeric != null) { + return numeric; + } + String temporal = mapTemporal(type, column).orElse(null); + if (temporal != null) { + return temporal; + } + return type.toUpperCase(Locale.ROOT); + } + + private Optional mapCharOrText(String type, ColumnDTO column) { + if (type.contains("varchar") || type.contains("char")) { + Integer length = column.getLength(); + if (length == null || length <= 0 || length > 8000) { + return Optional.of("VARCHAR(MAX)"); + } + return Optional.of("VARCHAR(" + length + ")"); + } + if (type.contains("text")) { + return Optional.of("VARCHAR(MAX)"); + } + return Optional.empty(); + } + + private Optional mapInteger(String type) { + if (type.contains("int")) { + if (type.contains("bigint")) { + return Optional.of("BIGINT"); + } + if (type.contains("smallint")) { + return Optional.of("SMALLINT"); + } + if (type.contains("tinyint")) { + return Optional.of("TINYINT"); + } + return Optional.of("INT"); + } + return Optional.empty(); + } + + private Optional mapNumeric(String type, ColumnDTO column) { + if (type.matches("^(decimal|numeric|float|real)$")) { + StringBuilder numeric = new StringBuilder(type.toUpperCase(Locale.ROOT)); + if (column.getLength() != null && column.getPrecision() != null) { + numeric.append('(') + .append(column.getLength()) + .append(", ") + .append(column.getPrecision()) + .append(')'); + } + return Optional.of(numeric.toString()); + } + return Optional.empty(); + } + + private Optional mapTemporal(String type, ColumnDTO column) { + if (type.contains("date")) { + return Optional.of("DATE"); + } + if (type.contains("time")) { + return Optional.of(parsePrecisionType("time", column.getDataType())); + } + if (type.contains("datetime")) { + return Optional.of(parsePrecisionType("datetime2", column.getDataType())); + } + return Optional.empty(); + } + + private String parsePrecisionType(String targetType, String originalType) { + if (originalType == null) { + return targetType; + } + Matcher matcher = PRECISION_PATTERN.matcher(originalType); + if (matcher.find()) { + return targetType + "(" + matcher.group(1) + ")"; + } + return targetType; + } + + private String formatSqlServerDefaultValue(String value, String dataType) { + if (value == null || value.isBlank() || "null".equalsIgnoreCase(value)) { + return "NULL"; + } + String lowerValue = value.toLowerCase(Locale.ROOT); + if (lowerValue.startsWith("`") && lowerValue.endsWith("`")) { + return "(" + lowerValue.substring(1, lowerValue.length() - 1) + ")"; + } + String lowerType = dataType.toLowerCase(Locale.ROOT); + if ("current_timestamp".equals(lowerValue) || "now()".equals(lowerValue)) { + return "(GETDATE())"; + } + if (lowerType.contains("bool")) { + return "true".equals(lowerValue) || "1".equals(lowerValue) ? "1" : "0"; + } + if (lowerType.matches("^(int|bigint|smallint|tinyint|decimal|numeric|float|double|real)")) { + if (value.matches("^[-+]?\\d*\\.?\\d+$")) { + return value; + } + } + return "N'" + value.replace("'", "''") + "'"; + } + + private String mapSqlServerReferentialAction(String action) { + if (action == null) { + return "NO ACTION"; + } + return switch (action.toUpperCase(Locale.ROOT)) { + case "CASCADE" -> "CASCADE"; + case "SET NULL" -> "SET NULL"; + case "SET DEFAULT" -> "SET DEFAULT"; + case "RESTRICT" -> "NO ACTION"; + default -> "NO ACTION"; + }; + } + + private List safeList(List source) { + return source == null ? Collections.emptyList() : source; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/router/SqlGeneratorRouter.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/router/SqlGeneratorRouter.java new file mode 100644 index 000000000..b4a931d93 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/sql/router/SqlGeneratorRouter.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.sql.router; + +import org.opengauss.admin.plugin.dto.TableDTO; +import org.opengauss.admin.plugin.enums.DatabaseDialect; +import org.opengauss.admin.plugin.sql.core.SqlGenerator; +import org.opengauss.admin.plugin.sql.dialect.MySQLGenerator; +import org.opengauss.admin.plugin.sql.dialect.OpenGaussSqlGenerator; +import org.opengauss.admin.plugin.sql.dialect.PostgreSQLGenerator; +import org.opengauss.admin.plugin.sql.dialect.SqlServerSQLGenerator; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +/** + * Central dispatcher that selects the proper SQL generator based on the database dialect. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +public class SqlGeneratorRouter { + // Map keyed by DatabaseDialect; EnumMap is optimized for enum keys + private static final Map GENERATOR_MAP = new EnumMap<>(DatabaseDialect.class); + + static { + GENERATOR_MAP.put(DatabaseDialect.MYSQL, new MySQLGenerator()); + GENERATOR_MAP.put(DatabaseDialect.POSTGRESQL, new PostgreSQLGenerator()); + GENERATOR_MAP.put(DatabaseDialect.MSSQL, new SqlServerSQLGenerator()); + GENERATOR_MAP.put(DatabaseDialect.OPENGAUSS, new OpenGaussSqlGenerator()); + } + + /** + * Generate SQL statements using the generator that matches the dialect. + * + * @param tables table structure definitions + * @param dialect database dialect + * @return SQL string produced by the dialect-specific generator + */ + public static String generateSQL(List tables, DatabaseDialect dialect) { + SqlGenerator generator = GENERATOR_MAP.get(dialect); + + if (generator == null) { + // This should not occur if every dialect is initialized above, but keep the guard for new enums. + throw new UnsupportedOperationException( + "No SQL generator is configured for dialect " + dialect.name() + '.'); + } + + return generator.generate(tables); + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/translate/DbmlToDtoTranslator.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/translate/DbmlToDtoTranslator.java new file mode 100644 index 000000000..a9e7418f6 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/translate/DbmlToDtoTranslator.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.translate; + +import com.wn.dbml.model.Column; +import com.wn.dbml.model.ColumnSetting; +import com.wn.dbml.model.Database; +import com.wn.dbml.model.Index; +import com.wn.dbml.model.IndexSetting; +import com.wn.dbml.model.Relationship; +import com.wn.dbml.model.RelationshipSetting; +import com.wn.dbml.model.Table; + +import org.opengauss.admin.plugin.dto.ColumnDTO; +import org.opengauss.admin.plugin.dto.ForeignKeyDTO; +import org.opengauss.admin.plugin.dto.IndexDTO; +import org.opengauss.admin.plugin.dto.TableDTO; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Map dbml-java models to internal DTOs so dialect-specific SQL can be generated later. + * + * @version 1.0.0 + * @since 2024-05-07 + */ +public class DbmlToDtoTranslator { + /* ----- 1. Constants required for parsing ----- */ + private static final Pattern TYPE_WITH_PARAMS_PATTERN = + Pattern.compile("([a-zA-Z ]+)\\s*\\(\\s*(\\d+)\\s*(?:,\\s*(\\d+))?\\s*\\)"); + + /** MySQL keywords that imply unsigned semantics */ + private static final Set UNSIGNED_FLAGS = Set.of("unsigned", "zerofill"); + + /** Normalise MySQL variations to canonical names for easier downstream processing */ + private static final Map MYSQL_CANONICAL = Map.ofEntries( + Map.entry("character varying", "varchar"), + Map.entry("double precision", "double"), + Map.entry("timestamp without time zone", "timestamp"), + Map.entry("timestamp with time zone", "timestamptz"), + Map.entry("integer", "int"), // DBML may emit integer + Map.entry("mediumint", "mediumint") // Keep as-is; dialect generators adjust later + ); + + /* ----- 2. Entry point ----- */ + + /** + * Translate a DBML database model into a list of table DTOs ready for SQL generation. + * + * @param database Database parsed by dbml-java + * @return list of TableDTO instances + */ + public static List map(Database database) { + // 1) Map every table to a TableDTO + List tableDTOs = database.getSchemas().stream() + .flatMap(schema -> schema.getTables().stream()) + .map(DbmlToDtoTranslator::mapTable) + .collect(Collectors.toList()); + + // 2) Build a lookup map to attach foreign keys later + Map tableDtoMap = tableDTOs.stream() + .collect(Collectors.toMap(TableDTO::getTableName, Function.identity())); + + // 3) Process foreign keys + mapForeignKeys(database, tableDtoMap); + + return tableDTOs; + } + + /* ----- 3. Table / Column / Index mapping ----- */ + + private static TableDTO mapTable(Table table) { + TableDTO dto = new TableDTO(table.getName()); + + if (table.getNote() != null) { + dto.setComment(table.getNote().getValue()); + } + + table.getColumns().forEach(col -> dto.getColumns().add(mapColumn(col))); + List inlinePrimaryKeys = table.getColumns().stream() + .filter(col -> col.getSettings().containsKey(ColumnSetting.PRIMARY_KEY)) + .map(Column::getName) + .collect(Collectors.toList()); + if (!inlinePrimaryKeys.isEmpty()) { + dto.getPrimaryKeys().add(inlinePrimaryKeys); + } + table.getIndexes().forEach(idx -> dto.getIndexes().add(mapIndex(idx))); + + return dto; + } + + private static ColumnDTO mapColumn(Column column) { + ColumnDTO dto = new ColumnDTO(); + dto.setColumnName(column.getName()); + + // Parse data type, unsigned flag, and enum information + parseDataType(column.getType(), dto); + + /* Constraints */ + dto.setPrimaryKey(column.getSettings().containsKey(ColumnSetting.PRIMARY_KEY)); + dto.setUnique(column.getSettings().containsKey(ColumnSetting.UNIQUE)); + dto.setNullable(!column.getSettings().containsKey(ColumnSetting.NOT_NULL)); + dto.setAutoIncrement(column.getSettings().containsKey(ColumnSetting.INCREMENT)); + dto.setDefaultValue(column.getSettings().get(ColumnSetting.DEFAULT)); + + if (column.getNote() != null) { + dto.setComment(column.getNote().getValue()); + } + return dto; + } + + private static IndexDTO mapIndex(Index index) { + IndexDTO dto = new IndexDTO(); + dto.setIndexName(index.getSettings().get(IndexSetting.NAME)); + dto.setPk(index.getSettings().containsKey(IndexSetting.PK)); + dto.setUnique(dto.getPk() || index.getSettings().containsKey(IndexSetting.UNIQUE)); + dto.setIndexType(index.getSettings().get(IndexSetting.TYPE)); + + if (index.getNote() != null) { + dto.setComment(index.getNote().getValue()); + } + + List cols = index.getColumns().keySet().stream() + .map(n -> n.replace("`", "")) + .collect(Collectors.toList()); + dto.setColumnNames(cols); + return dto; + } + + /* ----- 4. Foreign keys ----- */ + + private static void mapForeignKeys(Database database, Map tableDtoMap) { + for (Relationship rel : database.getRelationships()) { + if (rel.getFrom().isEmpty() || rel.getTo().isEmpty()) { + continue; + } + Table fromTableModel = rel.getFrom().get(0).getTable(); + Table toTableModel = rel.getTo().get(0).getTable(); + + TableDTO fromDto = tableDtoMap.get(fromTableModel.getName()); + if (fromDto == null) { + continue; + } + + ForeignKeyDTO fk = new ForeignKeyDTO(); + fk.setReferencedTable(toTableModel.getName()); + + fk.setColumnNames(rel.getFrom().stream().map(Column::getName).collect(Collectors.toList())); + fk.setReferencedColumnNames(rel.getTo().stream().map(Column::getName).collect(Collectors.toList())); + + fk.setOnUpdateAction(rel.getSettings().get(RelationshipSetting.UPDATE)); + fk.setOnDeleteAction(rel.getSettings().get(RelationshipSetting.DELETE)); + + fromDto.getForeignKeys().add(fk); + } + } + + /* ----- 5. Core: type parsing ----- */ + + private static void parseDataType(String rawType, ColumnDTO dto) { + /* 0) Normalise format: lowercase and collapse extra spaces */ + String lower = rawType.toLowerCase(Locale.ROOT) + .trim() + .replaceAll("\\s{2,}", " "); + + /* 1) Detect unsigned / zerofill flags */ + for (String flag : UNSIGNED_FLAGS) { + if (lower.contains(flag)) { + dto.setUnsigned(true); + lower = lower.replace(flag, ""); // Remove keyword before parsing + } + } + lower = lower.trim(); + + /* 2) Handle ENUM / SET first */ + if (lower.startsWith("enum(") || lower.startsWith("set(")) { + dto.setDataType(lower.startsWith("enum(") ? "enum" : "set"); + + int l = lower.indexOf('('); + int r = lower.lastIndexOf(')'); + if (l > 0 && r > l) { + String inside = lower.substring(l + 1, r); + List vals = Arrays.stream(inside.split(",")) + .map(s -> s.trim().replaceAll("^'|'$", "")) // Strip surrounding quotes + .collect(Collectors.toList()); + dto.setEnumValues(vals); + } + return; // Enum type handled + } + + /* 3) Parse parameterised types such as varchar(255) / decimal(10,2) */ + Matcher m = TYPE_WITH_PARAMS_PATTERN.matcher(lower); + if (m.find()) { + String typeName = m.group(1).trim(); + int p = Integer.parseInt(m.group(2)); + Integer s = m.group(3) != null ? Integer.parseInt(m.group(3)) : null; + + // Decide between length or precision based on the type + if ("char".equals(typeName) || typeName.contains("char") + || "binary".equals(typeName) || "varbinary".equals(typeName) + || "bit".equals(typeName)) { + dto.setLength(p); + } else { // Treat as numeric + dto.setPrecision(p); + dto.setScale(s); + } + + lower = typeName; // Remove (p,s) before canonicalisation + } + + /* 4) Convert synonyms to canonical names */ + String canonical = MYSQL_CANONICAL.getOrDefault(lower, lower); + dto.setDataType(canonical); + + /* 5) float precision thresholds: real vs double */ + if ("float".equals(canonical) && dto.getScale() == null && dto.getPrecision() != null) { + int p = dto.getPrecision(); + if (p >= 25) { + dto.setDataType("double"); + } else { + dto.setDataType("real"); + } + } + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/translate/SqlToDbmlTranslator.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/translate/SqlToDbmlTranslator.java new file mode 100644 index 000000000..d9a3a7c4a --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/translate/SqlToDbmlTranslator.java @@ -0,0 +1,445 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.translate; + +import org.opengauss.admin.plugin.dto.ColumnDTO; +import org.opengauss.admin.plugin.dto.ForeignKeyDTO; +import org.opengauss.admin.plugin.dto.IndexDTO; +import org.opengauss.admin.plugin.dto.TableDTO; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Convert dialect-agnostic DTOs into DBML. + * Highlights: + * - Table and column names use bare identifiers (no double quotes) + * - Single-column PK (e.g., serial/bigserial) -> column-level [pk, increment] + * - Composite PK -> indexes { (a, b) [pk] } + * - Index rules: + * - Single column: col [name: 'idx'] + * - Unique: ("col") [unique, name: '...'] + * - Expression: (`lower(title)`) [name: '...'] + * - Comments: table note: '...'; column [note: '...'] + * - Default values: numbers literal; strings quoted; expressions wrapped in backticks. + * + * @since 2025-01-07 + */ +public class SqlToDbmlTranslator { + private static final Pattern IDENTIFIER = Pattern.compile("^[A-Za-z_][A-Za-z0-9_]*$"); + + /** + * Transform the provided table definitions into DBML text. + * + * @param tables logical schema description to render + * @return DBML string for the supplied tables + */ + public static String generate(List tables) { + StringBuilder sb = new StringBuilder(8192); + List relationships = new ArrayList<>(); + + for (TableDTO table : safeList(tables)) { + appendTableDefinition(sb, relationships, table); + } + + appendRelationships(sb, relationships); + return sb.toString(); + } + + private static void appendTableDefinition(StringBuilder sb, List relationships, TableDTO table) { + String tableName = bare(table.getTableName()); + sb.append("Table ").append(tableName).append(" {\n"); + + List> primaryKeySets = collectPrimaryKeySets(table); + Set singleColumnPks = extractSingleColumnPkNames(primaryKeySets); + + appendColumns(sb, table, singleColumnPks); + appendTableNote(sb, table); + appendIndexSection(sb, table, primaryKeySets); + + sb.append("}\n\n"); + + relationships.addAll(buildRelationships(tableName, table)); + } + + private static List> collectPrimaryKeySets(TableDTO table) { + List> primaryKeys = new ArrayList<>(); + for (List pk : safeList(table.getPrimaryKeys())) { + List normalized = new ArrayList<>(); + for (String column : safeList(pk)) { + String columnName = bare(column); + if (!columnName.isEmpty()) { + normalized.add(columnName); + } + } + if (!normalized.isEmpty()) { + primaryKeys.add(normalized); + } + } + return primaryKeys; + } + + private static Set extractSingleColumnPkNames(List> primaryKeySets) { + Set singleColumnPks = new HashSet<>(); + for (List pk : primaryKeySets) { + if (pk.size() == 1) { + singleColumnPks.add(pk.get(0)); + } + } + return singleColumnPks; + } + + private static void appendColumns(StringBuilder sb, TableDTO table, Set singleColumnPks) { + for (ColumnDTO column : safeList(table.getColumns())) { + String columnName = bare(column.getColumnName()); + String dataType = formatDataType(column); + List attributes = buildColumnAttributes(column, columnName, singleColumnPks); + + sb.append(" ") + .append(colQuotedIfNeeded(columnName)) + .append(" ") + .append(dataType); + + if (!attributes.isEmpty()) { + sb.append(" [").append(String.join(", ", attributes)).append("]"); + } + sb.append("\n"); + } + } + + private static List buildColumnAttributes( + ColumnDTO column, + String columnName, + Set singleColumnPks) { + List attributes = new ArrayList<>(); + boolean isColumnPk = Boolean.TRUE.equals(column.getPrimaryKey()) || singleColumnPks.contains(columnName); + if (isColumnPk) { + attributes.add("pk"); + } + if (Boolean.TRUE.equals(column.getAutoIncrement())) { + attributes.add("increment"); + } + if (Boolean.TRUE.equals(column.getUnique())) { + attributes.add("unique"); + } + if (Boolean.FALSE.equals(column.getNullable())) { + attributes.add("not null"); + } + if (StringUtils.hasText(column.getDefaultValue())) { + String normalizedDefault = normalizeDbmlDefault(column.getDefaultValue()); + if (!normalizedDefault.isEmpty()) { + attributes.add("default: " + normalizedDefault); + } + } + if (StringUtils.hasText(column.getComment())) { + attributes.add("note: '" + escapeSingle(column.getComment()) + "'"); + } + return attributes; + } + + private static String bare(String s) { + if (s == null) { + return ""; + } + String value = s.trim(); + if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("`") && value.endsWith("`"))) { + return value.substring(1, value.length() - 1); + } + return value; + } + + private static void appendTableNote(StringBuilder sb, TableDTO table) { + if (StringUtils.hasText(table.getComment())) { + sb.append("\n note: '").append(escapeSingle(table.getComment())).append("'\n"); + } + } + + private static void appendIndexSection(StringBuilder sb, TableDTO table, List> primaryKeySets) { + List> compositePks = new ArrayList<>(); + for (List pk : primaryKeySets) { + if (pk.size() > 1) { + compositePks.add(pk); + } + } + List indexLines = buildIndexLines(table, compositePks); + if (indexLines.isEmpty()) { + return; + } + sb.append("\n indexes {\n"); + for (String line : indexLines) { + sb.append(" ").append(line).append("\n"); + } + sb.append(" }\n"); + } + + private static List buildIndexLines(TableDTO table, List> compositePks) { + List indexLines = new ArrayList<>(); + for (List pk : compositePks) { + indexLines.add("(" + String.join(", ", quoteColumns(pk)) + ") [pk]"); + } + for (IndexDTO index : safeList(table.getIndexes())) { + List columns = safeList(index.getColumnNames()); + if (columns.isEmpty()) { + continue; + } + List renderedColumns = renderIndexColumns(columns); + if (renderedColumns.isEmpty()) { + continue; + } + if (Boolean.TRUE.equals(index.getUnique())) { + indexLines.add(buildUniqueIndexLine(renderedColumns, index)); + } else { + indexLines.add(buildRegularIndexLine(renderedColumns, index)); + } + } + return indexLines; + } + + private static List quoteColumns(List columns) { + List quoted = new ArrayList<>(); + for (String column : columns) { + quoted.add(colQuotedIfNeeded(column)); + } + return quoted; + } + + private static List renderIndexColumns(List columns) { + List rendered = new ArrayList<>(); + for (String raw : columns) { + String normalized = raw == null ? "" : raw.trim(); + if (normalized.isEmpty()) { + continue; + } + if (IDENTIFIER.matcher(normalized).matches()) { + rendered.add(colQuotedIfNeeded(bare(normalized))); + } else { + String tightened = normalized.replaceAll("\\s*\\(\\s*", "(") + .replaceAll("\\s*\\)\\s*", ")"); + rendered.add("`" + tightened + "`"); + } + } + return rendered; + } + + private static String buildUniqueIndexLine(List renderedColumns, IndexDTO index) { + StringBuilder suffix = new StringBuilder("unique"); + String name = stripQuotes(index.getIndexName()); + if (StringUtils.hasText(name)) { + suffix.append(", name: '").append(escapeSingle(name)).append("'"); + } + if (renderedColumns.size() == 1 && isPlainIdentifier(renderedColumns.get(0))) { + return renderedColumns.get(0) + " [" + suffix + "]"; + } + return "(" + String.join(", ", renderedColumns) + ") [" + suffix + "]"; + } + + private static String buildRegularIndexLine(List renderedColumns, IndexDTO index) { + String name = escapeSingle(stripQuotes(index.getIndexName())); + String suffix = "[name: '" + name + "']"; + if (renderedColumns.size() == 1 && isPlainIdentifier(renderedColumns.get(0))) { + return renderedColumns.get(0) + " " + suffix; + } + return "(" + String.join(", ", renderedColumns) + ") " + suffix; + } + + private static boolean isPlainIdentifier(String value) { + String candidate = stripBackticks(value); + return IDENTIFIER.matcher(candidate).matches(); + } + + private static void appendRelationships(StringBuilder sb, List relationships) { + if (relationships.isEmpty()) { + return; + } + for (String relationship : relationships) { + sb.append(relationship).append("\n"); + } + } + + private static List buildRelationships(String tableName, TableDTO table) { + List relationships = new ArrayList<>(); + for (ForeignKeyDTO foreignKey : safeList(table.getForeignKeys())) { + List sourceColumns = toBareNames(foreignKey.getColumnNames()); + List targetColumns = toBareNames(foreignKey.getReferencedColumnNames()); + String referencedTable = bare(foreignKey.getReferencedTable()); + if (sourceColumns.isEmpty() || targetColumns.isEmpty() || !StringUtils.hasText(referencedTable)) { + continue; + } + String sourcePart = formatRelationshipSide(tableName, sourceColumns); + String targetPart = formatRelationshipSide(referencedTable, targetColumns); + relationships.add("Ref: " + sourcePart + " > " + targetPart); + } + return relationships; + } + + private static List toBareNames(List names) { + List results = new ArrayList<>(); + for (String name : safeList(names)) { + String value = bare(name); + if (!value.isEmpty()) { + results.add(value); + } + } + return results; + } + + private static String formatRelationshipSide(String tableName, List columns) { + if (columns.size() == 1) { + return tableName + "." + columns.get(0); + } + return tableName + ".(" + String.join(", ", columns) + ")"; + } + + private static List safeList(List list) { + if (list == null) { + return Collections.emptyList(); + } + return list; + } + + private static String colQuotedIfNeeded(String ident) { + return ident; + } + + private static String stripQuotes(String s) { + if (s == null) { + return ""; + } + String value = s.trim(); + if ((value.startsWith("\"") && value.endsWith("\"")) + || (value.startsWith("`") && value.endsWith("`")) + || (value.startsWith("'") && value.endsWith("'"))) { + return value.substring(1, value.length() - 1); + } + return value; + } + + private static String stripBackticks(String s) { + if (s == null) { + return ""; + } + String value = s.trim(); + if (value.startsWith("`") && value.endsWith("`")) { + return value.substring(1, value.length() - 1); + } + return value; + } + + private static String escapeSingle(String s) { + if (s == null) { + return ""; + } + return s.replace("'", "\\'"); + } + + private static String formatDataType(ColumnDTO column) { + String rawType = column.getDataType() == null ? "varchar" : column.getDataType(); + String baseType = toDbmlType(rawType); + if (column.getEnumValues() != null && !column.getEnumValues().isEmpty()) { + String enums = column.getEnumValues().stream() + .map(value -> "'" + value.replace("'", "\\'") + "'") + .collect(Collectors.joining(", ")); + return baseType + "(" + enums + ")"; + } + if (column.getPrecision() != null) { + if (column.getScale() != null) { + return baseType + "(" + column.getPrecision() + "," + column.getScale() + ")"; + } + return baseType + "(" + column.getPrecision() + ")"; + } + if (column.getLength() != null) { + return baseType + "(" + column.getLength() + ")"; + } + return baseType; + } + + private static String normalizeDbmlDefault(String dv) { + if (dv == null) { + return ""; + } + String value = dv.trim(); + if (value.isEmpty()) { + return value; + } + if (value.startsWith("'") && value.endsWith("'")) { + return value; + } + if (value.matches("^-?\\d+(\\.\\d+)?$")) { + return value; + } + if (value.startsWith("`") && value.endsWith("`")) { + return value; + } + String tightened = value.replaceAll("\\s*\\(\\s*", "(") + .replaceAll("\\s*\\)\\s*", ")"); + return "`" + tightened + "`"; + } + + private static String toDbmlType(String raw) { + if (raw == null) { + return "text"; + } + String normalized = raw.trim().toLowerCase(Locale.ROOT).replaceAll("\\s+", " "); + + if (normalized.matches("^character varying\\s*\\(\\s*\\d+\\s*\\)$")) { + return normalized.replaceFirst("^character varying", "varchar").replace(" ", ""); + } + if (normalized.matches("^character\\s*\\(\\s*\\d+\\s*\\)$")) { + return normalized.replaceFirst("^character", "char").replace(" ", ""); + } + String mapped = mapExactType(normalized); + if (mapped != null) { + return mapped; + } + if (normalized.startsWith("timestamp")) { + return "timestamp"; + } + if (normalized.startsWith("time")) { + return "time"; + } + if (normalized.startsWith("numeric") || normalized.startsWith("decimal")) { + return normalized.replace(" ", ""); + } + if (normalized.startsWith("varchar") || normalized.startsWith("char")) { + return normalized.replace(" ", ""); + } + return normalized.replace(" ", ""); + } + + private static String mapExactType(String normalized) { + return switch (normalized) { + case "character varying" -> "varchar"; + case "character" -> "char"; + case "integer", "int4" -> "int"; + case "bigint", "int8" -> "bigint"; + case "smallint", "int2" -> "int"; + case "double precision" -> "double"; + case "real" -> "float"; + case "boolean", "bool" -> "bool"; + case "text" -> "text"; + case "date" -> "date"; + case "json", "jsonb" -> "json"; + default -> null; + }; + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/util/PlantUMLGenerator.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/util/PlantUMLGenerator.java new file mode 100644 index 000000000..76b46c230 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/util/PlantUMLGenerator.java @@ -0,0 +1,453 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.util; + +import org.opengauss.admin.plugin.dto.ColumnDTO; +import org.opengauss.admin.plugin.dto.ForeignKeyDTO; +import org.opengauss.admin.plugin.dto.IndexDTO; +import org.opengauss.admin.plugin.dto.TableDTO; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Renders table metadata as a PlantUML ERD diagram using IE notation. + * + * @since 2025-01-07 + */ +public final class PlantUMLGenerator { + private record AliasContext( + Map aliasByKey, + Map> primaryKeys, + Map>> uniqueSets, + Map> columns, + Map> foreignKeyColumns) { + + String aliasFor(String tableKey) { + return aliasByKey.get(tableKey); + } + + Set primaryKeysFor(String tableKey) { + return primaryKeys.getOrDefault(tableKey, Collections.emptySet()); + } + + Set> uniqueSetsFor(String tableKey) { + return uniqueSets.getOrDefault(tableKey, Collections.emptySet()); + } + + Map columnsFor(String tableKey) { + return columns.getOrDefault(tableKey, Collections.emptyMap()); + } + + Set foreignKeyColumnsFor(String tableKey) { + return foreignKeyColumns.getOrDefault(tableKey, Collections.emptySet()); + } + } + + /** + * Generate PlantUML entity relationship diagram from table definitions. + * + * @param tables the list of table definitions + * @return PlantUML source code + */ + public static String generate(List tables) { + List safeTables = tables == null ? List.of() : tables; + AliasContext context = buildAliasContext(safeTables); + + StringBuilder sb = new StringBuilder(8192); + appendHeader(sb); + appendEntities(sb, safeTables, context); + appendRelationships(sb, safeTables, context); + sb.append("@enduml\n"); + return sb.toString(); + } + + private static void appendHeader(StringBuilder sb) { + sb.append("@startuml\n"); + sb.append("left to right direction\n"); + sb.append("hide circle\n"); + sb.append("skinparam linetype ortho\n\n"); + } + + private static AliasContext buildAliasContext(List tables) { + Map aliasByKey = new LinkedHashMap<>(); + int counter = 1; + for (TableDTO table : tables) { + String key = tableKey(table); + if (!aliasByKey.containsKey(key)) { + aliasByKey.put(key, "T" + counter++); + } + } + + Map> primaryKeys = new HashMap<>(); + Map>> uniqueSets = new HashMap<>(); + Map> columnsByTable = new HashMap<>(); + Map> foreignKeyColumns = new HashMap<>(); + + for (TableDTO table : tables) { + String key = tableKey(table); + primaryKeys.put(key, calcPrimaryKeyColumns(table)); + uniqueSets.put(key, calcUniqueSets(table)); + columnsByTable.put(key, toColumnMap(table)); + foreignKeyColumns.put(key, collectForeignKeyColumns(table)); + } + + return new AliasContext(aliasByKey, primaryKeys, uniqueSets, columnsByTable, foreignKeyColumns); + } + + private static void appendEntities(StringBuilder sb, List tables, AliasContext context) { + for (TableDTO table : tables) { + String key = tableKey(table); + String alias = context.aliasFor(key); + if (alias == null) { + continue; + } + + Set primaryColumns = context.primaryKeysFor(key); + Set foreignColumns = context.foreignKeyColumnsFor(key); + + sb.append("entity \"").append(escape(key)).append("\" as ").append(alias).append(" {\n"); + for (ColumnDTO column : safe(table.getColumns())) { + appendColumn(sb, column, primaryColumns, foreignColumns); + } + sb.append("}\n\n"); + } + } + + private static void appendColumn( + StringBuilder sb, + ColumnDTO column, + Set primaryColumns, + Set foreignColumns) { + boolean isPrimary = primaryColumns.contains(column.getColumnName()) + || Boolean.TRUE.equals(column.getPrimaryKey()); + boolean isRequired = isPrimary || Boolean.FALSE.equals(column.getNullable()); + boolean isUniqueColumn = Boolean.TRUE.equals(column.getUnique()) && !isPrimary; + boolean isForeign = foreignColumns.contains(column.getColumnName()); + + sb.append(" ").append(isRequired ? "*" : " "); + sb.append(escape(column.getColumnName())).append(" : ").append(formatType(column)); + + List stereotypes = new ArrayList<>(); + if (isPrimary) { + stereotypes.add("PK"); + } + if (isUniqueColumn) { + stereotypes.add("UQ"); + } + if (isForeign) { + stereotypes.add("FK"); + } + if (!stereotypes.isEmpty()) { + sb.append(" <<").append(String.join(",", stereotypes)).append(">>"); + } + + if (column.getDefaultValue() != null && !column.getDefaultValue().isBlank()) { + sb.append(" = ").append(formatDefault(column)); + } + if (column.getComment() != null && !column.getComment().isBlank()) { + sb.append(" // ").append(escapeInline(column.getComment())); + } + sb.append('\n'); + } + + private static void appendRelationships(StringBuilder sb, List tables, AliasContext context) { + for (TableDTO child : tables) { + String childKey = tableKey(child); + String childAlias = context.aliasFor(childKey); + if (childAlias == null) { + continue; + } + + Map childColumns = context.columnsFor(childKey); + Set> childUniqueSets = context.uniqueSetsFor(childKey); + Set childPrimary = context.primaryKeysFor(childKey); + + for (ForeignKeyDTO fk : safe(child.getForeignKeys())) { + List fkColumns = safe(fk.getColumnNames()); + if (fkColumns.isEmpty()) { + continue; + } + + String parentKey = normalizeName(nullToEmpty(fk.getReferencedTable())); + String parentAlias = context.aliasFor(parentKey); + if (parentAlias == null) { + continue; + } + + boolean isChildNullable = isAnyNullable(childColumns, fkColumns, childPrimary); + boolean isChildUnique = isUniqueForeignKey(childUniqueSets, childPrimary, childColumns, fkColumns); + + String leftEndpoint = "||"; + String rightEndpoint = calcRightEndpoint(isChildUnique, isChildNullable); + + sb.append(parentAlias).append(' ') + .append(leftEndpoint).append("--").append(rightEndpoint).append(' ') + .append(childAlias); + + String tail = buildForeignKeyTail(fk); + if (!tail.isBlank()) { + sb.append(" : \"").append(escape(tail)).append('"'); + } + sb.append('\n'); + } + } + } + + private static String tableKey(TableDTO table) { + return normalizeName(nullToEmpty(table.getTableName())); + } + + private static String normalizeName(String value) { + return nullToEmpty(value).trim(); + } + + private static Map toColumnMap(TableDTO table) { + Map columns = new HashMap<>(); + for (ColumnDTO column : safe(table.getColumns())) { + columns.put(column.getColumnName(), column); + } + return columns; + } + + private static Set calcPrimaryKeyColumns(TableDTO table) { + Set primary = new LinkedHashSet<>(); + for (List group : safe(table.getPrimaryKeys())) { + primary.addAll(group); + } + for (IndexDTO index : safe(table.getIndexes())) { + if (Boolean.TRUE.equals(index.getPk())) { + primary.addAll(safe(index.getColumnNames())); + } + } + for (ColumnDTO column : safe(table.getColumns())) { + if (Boolean.TRUE.equals(column.getPrimaryKey())) { + primary.add(column.getColumnName()); + } + } + return primary; + } + + private static Set> calcUniqueSets(TableDTO table) { + Set> sets = new HashSet<>(); + for (IndexDTO index : safe(table.getIndexes())) { + if (Boolean.TRUE.equals(index.getUnique()) || Boolean.TRUE.equals(index.getPk())) { + sets.add(new LinkedHashSet<>(safe(index.getColumnNames()))); + } + } + for (List group : safe(table.getPrimaryKeys())) { + if (!group.isEmpty()) { + sets.add(new LinkedHashSet<>(group)); + } + } + return sets; + } + + private static Set collectForeignKeyColumns(TableDTO table) { + Set columns = new HashSet<>(); + for (ForeignKeyDTO fk : safe(table.getForeignKeys())) { + columns.addAll(safe(fk.getColumnNames())); + } + return columns; + } + + /** + * Determine whether any child-side foreign key column is nullable. + * + * @param columns metadata keyed by column name + * @param fkColumns the columns participating in the foreign key + * @param childPkColumns child-side primary key columns + * @return {@code true} if at least one column allows {@code NULL}; {@code false} otherwise + */ + private static boolean isAnyNullable( + Map columns, + List fkColumns, + Set childPkColumns) { + for (String name : fkColumns) { + ColumnDTO column = columns.get(name); + if (column == null) { + return true; + } + if (Boolean.TRUE.equals(column.getPrimaryKey()) + || (childPkColumns != null && childPkColumns.contains(name))) { + continue; + } + if (!Boolean.FALSE.equals(column.getNullable())) { + return true; + } + } + return false; + } + + private static boolean isUniqueForeignKey( + Set> uniqueSets, + Set pkColumns, + Map columns, + List fkColumns) { + Set set = new LinkedHashSet<>(fkColumns); + if (!pkColumns.isEmpty() && pkColumns.equals(set)) { + return true; + } + if (uniqueSets.contains(set)) { + return true; + } + if (set.size() == 1) { + ColumnDTO column = columns.get(set.iterator().next()); + return column != null && Boolean.TRUE.equals(column.getUnique()); + } + return false; + } + + private static String calcRightEndpoint(boolean isUnique, boolean isNullable) { + if (isUnique) { + return isNullable ? "o|" : "||"; + } + return isNullable ? "o{" : "|{"; + } + + private static String buildForeignKeyTail(ForeignKeyDTO fk) { + List parts = new ArrayList<>(); + if (fk.getOnDeleteAction() != null && !fk.getOnDeleteAction().isBlank()) { + parts.add("ON DELETE " + fk.getOnDeleteAction()); + } + if (fk.getOnUpdateAction() != null && !fk.getOnUpdateAction().isBlank()) { + parts.add("ON UPDATE " + fk.getOnUpdateAction()); + } + return String.join(", ", parts); + } + + private static String formatType(ColumnDTO column) { + String type = nullToEmpty(column.getDataType()); + String upperType = type.toUpperCase(Locale.ROOT); + + if (column.getEnumValues() != null && !column.getEnumValues().isEmpty()) { + String enums = column.getEnumValues().stream() + .map(value -> "'" + value.replace("'", "''") + "'") + .collect(Collectors.joining(",")); + type = upperType + "(" + enums + ")"; + } else if (column.getPrecision() != null) { + if (column.getScale() != null) { + type = upperType + "(" + column.getPrecision() + "," + column.getScale() + ")"; + } else { + type = upperType + "(" + column.getPrecision() + ")"; + } + } else if (column.getLength() != null) { + type = upperType + "(" + column.getLength() + ")"; + } else { + type = upperType; + } + + if (Boolean.TRUE.equals(column.getUnsigned())) { + type = type + " UNSIGNED"; + } + return type; + } + + /** + * Format default values, adding quotes for string types while leaving expressions untouched. + * + * @param column column metadata + * @return formatted default value literal + */ + private static String formatDefault(ColumnDTO column) { + String defaultValue = column.getDefaultValue(); + if (defaultValue == null) { + return ""; + } + + String trimmed = defaultValue.trim(); + if (trimmed.isEmpty()) { + return trimmed; + } + + if (trimmed.length() >= 2 && trimmed.startsWith("`") && trimmed.endsWith("`")) { + trimmed = trimmed.substring(1, trimmed.length() - 1); + } + + if (isQuoted(trimmed) + || looksFunctionCall(trimmed) + || looksNumeric(trimmed) + || isBooleanLiteral(trimmed) + || isNullLiteral(trimmed)) { + return trimmed; + } + + if (isStringLike(column.getDataType())) { + return "'" + trimmed.replace("'", "''") + "'"; + } + + return trimmed; + } + + private static List safe(List list) { + return list == null ? List.of() : list; + } + + private static String nullToEmpty(String value) { + return value == null ? "" : value; + } + + private static String escape(String value) { + return nullToEmpty(value).replace("\\", "\\\\").replace("\"", "\\\""); + } + + private static String escapeInline(String value) { + return escape(value).replace("\n", " "); + } + + private static boolean isQuoted(String value) { + return (value.startsWith("'") && value.endsWith("'")) + || (value.startsWith("\"") && value.endsWith("\"")); + } + + private static boolean looksFunctionCall(String value) { + return value.matches("(?i)[A-Z_][A-Z0-9_]*\\s*\\(.*\\)"); + } + + private static boolean looksNumeric(String value) { + return value.matches("[-+]?\\d+(\\.\\d+)?"); + } + + private static boolean isBooleanLiteral(String value) { + return "true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value); + } + + private static boolean isNullLiteral(String value) { + return "null".equalsIgnoreCase(value); + } + + private static boolean isStringLike(String type) { + if (type == null) { + return false; + } + String upper = type.toUpperCase(Locale.ROOT); + return upper.contains("CHAR") + || upper.contains("TEXT") + || upper.contains("CLOB") + || upper.contains("STRING") + || upper.contains("JSON") + || upper.contains("UUID"); + } +} diff --git a/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/util/SqlDumpCleaner.java b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/util/SqlDumpCleaner.java new file mode 100644 index 000000000..7ef8dc0a0 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/java/org/opengauss/admin/plugin/util/SqlDumpCleaner.java @@ -0,0 +1,308 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.opengauss.admin.plugin.util; + +import org.opengauss.admin.plugin.enums.DatabaseDialect; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Optional; + +/** + * Clean SQL dumps by stripping data statements and retaining only DDL. + * - Remove PostgreSQL/openGauss COPY … FROM stdin data blocks + * - Split statements with awareness of quotes, comments, dollar-quoted strings, and backticks + * - Whitelist: CREATE TABLE / ALTER TABLE / CREATE INDEX + * + * @since 2025-01-07 + */ +public final class SqlDumpCleaner { + private SqlDumpCleaner() {} + + /** + * Remove data statements from a SQL dump, keeping only DDL statements supported by the ER modeler. + * + * @param sql raw SQL dump text + * @param dialect target database dialect (currently informational) + * @return cleaned SQL containing only whitelisted DDL statements + */ + public static String keepDdlOnly(String sql, DatabaseDialect dialect) { + if (sql == null || sql.isBlank()) { + return ""; + } + String noCopy = stripPgCopyData(sql); + List statements = splitSqlStatements(noCopy); + + StringBuilder ddlOnly = new StringBuilder(noCopy.length()); + for (String statement : statements) { + String keyword = leadingKeyword(statement); + if (isDdlHead(keyword)) { + String trimmed = statement.trim(); + ddlOnly.append(trimmed); + if (!trimmed.endsWith(";")) { + ddlOnly.append(";"); + } + ddlOnly.append("\n"); + } + } + return ddlOnly.toString(); + } + + /* --- A: Remove PG/OG COPY data segments --- */ + private static String stripPgCopyData(String sql) { + StringBuilder out = new StringBuilder(sql.length()); + try (BufferedReader br = new BufferedReader(new StringReader(sql))) { + String line; + boolean isSkippingCopyData = false; + while ((line = br.readLine()) != null) { + String trim = line.trim(); + if (!isSkippingCopyData) { + if (trim.regionMatches(true, 0, "COPY ", 0, 5) + && trim.toUpperCase(Locale.ROOT).contains("FROM STDIN")) { + isSkippingCopyData = true; + continue; + } else { + out.append(line).append("\n"); + } + } else if ("\\.".equals(trim)) { + isSkippingCopyData = false; + } else { + out.append(line).append("\n"); + } + } + } catch (IOException ex) { + // Preserve existing behaviour: retain COPY contents when parsing fails + return sql; + } + return out.toString(); + } + + /* --- B: Split statements (aware of quotes/comments/dollar/backtick) --- */ + private static List splitSqlStatements(String sql) { + return new StatementSplitter(sql).split(); + } + + private static final class StatementSplitter { + private final String source; + private final List statements = new ArrayList<>(); + private final StringBuilder buffer; + + private boolean isInSingleQuote; + private boolean isInDoubleQuote; + private boolean isInBacktick; + private boolean isInLineComment; + private boolean isInBlockComment; + private String activeDollarTag; + private char previousChar; + + StatementSplitter(String source) { + this.source = source; + this.buffer = new StringBuilder(source.length()); + } + + List split() { + for (int index = 0; index < source.length(); index++) { + char current = source.charAt(index); + char next = nextChar(index); + + handleCommentTransitions(current, next); + if (isInLineComment) { + appendAndMaybeExitLineComment(current); + continue; + } + if (isInBlockComment) { + appendAndTrack(current); + continue; + } + + handleDollarStrings(index, current); + toggleQuoteStates(current); + + if (shouldSplitOn(current)) { + buffer.append(current); + statements.add(buffer.toString()); + buffer.setLength(0); + previousChar = current; + continue; + } + + appendAndTrack(current); + } + + String remaining = buffer.toString().trim(); + if (!remaining.isEmpty()) { + statements.add(remaining); + } + return statements; + } + + private char nextChar(int index) { + return index + 1 < source.length() ? source.charAt(index + 1) : 0; + } + + private void handleCommentTransitions(char current, char next) { + if (isInDollarOrQuoted()) { + return; + } + if (!isInLineComment && !isInBlockComment && current == '-' && next == '-') { + isInLineComment = true; + } else if (!isInLineComment && !isInBlockComment && current == '#') { + isInLineComment = true; + } else if (!isInBlockComment && current == '/' && next == '*') { + isInBlockComment = true; + } else if (isInBlockComment && previousChar == '*' && current == '/') { + isInBlockComment = false; + } else { + ; // Not a comment marker, continue processing + } + } + + private boolean isInDollarOrQuoted() { + return activeDollarTag != null || isInSingleQuote || isInDoubleQuote || isInBacktick; + } + + private void appendAndMaybeExitLineComment(char current) { + appendAndTrack(current); + if (current == '\n') { + isInLineComment = false; + } + } + + private void appendAndTrack(char current) { + buffer.append(current); + previousChar = current; + } + + private void handleDollarStrings(int index, char current) { + if (isInSingleQuote || isInDoubleQuote || isInBacktick) { + return; + } + if (activeDollarTag == null && current == '$') { + probeDollarTag(index).ifPresent(tag -> activeDollarTag = tag); + } else if (activeDollarTag != null && current == '$' && matchesClosingDollarTag(index)) { + activeDollarTag = null; + } else { + ; // Not a dollar tag delimiter, continue processing + } + } + + private Optional probeDollarTag(int start) { + int probe = start + 1; + while (probe < source.length() && isDollarIdent(source.charAt(probe))) { + probe++; + } + if (probe < source.length() && source.charAt(probe) == '$') { + return Optional.of(source.substring(start, probe + 1)); + } + return Optional.empty(); + } + + private boolean matchesClosingDollarTag(int index) { + int tagLength = activeDollarTag.length(); + int start = index - (tagLength - 1); + if (start < 0) { + return false; + } + String candidate = source.substring(start, index + 1); + return candidate.equals(activeDollarTag); + } + + private void toggleQuoteStates(char current) { + if (activeDollarTag != null) { + return; + } + if (!isInDoubleQuote && !isInBacktick && current == '\'' && previousChar != '\\') { + isInSingleQuote = !isInSingleQuote; + } else if (!isInSingleQuote && !isInBacktick && current == '"' && previousChar != '\\') { + isInDoubleQuote = !isInDoubleQuote; + } else if (!isInSingleQuote && !isInDoubleQuote && current == '`' && previousChar != '\\') { + isInBacktick = !isInBacktick; + } else { + ; // Not a quote character, continue processing + } + } + + private boolean shouldSplitOn(char current) { + return current == ';' + && !isInSingleQuote + && !isInDoubleQuote + && !isInBacktick + && activeDollarTag == null; + } + } + + private static boolean isDollarIdent(char candidate) { + return Character.isLetterOrDigit(candidate) || candidate == '_'; + } + + private static String leadingKeyword(String statement) { + if (statement == null) { + return ""; + } + String remaining = statement.trim(); + + boolean isStrippingComments = true; + while (isStrippingComments && !remaining.isEmpty()) { + if (remaining.startsWith("--")) { + int newline = remaining.indexOf('\n'); + remaining = newline >= 0 ? remaining.substring(newline + 1).trim() : ""; + } else if (remaining.startsWith("#")) { + int newline = remaining.indexOf('\n'); + remaining = newline >= 0 ? remaining.substring(newline + 1).trim() : ""; + } else if (remaining.startsWith("/*")) { + int end = remaining.indexOf("*/"); + remaining = end >= 0 ? remaining.substring(end + 2).trim() : ""; + } else { + isStrippingComments = false; + } + } + + if (remaining.isEmpty()) { + return ""; + } + String[] parts = remaining.toUpperCase(Locale.ROOT).split("\\s+"); + if (parts.length == 0) { + return ""; + } + if (parts.length >= 2) { + return parts[0] + " " + parts[1]; + } + return parts[0]; + } + + private static boolean isDdlHead(String headUpper) { + if (headUpper == null) { + return false; + } + if (headUpper.startsWith("CREATE TABLE")) { + return true; + } + if (headUpper.startsWith("ALTER TABLE")) { + return true; + } + if (headUpper.startsWith("CREATE INDEX")) { + return true; + } + if (headUpper.startsWith("CREATE UNIQUE")) { + return true; + } + return false; + } +} diff --git a/ER-Model/plugins/er-model/src/main/resources/application.yml b/ER-Model/plugins/er-model/src/main/resources/application.yml new file mode 100644 index 000000000..a2291bfe8 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/resources/application.yml @@ -0,0 +1,45 @@ +install: + package: + upgrade: + old-history-version: 3.0.0;3.0.3;3.0.5;3.1.0;3.1.1;5.0.0;5.0.1;5.0.2;5.0.3;5.1.0;6.0.0-RC1 + history-version: 6.0.0;7.0.0-RC1 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + druid: + test-while-idle: false + test-on-borrow: true + validation-query: "select 1" + validation-query-timeout: 30000 + web: + resources: + static-locations: classpath:resources +system: + defaultStoragePath: files/plugins/base_ops_default_path +# log config +logging: + level: + org.opengauss.admin.plugin: INFO + file: + name: logs/plugins_base_ops.log +mybatis-plus: + # Search for the specified package alias + typeAliasesPackage: org.opengauss.admin.plugin.domain + + mapperLocations: classpath*:mapper/**/*.xml + # Load the global configuration file + configuration: + mapUnderscoreToCamelCase: true + log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl + +installPackage: + urlPrefix: https://opengauss.obs.cn-south-1.myhuaweicloud.com + +olk: + # read log timeout, seconds + readLogTimeout: 30 + # Loop count to find start flag in log file + startLoopCount: 4 + # wait time before each loop, seconds + startLoopWaitTime: 5 + diff --git a/ER-Model/plugins/er-model/src/main/resources/intarkdb-schema.sql b/ER-Model/plugins/er-model/src/main/resources/intarkdb-schema.sql new file mode 100644 index 000000000..60295fdd1 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/resources/intarkdb-schema.sql @@ -0,0 +1,35 @@ +-- ER Model plugin minimal schema (INTARKDB variant). +-- Matches the openGauss schema so plugin features work identically across drivers. + +CREATE TABLE IF NOT EXISTS er_model_saved_diagram ( + diagram_id BIGSERIAL PRIMARY KEY, + diagram_name VARCHAR(128) NOT NULL, + dialect VARCHAR(32) NOT NULL, + dbml_content TEXT NOT NULL, + description TEXT, + created_by VARCHAR(64), + updated_by VARCHAR(64), + created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS er_model_execution_audit ( + execution_id BIGSERIAL PRIMARY KEY, + diagram_id BIGINT, + cluster_id VARCHAR(128), + node_id VARCHAR(128), + target_database VARCHAR(128), + target_schema VARCHAR(128), + executed_sql TEXT NOT NULL, + status VARCHAR(32) DEFAULT 'SUCCESS', + error_message TEXT, + executed_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_er_model_execution_diagram + FOREIGN KEY (diagram_id) + REFERENCES er_model_saved_diagram (diagram_id) + ON DELETE SET NULL +); + +CREATE INDEX IF NOT EXISTS idx_er_model_execution_diagram + ON er_model_execution_audit (diagram_id); + diff --git a/ER-Model/plugins/er-model/src/main/resources/log4j2.xml b/ER-Model/plugins/er-model/src/main/resources/log4j2.xml new file mode 100644 index 000000000..a8fc64da4 --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/resources/log4j2.xml @@ -0,0 +1,52 @@ + + + + $${sys:logging.file.path:-./logs} + %enc{%msg}{CRLF}%n + %style{%d{yyyy-MM-dd HH:mm:ss}}{blue} [%style{%t}{cyan}] [%style{%c{32}}{yellow}] - [%highlight{%p}] ${SAFE_MSG} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ER-Model/plugins/er-model/src/main/resources/opengauss-schema.sql b/ER-Model/plugins/er-model/src/main/resources/opengauss-schema.sql new file mode 100644 index 000000000..c0fd81b0e --- /dev/null +++ b/ER-Model/plugins/er-model/src/main/resources/opengauss-schema.sql @@ -0,0 +1,35 @@ +-- ER Model plugin minimal schema (legacy filename). +-- The content mirrors openGauss-shema.sql for compatibility with older loaders. + +CREATE TABLE IF NOT EXISTS er_model_saved_diagram ( + diagram_id BIGSERIAL PRIMARY KEY, + diagram_name VARCHAR(128) NOT NULL, + dialect VARCHAR(32) NOT NULL, + dbml_content TEXT NOT NULL, + description TEXT, + created_by VARCHAR(64), + updated_by VARCHAR(64), + created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS er_model_execution_audit ( + execution_id BIGSERIAL PRIMARY KEY, + diagram_id BIGINT, + cluster_id VARCHAR(128), + node_id VARCHAR(128), + target_database VARCHAR(128), + target_schema VARCHAR(128), + executed_sql TEXT NOT NULL, + status VARCHAR(32) DEFAULT 'SUCCESS', + error_message TEXT, + executed_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_er_model_execution_diagram + FOREIGN KEY (diagram_id) + REFERENCES er_model_saved_diagram (diagram_id) + ON DELETE SET NULL +); + +CREATE INDEX IF NOT EXISTS idx_er_model_execution_diagram + ON er_model_execution_audit (diagram_id); + diff --git a/ER-Model/plugins/er-model/web-ui/.editorconfig b/ER-Model/plugins/er-model/web-ui/.editorconfig new file mode 100644 index 000000000..ca2d7f4a8 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/.editorconfig @@ -0,0 +1,294 @@ +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +max_line_length = 999 +tab_width = 2 +trim_trailing_whitespace = true +ij_continuation_indent_size = 2 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = false +ij_smart_tabs = false +ij_visual_guides = none +ij_wrap_on_typing = false + +[*.css] +ij_css_align_closing_brace_with_properties = false +ij_css_blank_lines_around_nested_selector = 1 +ij_css_blank_lines_between_blocks = 1 +ij_css_brace_placement = end_of_line +ij_css_enforce_quotes_on_format = false +ij_css_hex_color_long_format = false +ij_css_hex_color_lower_case = false +ij_css_hex_color_short_format = false +ij_css_hex_color_upper_case = false +ij_css_keep_blank_lines_in_code = 1 +ij_css_keep_indents_on_empty_lines = false +ij_css_keep_single_line_blocks = false +ij_css_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow +ij_css_space_after_colon = true +ij_css_space_before_opening_brace = true +ij_css_use_double_quotes = true +ij_css_value_alignment = do_not_align + +[*.scss] +ij_scss_align_closing_brace_with_properties = false +ij_scss_blank_lines_around_nested_selector = 1 +ij_scss_blank_lines_between_blocks = 1 +ij_scss_brace_placement = 0 +ij_scss_enforce_quotes_on_format = false +ij_scss_hex_color_long_format = false +ij_scss_hex_color_lower_case = false +ij_scss_hex_color_short_format = false +ij_scss_hex_color_upper_case = false +ij_scss_keep_blank_lines_in_code = 1 +ij_scss_keep_indents_on_empty_lines = false +ij_scss_keep_single_line_blocks = false +ij_scss_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow +ij_scss_space_after_colon = true +ij_scss_space_before_opening_brace = true +ij_scss_use_double_quotes = true +ij_scss_value_alignment = 0 + +[*.vue] +ij_vue_indent_children_of_top_level = template, script, style +ij_vue_interpolation_new_line_after_start_delimiter = true +ij_vue_interpolation_new_line_before_end_delimiter = true +ij_vue_interpolation_wrap = off +ij_vue_keep_indents_on_empty_lines = false +ij_vue_spaces_within_interpolation_expressions = true +ij_vue_uniform_indent = true + +[.editorconfig] +ij_editorconfig_align_group_field_declarations = false +ij_editorconfig_space_after_colon = false +ij_editorconfig_space_after_comma = true +ij_editorconfig_space_before_colon = false +ij_editorconfig_space_before_comma = false +ij_editorconfig_spaces_around_assignment_operators = true + +[{*.ant, *.fxml, *.jhm, *.jnlp, *.jrxml, *.rng, *.tld, *.wsdl, *.xml, *.xsd, *.xsl, *.xslt, *.xul}] +ij_xml_align_attributes = true +ij_xml_align_text = false +ij_xml_attribute_wrap = normal +ij_xml_block_comment_at_first_column = true +ij_xml_keep_blank_lines = 1 +ij_xml_keep_indents_on_empty_lines = false +ij_xml_keep_line_breaks = true +ij_xml_keep_line_breaks_in_text = true +ij_xml_keep_whitespaces = false +ij_xml_keep_whitespaces_around_cdata = preserve +ij_xml_keep_whitespaces_inside_cdata = false +ij_xml_line_comment_at_first_column = true +ij_xml_space_after_tag_name = false +ij_xml_space_around_equals_in_attribute = false +ij_xml_space_inside_empty_tag = false +ij_xml_text_wrap = normal + +[{*.cjs, *.js}] +ij_javascript_align_imports = false +ij_javascript_align_multiline_array_initializer_expression = false +ij_javascript_align_multiline_binary_operation = false +ij_javascript_align_multiline_chained_methods = false +ij_javascript_align_multiline_extends_list = false +ij_javascript_align_multiline_for = true +ij_javascript_align_multiline_parameters = true +ij_javascript_align_multiline_parameters_in_calls = false +ij_javascript_align_multiline_ternary_operation = false +ij_javascript_align_object_properties = 0 +ij_javascript_align_union_types = false +ij_javascript_align_var_statements = 0 +ij_javascript_array_initializer_new_line_after_left_brace = false +ij_javascript_array_initializer_right_brace_on_new_line = false +ij_javascript_array_initializer_wrap = off +ij_javascript_assignment_wrap = off +ij_javascript_binary_operation_sign_on_next_line = false +ij_javascript_binary_operation_wrap = off +ij_javascript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/** +ij_javascript_blank_lines_after_imports = 1 +ij_javascript_blank_lines_around_class = 1 +ij_javascript_blank_lines_around_field = 0 +ij_javascript_blank_lines_around_function = 1 +ij_javascript_blank_lines_around_method = 1 +ij_javascript_block_brace_style = end_of_line +ij_javascript_call_parameters_new_line_after_left_paren = false +ij_javascript_call_parameters_right_paren_on_new_line = false +ij_javascript_call_parameters_wrap = off +ij_javascript_catch_on_new_line = false +ij_javascript_chained_call_dot_on_new_line = true +ij_javascript_class_brace_style = end_of_line +ij_javascript_comma_on_new_line = false +ij_javascript_do_while_brace_force = never +ij_javascript_else_on_new_line = false +ij_javascript_enforce_trailing_comma = remove +ij_javascript_extends_keyword_wrap = off +ij_javascript_extends_list_wrap = off +ij_javascript_field_prefix = _ +ij_javascript_file_name_style = relaxed +ij_javascript_finally_on_new_line = false +ij_javascript_for_brace_force = never +ij_javascript_for_statement_new_line_after_left_paren = false +ij_javascript_for_statement_right_paren_on_new_line = false +ij_javascript_for_statement_wrap = off +ij_javascript_force_quote_style = true +ij_javascript_force_semicolon_style = true +ij_javascript_function_expression_brace_style = end_of_line +ij_javascript_if_brace_force = never +ij_javascript_import_merge_members = global +ij_javascript_import_prefer_absolute_path = global +ij_javascript_import_sort_members = true +ij_javascript_import_sort_module_name = false +ij_javascript_import_use_node_resolution = true +ij_javascript_imports_wrap = on_every_item +ij_javascript_indent_case_from_switch = true +ij_javascript_indent_chained_calls = true +ij_javascript_indent_package_children = 0 +ij_javascript_jsx_attribute_value = braces +ij_javascript_keep_blank_lines_in_code = 1 +ij_javascript_keep_first_column_comment = true +ij_javascript_keep_indents_on_empty_lines = false +ij_javascript_keep_line_breaks = true +ij_javascript_keep_simple_blocks_in_one_line = false +ij_javascript_keep_simple_methods_in_one_line = false +ij_javascript_line_comment_add_space = true +ij_javascript_line_comment_at_first_column = false +ij_javascript_method_brace_style = end_of_line +ij_javascript_method_call_chain_wrap = off +ij_javascript_method_parameters_new_line_after_left_paren = false +ij_javascript_method_parameters_right_paren_on_new_line = false +ij_javascript_method_parameters_wrap = off +ij_javascript_object_literal_wrap = on_every_item +ij_javascript_parentheses_expression_new_line_after_left_paren = false +ij_javascript_parentheses_expression_right_paren_on_new_line = false +ij_javascript_place_assignment_sign_on_next_line = false +ij_javascript_prefer_as_type_cast = false +ij_javascript_prefer_explicit_types_function_expression_returns = false +ij_javascript_prefer_explicit_types_function_returns = false +ij_javascript_prefer_explicit_types_vars_fields = false +ij_javascript_prefer_parameters_wrap = false +ij_javascript_reformat_c_style_comments = false +ij_javascript_space_after_colon = true +ij_javascript_space_after_comma = true +ij_javascript_space_after_dots_in_rest_parameter = false +ij_javascript_space_after_generator_mult = true +ij_javascript_space_after_property_colon = true +ij_javascript_space_after_quest = true +ij_javascript_space_after_type_colon = true +ij_javascript_space_after_unary_not = false +ij_javascript_space_before_async_arrow_lparen = true +ij_javascript_space_before_catch_keyword = true +ij_javascript_space_before_catch_left_brace = true +ij_javascript_space_before_catch_parentheses = true +ij_javascript_space_before_class_lbrace = true +ij_javascript_space_before_class_left_brace = true +ij_javascript_space_before_colon = true +ij_javascript_space_before_comma = false +ij_javascript_space_before_do_left_brace = true +ij_javascript_space_before_else_keyword = true +ij_javascript_space_before_else_left_brace = true +ij_javascript_space_before_finally_keyword = true +ij_javascript_space_before_finally_left_brace = true +ij_javascript_space_before_for_left_brace = true +ij_javascript_space_before_for_parentheses = true +ij_javascript_space_before_for_semicolon = false +ij_javascript_space_before_function_left_parenth = true +ij_javascript_space_before_generator_mult = false +ij_javascript_space_before_if_left_brace = true +ij_javascript_space_before_if_parentheses = true +ij_javascript_space_before_method_call_parentheses = false +ij_javascript_space_before_method_left_brace = true +ij_javascript_space_before_method_parentheses = false +ij_javascript_space_before_property_colon = false +ij_javascript_space_before_quest = true +ij_javascript_space_before_switch_left_brace = true +ij_javascript_space_before_switch_parentheses = true +ij_javascript_space_before_try_left_brace = true +ij_javascript_space_before_type_colon = false +ij_javascript_space_before_unary_not = false +ij_javascript_space_before_while_keyword = true +ij_javascript_space_before_while_left_brace = true +ij_javascript_space_before_while_parentheses = true +ij_javascript_spaces_around_additive_operators = true +ij_javascript_spaces_around_arrow_function_operator = true +ij_javascript_spaces_around_assignment_operators = true +ij_javascript_spaces_around_bitwise_operators = true +ij_javascript_spaces_around_equality_operators = true +ij_javascript_spaces_around_logical_operators = true +ij_javascript_spaces_around_multiplicative_operators = true +ij_javascript_spaces_around_relational_operators = true +ij_javascript_spaces_around_shift_operators = true +ij_javascript_spaces_around_unary_operator = false +ij_javascript_spaces_within_array_initializer_brackets = false +ij_javascript_spaces_within_brackets = false +ij_javascript_spaces_within_catch_parentheses = false +ij_javascript_spaces_within_for_parentheses = false +ij_javascript_spaces_within_if_parentheses = false +ij_javascript_spaces_within_imports = true +ij_javascript_spaces_within_interpolation_expressions = false +ij_javascript_spaces_within_method_call_parentheses = false +ij_javascript_spaces_within_method_parentheses = false +ij_javascript_spaces_within_object_literal_braces = true +ij_javascript_spaces_within_object_type_braces = true +ij_javascript_spaces_within_parentheses = false +ij_javascript_spaces_within_switch_parentheses = false +ij_javascript_spaces_within_type_assertion = false +ij_javascript_spaces_within_union_types = true +ij_javascript_spaces_within_while_parentheses = false +ij_javascript_special_else_if_treatment = true +ij_javascript_ternary_operation_signs_on_next_line = false +ij_javascript_ternary_operation_wrap = off +ij_javascript_union_types_wrap = on_every_item +ij_javascript_use_chained_calls_group_indents = false +ij_javascript_use_double_quotes = true +ij_javascript_use_explicit_js_extension = global +ij_javascript_use_path_mapping = always +ij_javascript_use_public_modifier = false +ij_javascript_use_semicolon_after_statement = true +ij_javascript_var_declaration_wrap = normal +ij_javascript_while_brace_force = never +ij_javascript_while_on_new_line = false +ij_javascript_wrap_comments = false + +[{*.har, *.jsb2, *.jsb3, *.json, .babelrc, .eslintrc, .prettierrc, .stylelintrc, bowerrc, jest.config}] +ij_json_keep_blank_lines_in_code = 0 +ij_json_keep_indents_on_empty_lines = false +ij_json_keep_line_breaks = true +ij_json_space_after_colon = true +ij_json_space_after_comma = true +ij_json_space_before_colon = true +ij_json_space_before_comma = false +ij_json_spaces_within_braces = false +ij_json_spaces_within_brackets = false +ij_json_wrap_long_lines = false + +[{*.htm, *.html, *.ng, *.sht, *.shtm, *.shtml}] +ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3 +ij_html_align_attributes = true +ij_html_align_text = false +ij_html_attribute_wrap = normal +ij_html_block_comment_at_first_column = true +ij_html_do_not_align_children_of_min_lines = 0 +ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p +ij_html_do_not_indent_children_of_tags = html +ij_html_enforce_quotes = false +ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var +ij_html_keep_blank_lines = 1 +ij_html_keep_indents_on_empty_lines = false +ij_html_keep_line_breaks = true +ij_html_keep_line_breaks_in_text = true +ij_html_keep_whitespaces = false +ij_html_keep_whitespaces_inside = span, pre, textarea +ij_html_line_comment_at_first_column = true +ij_html_new_line_after_last_attribute = when multiline +ij_html_new_line_before_first_attribute = when multiline +ij_html_quote_style = double +ij_html_remove_new_line_before_tags = br +ij_html_space_after_tag_name = false +ij_html_space_around_equality_in_attribute = false +ij_html_space_inside_empty_tag = true +ij_html_text_wrap = normal +ij_html_uniform_ident = false + diff --git a/ER-Model/plugins/er-model/web-ui/.eslintignore b/ER-Model/plugins/er-model/web-ui/.eslintignore new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/.eslintrc.js b/ER-Model/plugins/er-model/web-ui/.eslintrc.js new file mode 100644 index 000000000..ffbdd562a --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/.eslintrc.js @@ -0,0 +1,51 @@ +module.exports = { + root: true, + parserOptions: { + parser: "@babel/eslint-parser" + }, + env: { + browser: true + // node: true + }, + globals: { + "$": true + }, + extends: [ + "plugin:vue/vue3-essential", + "eslint:recommended" + //"standard" + ], + plugins: [ + "vue" + ], + rules: { + "no-console": process.env.NODE_ENV === "production" ? "warn" : "off", + "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", + // allow async-await + "generator-star-spacing": "off", + // allow paren-less arrow functions + "arrow-parens": "off", + "one-var": "off", + "no-void": "off", + "multiline-ternary": "off", + + "import/first": "off", + // 'import/namespace': 'off', + // 'import/default': 'error', + // 'import/export': 'error', + "import/extensions": "off", + "import/no-unresolved": "off", + "import/no-extraneous-dependencies": "off", + "prefer-promise-reject-errors": "off", + "vue/no-unused-components": "warn", + "vue/multi-word-component-names": "off", + + 'no-unused-vars': 'off', + 'no-undef': 'off', + 'no-redeclare': 'off', + "quotes": 'off' + }, + ignorePatterns: [ + "mode-dbml.js" + ] +}; diff --git a/ER-Model/plugins/er-model/web-ui/.gitignore b/ER-Model/plugins/er-model/web-ui/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/.postcssrc.js b/ER-Model/plugins/er-model/web-ui/.postcssrc.js new file mode 100644 index 000000000..1174fe52b --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/.postcssrc.js @@ -0,0 +1,8 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + plugins: [ + // to edit target browsers: use "browserslist" field in package.json + require('autoprefixer') + ] +} diff --git a/ER-Model/plugins/er-model/web-ui/.quasar/app.js b/ER-Model/plugins/er-model/web-ui/.quasar/app.js new file mode 100644 index 000000000..ad7ff1f65 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/.quasar/app.js @@ -0,0 +1,57 @@ +/** + * THIS FILE IS GENERATED AUTOMATICALLY. + * DO NOT EDIT. + * + * You are probably looking on adding startup/initialization code. + * Use "quasar new boot " and add it there. + * One boot file per concern. Then reference the file(s) in quasar.config.js > boot: + * boot: ['file', ...] // do not add ".js" extension to it. + * + * Boot files are your "main.js" + **/ + + + +import { Quasar } from 'quasar' +import { markRaw } from 'vue' +import RootComponent from 'app/src/App.vue' + + +import createRouter from 'app/src/router/index' + + + + + + + +export default async function (createAppFn, quasarUserOptions) { + // Create the app instance. + // Here we inject into it the Quasar UI, the router & possibly the store. + const app = createAppFn(RootComponent) + + + + app.use(Quasar, quasarUserOptions) + + + + + + const router = markRaw( + typeof createRouter === 'function' + ? await createRouter({}) + : createRouter + ) + + + + // Expose the app, the router and the store. + // Note that we are not mounting the app here, since bootstrapping will be + // different depending on whether we are in a browser or on the server. + return { + app, + + router + } +} diff --git a/ER-Model/plugins/er-model/web-ui/.quasar/artifacts.json b/ER-Model/plugins/er-model/web-ui/.quasar/artifacts.json new file mode 100644 index 000000000..baae5d8a5 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/.quasar/artifacts.json @@ -0,0 +1 @@ +{"folders":["/Users/wangziqi/JavaProject/openGauss-workbench/plugins/er-model/web-ui/dist/spa","/Users/wangziqi/JavaProject/openGauss-workbench/plugins/er-model/web-ui/dist","/Users/wangziqi/SummerProject/openGauss-workbench/plugins/er-model/web-ui/dist"]} \ No newline at end of file diff --git a/ER-Model/plugins/er-model/web-ui/.quasar/client-entry.js b/ER-Model/plugins/er-model/web-ui/.quasar/client-entry.js new file mode 100644 index 000000000..095518bea --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/.quasar/client-entry.js @@ -0,0 +1,355 @@ +/** + * THIS FILE IS GENERATED AUTOMATICALLY. + * DO NOT EDIT. + * + * You are probably looking on adding startup/initialization code. + * Use "quasar new boot " and add it there. + * One boot file per concern. Then reference the file(s) in quasar.config.js > boot: + * boot: ['file', ...] // do not add ".js" extension to it. + * + * Boot files are your "main.js" + **/ + + +import { createApp } from 'vue' + + + + + + + +import '@quasar/extras/roboto-font/roboto-font.css' + +import '@quasar/extras/material-icons/material-icons.css' + + + +import '@quasar/extras/animate/bounce.css' + +import '@quasar/extras/animate/flash.css' + +import '@quasar/extras/animate/flip.css' + +import '@quasar/extras/animate/headShake.css' + +import '@quasar/extras/animate/heartBeat.css' + +import '@quasar/extras/animate/hinge.css' + +import '@quasar/extras/animate/jello.css' + +import '@quasar/extras/animate/pulse.css' + +import '@quasar/extras/animate/rubberBand.css' + +import '@quasar/extras/animate/shake.css' + +import '@quasar/extras/animate/shakeX.css' + +import '@quasar/extras/animate/shakeY.css' + +import '@quasar/extras/animate/swing.css' + +import '@quasar/extras/animate/tada.css' + +import '@quasar/extras/animate/wobble.css' + +import '@quasar/extras/animate/backInDown.css' + +import '@quasar/extras/animate/backInLeft.css' + +import '@quasar/extras/animate/backInRight.css' + +import '@quasar/extras/animate/backInUp.css' + +import '@quasar/extras/animate/bounceIn.css' + +import '@quasar/extras/animate/bounceInDown.css' + +import '@quasar/extras/animate/bounceInLeft.css' + +import '@quasar/extras/animate/bounceInRight.css' + +import '@quasar/extras/animate/bounceInUp.css' + +import '@quasar/extras/animate/fadeIn.css' + +import '@quasar/extras/animate/fadeInBottomLeft.css' + +import '@quasar/extras/animate/fadeInBottomRight.css' + +import '@quasar/extras/animate/fadeInDown.css' + +import '@quasar/extras/animate/fadeInDownBig.css' + +import '@quasar/extras/animate/fadeInLeft.css' + +import '@quasar/extras/animate/fadeInLeftBig.css' + +import '@quasar/extras/animate/fadeInRight.css' + +import '@quasar/extras/animate/fadeInRightBig.css' + +import '@quasar/extras/animate/fadeInTopLeft.css' + +import '@quasar/extras/animate/fadeInTopRight.css' + +import '@quasar/extras/animate/fadeInUp.css' + +import '@quasar/extras/animate/fadeInUpBig.css' + +import '@quasar/extras/animate/flipInX.css' + +import '@quasar/extras/animate/flipInY.css' + +import '@quasar/extras/animate/jackInTheBox.css' + +import '@quasar/extras/animate/lightSpeedInLeft.css' + +import '@quasar/extras/animate/lightSpeedInRight.css' + +import '@quasar/extras/animate/rollIn.css' + +import '@quasar/extras/animate/rotateIn.css' + +import '@quasar/extras/animate/rotateInDownLeft.css' + +import '@quasar/extras/animate/rotateInDownRight.css' + +import '@quasar/extras/animate/rotateInUpLeft.css' + +import '@quasar/extras/animate/rotateInUpRight.css' + +import '@quasar/extras/animate/slideInDown.css' + +import '@quasar/extras/animate/slideInLeft.css' + +import '@quasar/extras/animate/slideInRight.css' + +import '@quasar/extras/animate/slideInUp.css' + +import '@quasar/extras/animate/zoomIn.css' + +import '@quasar/extras/animate/zoomInDown.css' + +import '@quasar/extras/animate/zoomInLeft.css' + +import '@quasar/extras/animate/zoomInRight.css' + +import '@quasar/extras/animate/zoomInUp.css' + +import '@quasar/extras/animate/backOutDown.css' + +import '@quasar/extras/animate/backOutLeft.css' + +import '@quasar/extras/animate/backOutRight.css' + +import '@quasar/extras/animate/backOutUp.css' + +import '@quasar/extras/animate/bounceOut.css' + +import '@quasar/extras/animate/bounceOutDown.css' + +import '@quasar/extras/animate/bounceOutLeft.css' + +import '@quasar/extras/animate/bounceOutRight.css' + +import '@quasar/extras/animate/bounceOutUp.css' + +import '@quasar/extras/animate/fadeOut.css' + +import '@quasar/extras/animate/fadeOutBottomLeft.css' + +import '@quasar/extras/animate/fadeOutBottomRight.css' + +import '@quasar/extras/animate/fadeOutDown.css' + +import '@quasar/extras/animate/fadeOutDownBig.css' + +import '@quasar/extras/animate/fadeOutLeft.css' + +import '@quasar/extras/animate/fadeOutLeftBig.css' + +import '@quasar/extras/animate/fadeOutRight.css' + +import '@quasar/extras/animate/fadeOutRightBig.css' + +import '@quasar/extras/animate/fadeOutTopLeft.css' + +import '@quasar/extras/animate/fadeOutTopRight.css' + +import '@quasar/extras/animate/fadeOutUp.css' + +import '@quasar/extras/animate/fadeOutUpBig.css' + +import '@quasar/extras/animate/flipOutX.css' + +import '@quasar/extras/animate/flipOutY.css' + +import '@quasar/extras/animate/lightSpeedOutLeft.css' + +import '@quasar/extras/animate/lightSpeedOutRight.css' + +import '@quasar/extras/animate/rollOut.css' + +import '@quasar/extras/animate/rotateOut.css' + +import '@quasar/extras/animate/rotateOutDownLeft.css' + +import '@quasar/extras/animate/rotateOutDownRight.css' + +import '@quasar/extras/animate/rotateOutUpLeft.css' + +import '@quasar/extras/animate/rotateOutUpRight.css' + +import '@quasar/extras/animate/slideOutDown.css' + +import '@quasar/extras/animate/slideOutLeft.css' + +import '@quasar/extras/animate/slideOutRight.css' + +import '@quasar/extras/animate/slideOutUp.css' + +import '@quasar/extras/animate/zoomOut.css' + +import '@quasar/extras/animate/zoomOutDown.css' + +import '@quasar/extras/animate/zoomOutLeft.css' + +import '@quasar/extras/animate/zoomOutRight.css' + +import '@quasar/extras/animate/zoomOutUp.css' + + +// We load Quasar stylesheet file +import 'quasar/dist/quasar.sass' + + + + +import 'src/css/app.scss' + + +import createQuasarApp from './app.js' +import quasarUserOptions from './quasar-user-options.js' + + + + + + + + + +const publicPath = `/static-plugin/er-model/` + +const doubleSlashRE = /\/\// +const addPublicPath = url => (publicPath + url).replace(doubleSlashRE, '/') + + +async function start ({ + app, + router + +}, bootFiles) { + + + + let hasRedirected = false + const getRedirectUrl = url => { + try { return addPublicPath(router.resolve(url).href) } + catch (err) {} + + return Object(url) === url + ? null + : url + } + const redirect = url => { + hasRedirected = true + + if (typeof url === 'string' && /^https?:\/\//.test(url)) { + window.location.href = url + return + } + + const href = getRedirectUrl(url) + + // continue if we didn't fail to resolve the url + if (href !== null) { + window.location.href = href + + } + } + + const urlPath = window.location.href.replace(window.location.origin, '') + + for (let i = 0; hasRedirected === false && i < bootFiles.length; i++) { + try { + await bootFiles[i]({ + app, + router, + + ssrContext: null, + redirect, + urlPath, + publicPath + }) + } + catch (err) { + if (err && err.url) { + redirect(err.url) + return + } + + console.error('[Quasar] boot error:', err) + return + } + } + + if (hasRedirected === true) { + return + } + + + app.use(router) + + + + + + + + app.mount('#q-app') + + + + + + +} + +createQuasarApp(createApp, quasarUserOptions) + + .then(app => { + return Promise.all([ + + import(/* webpackMode: "eager" */ 'boot/i18n'), + + import(/* webpackMode: "eager" */ 'boot/ace'), + + import(/* webpackMode: "eager" */ 'boot/pinia'), + + import(/* webpackMode: "eager" */ 'boot/v3num'), + + import(/* webpackMode: "eager" */ 'boot/axios') + + ]).then(bootFiles => { + const boot = bootFiles + .map(entry => entry.default) + .filter(entry => typeof entry === 'function') + + start(app, boot) + }) + }) + diff --git a/ER-Model/plugins/er-model/web-ui/.quasar/client-prefetch.js b/ER-Model/plugins/er-model/web-ui/.quasar/client-prefetch.js new file mode 100644 index 000000000..f0d62a854 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/.quasar/client-prefetch.js @@ -0,0 +1,111 @@ +/** + * THIS FILE IS GENERATED AUTOMATICALLY. + * DO NOT EDIT. + * + * You are probably looking on adding startup/initialization code. + * Use "quasar new boot " and add it there. + * One boot file per concern. Then reference the file(s) in quasar.config.js > boot: + * boot: ['file', ...] // do not add ".js" extension to it. + * + * Boot files are your "main.js" + **/ + + + +import App from 'app/src/App.vue' +let appPrefetch = typeof App.preFetch === 'function' + ? App.preFetch + : ( + // Class components return the component options (and the preFetch hook) inside __c property + App.__c !== void 0 && typeof App.__c.preFetch === 'function' + ? App.__c.preFetch + : false + ) + + +function getMatchedComponents (to, router) { + const route = to + ? (to.matched ? to : router.resolve(to).route) + : router.currentRoute.value + + if (!route) { return [] } + + return Array.prototype.concat.apply([], route.matched.map(m => { + return Object.keys(m.components).map(key => { + const comp = m.components[key] + return { + path: m.path, + c: comp + } + }) + })) +} + +export function addPreFetchHooks ({ router, publicPath }) { + // Add router hook for handling preFetch. + // Doing it after initial route is resolved so that we don't double-fetch + // the data that we already have. Using router.beforeResolve() so that all + // async components are resolved. + router.beforeResolve((to, from, next) => { + const + urlPath = window.location.href.replace(window.location.origin, ''), + matched = getMatchedComponents(to, router), + prevMatched = getMatchedComponents(from, router) + + let diffed = false + const preFetchList = matched + .filter((m, i) => { + return diffed || (diffed = ( + !prevMatched[i] || + prevMatched[i].c !== m.c || + m.path.indexOf('/:') > -1 // does it has params? + )) + }) + .filter(m => m.c !== void 0 && ( + typeof m.c.preFetch === 'function' + // Class components return the component options (and the preFetch hook) inside __c property + || (m.c.__c !== void 0 && typeof m.c.__c.preFetch === 'function') + )) + .map(m => m.c.__c !== void 0 ? m.c.__c.preFetch : m.c.preFetch) + + + if (appPrefetch !== false) { + preFetchList.unshift(appPrefetch) + appPrefetch = false + } + + + if (preFetchList.length === 0) { + return next() + } + + let hasRedirected = false + const redirect = url => { + hasRedirected = true + next(url) + } + const proceed = () => { + + if (hasRedirected === false) { next() } + } + + + + preFetchList.reduce( + (promise, preFetch) => promise.then(() => hasRedirected === false && preFetch({ + + currentRoute: to, + previousRoute: from, + redirect, + urlPath, + publicPath + })), + Promise.resolve() + ) + .then(proceed) + .catch(e => { + console.error(e) + proceed() + }) + }) +} diff --git a/ER-Model/plugins/er-model/web-ui/.quasar/quasar-user-options.js b/ER-Model/plugins/er-model/web-ui/.quasar/quasar-user-options.js new file mode 100644 index 000000000..76f3dcc50 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/.quasar/quasar-user-options.js @@ -0,0 +1,22 @@ +/** + * THIS FILE IS GENERATED AUTOMATICALLY. + * DO NOT EDIT. + * + * You are probably looking on adding startup/initialization code. + * Use "quasar new boot " and add it there. + * One boot file per concern. Then reference the file(s) in quasar.config.js > boot: + * boot: ['file', ...] // do not add ".js" extension to it. + * + * Boot files are your "main.js" + **/ + +import iconSet from 'quasar/icon-set/material-icons' + + + +import {Dark,Notify,Dialog} from 'quasar' + + + +export default { config: {},iconSet,plugins: {Dark,Notify,Dialog} } + diff --git a/ER-Model/plugins/er-model/web-ui/README.md b/ER-Model/plugins/er-model/web-ui/README.md new file mode 100644 index 000000000..0cb7923b1 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/README.md @@ -0,0 +1,44 @@ +# ER Model Plugin - Web UI + +This is the frontend component of the ER Model plugin for openGauss Workbench. + +## Prerequisites + +- Node.js (version >= 12.22.1) +- npm (version >= 6.13.4) or yarn (version >= 1.21.1) + +## Installation + +To install the dependencies, run one of the following commands: + +```bash +npm install +``` + +or + +```bash +yarn install +``` + +## Development + +To start the development server: + +```bash +npm run dev +``` + +## Building for Production + +To build the application for production: + +```bash +npm run build +``` + +or specifically for SPA mode: + +```bash +npm run build:prod +``` \ No newline at end of file diff --git a/ER-Model/plugins/er-model/web-ui/babel.config.js b/ER-Model/plugins/er-model/web-ui/babel.config.js new file mode 100644 index 000000000..97adf3e92 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/babel.config.js @@ -0,0 +1,20 @@ +/* eslint-env node */ + +module.exports = api => { + return { + presets: [ + [ + "@quasar/babel-preset-app", + api.caller(caller => caller && caller.target === "node") + ? { + useBuiltIns: false + // targets: { node: "current" } + } + : { + useBuiltIns: false + } + ] + ] + }; +}; + diff --git a/ER-Model/plugins/er-model/web-ui/package.json b/ER-Model/plugins/er-model/web-ui/package.json new file mode 100644 index 000000000..41e1cd2e4 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/package.json @@ -0,0 +1,69 @@ +{ + "name": "dbdiagram-oss", + "version": "0.0.1", + "description": "An Open-Source dbdiagram.io", + "productName": "DB Diagram OSS", + "author": "Dan Spiteri ", + "private": true, + "scripts": { + "dev": "quasar dev", + "build": "quasar build", + "build:prod": "quasar build -m spa", + "build:pwa": "quasar build -m pwa", + "lint": "eslint --ext .js,.ts,.vue ./", + "test": "echo \"No test specified\" && exit 0", + "deploy": "push-dir --dir=dist/spa --branch=gh-pages" + }, + "dependencies": { + "@dbml/core": "^3.13.9", + "@quasar/extras": "^1.13.4", + "ace-builds": "^1.4.14", + "axios": "^1.10.0", + "canvg": "^4.0.3", + "core-js": "^3.6.5", + "dompurify": "^3.0.6", + "html2canvas": "^1.4.1", + "jspdf": "^3.0.1", + "localforage": "^1.10.0", + "pinia": "^2.0.11", + "quasar": "^2.6.2", + "src": "^1.1.2", + "svg-pan-zoom": "^3.6.1", + "svg2pdf.js": "^2.5.0", + "vue": "^3.2.31", + "vue-i18n": "^9.2.0-beta.23", + "vue-router": "^4.0.12", + "vue3-ace-editor": "^2.2.2" + }, + "devDependencies": { + "@babel/eslint-parser": "^7.17.0", + "@quasar/app-webpack": "^3.4.7", + "dotenv": "^16.0.0", + "eslint": "^7.14.0", + "eslint-config-standard": "^14.1.0", + "eslint-plugin-import": "^2.18.2", + "eslint-plugin-node": "^11.0.0", + "eslint-plugin-promise": "^5.1.0", + "eslint-plugin-vue": "^7.0.0", + "push-dir": "^0.4.1", + "sass": "^1.69.7", + "sass-embedded": "^1.69.7" + }, + "browserslist": [ + "last 10 Chrome versions", + "last 10 Firefox versions", + "last 4 Edge versions", + "last 7 Safari versions", + "last 8 Android versions", + "last 8 ChromeAndroid versions", + "last 8 FirefoxAndroid versions", + "last 10 iOS versions", + "last 5 Opera versions", + "not dead" + ], + "engines": { + "node": ">= 12.22.1", + "npm": ">= 6.13.4", + "yarn": ">= 1.21.1" + } +} diff --git a/ER-Model/plugins/er-model/web-ui/public/favicon.ico b/ER-Model/plugins/er-model/web-ui/public/favicon.ico new file mode 100644 index 000000000..d1a21fd92 Binary files /dev/null and b/ER-Model/plugins/er-model/web-ui/public/favicon.ico differ diff --git a/ER-Model/plugins/er-model/web-ui/public/icons/favicon-128x128.png b/ER-Model/plugins/er-model/web-ui/public/icons/favicon-128x128.png new file mode 100644 index 000000000..9e3675deb Binary files /dev/null and b/ER-Model/plugins/er-model/web-ui/public/icons/favicon-128x128.png differ diff --git a/ER-Model/plugins/er-model/web-ui/public/icons/favicon-16x16.png b/ER-Model/plugins/er-model/web-ui/public/icons/favicon-16x16.png new file mode 100644 index 000000000..3eb55da6e Binary files /dev/null and b/ER-Model/plugins/er-model/web-ui/public/icons/favicon-16x16.png differ diff --git a/ER-Model/plugins/er-model/web-ui/public/icons/favicon-32x32.png b/ER-Model/plugins/er-model/web-ui/public/icons/favicon-32x32.png new file mode 100644 index 000000000..ff7257928 Binary files /dev/null and b/ER-Model/plugins/er-model/web-ui/public/icons/favicon-32x32.png differ diff --git a/ER-Model/plugins/er-model/web-ui/public/icons/favicon-96x96.png b/ER-Model/plugins/er-model/web-ui/public/icons/favicon-96x96.png new file mode 100644 index 000000000..b5a7b40bf Binary files /dev/null and b/ER-Model/plugins/er-model/web-ui/public/icons/favicon-96x96.png differ diff --git a/ER-Model/plugins/er-model/web-ui/public/mode-dbml.js b/ER-Model/plugins/er-model/web-ui/public/mode-dbml.js new file mode 100644 index 000000000..80f665fac --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/public/mode-dbml.js @@ -0,0 +1,83 @@ +ace.define("ace/mode/dbml_highlight_rules", ["require", "exports", "ace/lib/oop", "ace/mode/text_highlight_rules"], function (require, exports) { + + var oop = require("../lib/oop"); + var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; + + var DBMLHighlightRules = function () { + // regexp must not have capturing parentheses. Use (?:) instead. + // regexps are ordered -> the first match is used + + var keywordMapper = this.createKeywordMapper({ + keyword: "table|ref|enum|indexes|tablegroup|note|project", + "support.function": "by|bool|boolean|bit|blob|decimal|double|float|long|" + + "longblob|longtext|medium|mediumblob|mediumint|mediumtext|timestamp|" + + "tinyblob|tinyint|tinytext|text|bigint|int|int1|int2|int3|int4|int8|" + + "integer|float|float4|float8|double|char|varbinary|varchar|varcharacter|" + + "precision|date|datetime|year|unsigned|signed|numeric|ucase|lcase|mid|" + + "len|round|rank|now|format|coalesce|ifnull|isnull|nvl" + }, "identifier", true); + + this.$rules = { + start: [ + { + token: "comment", + regex: "\\/\\/.*$" + }, + { + token: keywordMapper, + regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b" + }, + { + token: "keyword.operator", + regex: "<|>|\\-" + }, + { + token: "paren.lparen", + regex: "[\\{]" + }, + { + token: "paren.rparen", + regex: "[\\}]" + }, + { + token: ["text", "support.function"], + regex: /(^\s*[a-zA-Z0-9_-]+[ ]+)(\w+)/ + } + ] + }; + }; + + DBMLHighlightRules.metaData = { + name: "DBML" + }; + + oop.inherits(DBMLHighlightRules, TextHighlightRules); + exports.DBMLHighlightRules = DBMLHighlightRules; +}); + +ace.define("ace/mode/dbml", ["require", "exports", "ace/lib/oop", "ace/mode/text", "ace/mode/dbml_highlighting_rules"], function (require, exports) { + var oop = require("../lib/oop"); + var TextMode = require("./text").Mode; + var DBMLHighlightRules = require("./dbml_highlight_rules").DBMLHighlightRules; + + Mode = function () { + this.HighlightRules = DBMLHighlightRules; + this.$behaviour = this.$defaultBehaviour; + }; + oop.inherits(Mode, TextMode); + + (function () { + this.lineCommentStart = "//"; + this.$id = "ace/mode/dbml"; + }).call(Mode.prototype); + + exports.Mode = Mode; +}); +// (function () { +// ace.require(["ace/mode/dbml"], function (m) { +// if (typeof module == "object" && typeof exports == "object" && module) { +// module.exports = m; +// } +// }); +// })(); +// diff --git a/ER-Model/plugins/er-model/web-ui/public/mode-dbml2.js b/ER-Model/plugins/er-model/web-ui/public/mode-dbml2.js new file mode 100644 index 000000000..5be24f6ee --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/public/mode-dbml2.js @@ -0,0 +1,462 @@ +/* eslint-ignore */ +/* eslint-env browser */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2012, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +/* + THIS FILE WAS AUTOGENERATED BY mode.tmpl.js +*/ + +ace.define("ace/mode/dbml_highlight_rules", ["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(require, exports, module) { + "use strict"; + + var oop = require("../lib/oop"); + var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; + + var DBMLHighlightRules = function() { + // regexp must not have capturing parentheses. Use (?:) instead. + // regexps are ordered -> the first match is used + + this.$rules = { + start: [{ + include: "#structures" + }, { + include: "#comments" + }, { + include: "#strings" + }, { + include: "#relationships" + }, { + token: "keyword.control.dbml", + regex: /\bas\b/, + caseInsensitive: true + }, { + token: "text", + regex: /\{/, + push: [{ + token: "text", + regex: /\}/, + next: "pop" + }, { + include: "#comments" + }, { + include: "#strings" + }, { + include: "#keywords" + }, { + include: "#structures" + }, { + token: "text", + regex: /\[/, + push: [{ + token: "text", + regex: /\]/, + next: "pop" + }, { + include: "#structures" + }, { + include: "#strings" + }, { + include: "#booleans" + }, { + include: "#relationships" + }] + }] + }], + "#booleans": [{ + token: "variable.parameter.dbml", + regex: /\b(?:true|false)\b/ + }], + "#btstrings": [{ + token: "string.quoted.single.dbml", + regex: /[`]/, + push: [{ + token: "string.quoted.single.dbml", + regex: /[`]/, + next: "pop" + }, { + include: "#escapes" + }, { + defaultToken: "string.quoted.single.dbml" + }] + }], + "#comments": [{ + token: "comment.line.double-slash", + regex: /\/\/.*$/ + }], + "#dqstrings": [{ + token: "string.quoted.double.dbml", + regex: /["]/, + push: [{ + token: "string.quoted.double.dbml", + regex: /["]/, + next: "pop" + }, { + include: "#escapes" + }, { + defaultToken: "string.quoted.double.dbml" + }] + }], + "#escapes": [{ + token: "constant.character.escape.dbml", + regex: /\\./ + }], + "#keywords": [{ + token: "keyword.dbml", + regex: /\b(?:guid|uuid|as|by|bool|boolean|bit|blob|decimal|double|enum|float|long|longblob|longtext|medium|mediumblob|mediumint|mediumtext|time|timestamp|timestamptz|tinyblob|tinyint|tinytext|text|bigint|int|int1|int2|int3|int4|int8|integer|float|float4|float8|double|char|varbinary|varchar|varcharacter|precision|date|datetime|year|unsigned|signed|numeric|ucase|lcase|mid|len|round|rank|now|format|coalesce|ifnull|isnull|nvl)\b/ + }], + "#mlstrings": [{ + token: "string.quoted.single.dbml", + regex: /'''/, + push: [{ + token: "string.quoted.single.dbml", + regex: /'''/, + next: "pop" + }, { + token: "constant.character.escape.dbml", + regex: /\\'''/ + }, { + include: "#escapes" + }, { + token: "constant.character.escape.dbml", + regex: /\\/ + }, { + defaultToken: "string.quoted.single.dbml" + }] + }], + "#relationships": [{ + token: "support.type.dbml", + regex: /[<>\\-]/, + comment: "Relationship operators" + }], + "#sqstrings": [{ + token: "string.quoted.single.dbml", + regex: /'/, + push: [{ + token: "string.quoted.single.dbml", + regex: /'/, + next: "pop" + }, { + include: "#escapes" + }, { + defaultToken: "string.quoted.single.dbml" + }] + }], + "#strings": [{ + include: "#mlstrings" + }, { + include: "#sqstrings" + }, { + include: "#dqstrings" + }, { + include: "#btstrings" + }], + "#structures": [{ + token: "storage.entity.class.name.type.dbml", + regex: /\b(?:[pP]roject|[tT]able|[rR]ef|[eE]num|[iI]ndexes|[nN]ote|[tT]able[gG]roup)\b/, + comment: "Main data structure tags" + }] + } + + this.normalizeRules(); + }; + + DBMLHighlightRules.metaData = { + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + name: "DBML", + scopeName: "source.dbml" + } + + + oop.inherits(DBMLHighlightRules, TextHighlightRules); + + exports.DBMLHighlightRules = DBMLHighlightRules; +}); + +ace.define("ace/mode/matching_brace_outdent", ["require", "exports", "module", "ace/range"], function (require, exports, module) { + "use strict"; + + var Range = require("../range").Range; + + var MatchingBraceOutdent = function () { + }; + + (function () { + + this.checkOutdent = function (line, input) { + if (!/^\s+$/.test(line)) + return false; + + return /^\s*\}/.test(input); + }; + + this.autoOutdent = function (doc, row) { + var line = doc.getLine(row); + var match = line.match(/^(\s*\})/); + + if (!match) return 0; + + var column = match[1].length; + var openBracePos = doc.findMatchingBracket({ row: row, column: column }); + + if (!openBracePos || openBracePos.row == row) return 0; + + var indent = this.$getIndent(doc.getLine(openBracePos.row)); + doc.replace(new Range(row, 0, row, column - 1), indent); + }; + + this.$getIndent = function (line) { + return line.match(/^\s*/)[0]; + }; + + }).call(MatchingBraceOutdent.prototype); + + exports.MatchingBraceOutdent = MatchingBraceOutdent; +}); + +ace.define("ace/mode/folding/cstyle", ["require", "exports", "module", "ace/lib/oop", "ace/range", "ace/mode/folding/fold_mode"], function (require, exports, module) { + "use strict"; + + var oop = require("../../lib/oop"); + var Range = require("../../range").Range; + var BaseFoldMode = require("./fold_mode").FoldMode; + + var FoldMode = exports.FoldMode = function (commentRegex) { + if (commentRegex) { + this.foldingStartMarker = new RegExp( + this.foldingStartMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.start) + ); + this.foldingStopMarker = new RegExp( + this.foldingStopMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.end) + ); + } + }; + oop.inherits(FoldMode, BaseFoldMode); + + (function () { + + this.foldingStartMarker = /([\{\[\(])[^\}\]\)]*$|^\s*(\/\*)/; + this.foldingStopMarker = /^[^\[\{\(]*([\}\]\)])|^[\s\*]*(\*\/)/; + this.singleLineBlockCommentRe = /^\s*(\/\*).*\*\/\s*$/; + this.tripleStarBlockCommentRe = /^\s*(\/\*\*\*).*\*\/\s*$/; + this.startRegionRe = /^\s*(\/\*|\/\/)#?region\b/; + this._getFoldWidgetBase = this.getFoldWidget; + this.getFoldWidget = function (session, foldStyle, row) { + var line = session.getLine(row); + + if (this.singleLineBlockCommentRe.test(line)) { + if (!this.startRegionRe.test(line) && !this.tripleStarBlockCommentRe.test(line)) + return ""; + } + + var fw = this._getFoldWidgetBase(session, foldStyle, row); + + if (!fw && this.startRegionRe.test(line)) + return "start"; // lineCommentRegionStart + + return fw; + }; + + this.getFoldWidgetRange = function (session, foldStyle, row, forceMultiline) { + var line = session.getLine(row); + + if (this.startRegionRe.test(line)) + return this.getCommentRegionBlock(session, line, row); + + var match = line.match(this.foldingStartMarker); + if (match) { + var i = match.index; + + if (match[1]) + return this.openingBracketBlock(session, match[1], row, i); + + var range = session.getCommentFoldRange(row, i + match[0].length, 1); + + if (range && !range.isMultiLine()) { + if (forceMultiline) { + range = this.getSectionRange(session, row); + } else if (foldStyle != "all") + range = null; + } + + return range; + } + + if (foldStyle === "markbegin") + return; + + var match = line.match(this.foldingStopMarker); + if (match) { + var i = match.index + match[0].length; + + if (match[1]) + return this.closingBracketBlock(session, match[1], row, i); + + return session.getCommentFoldRange(row, i, -1); + } + }; + + this.getSectionRange = function (session, row) { + var line = session.getLine(row); + var startIndent = line.search(/\S/); + var startRow = row; + var startColumn = line.length; + row = row + 1; + var endRow = row; + var maxRow = session.getLength(); + while (++row < maxRow) { + line = session.getLine(row); + var indent = line.search(/\S/); + if (indent !== -1) { + if (startIndent > indent) + break; + var subRange = this.getFoldWidgetRange(session, "all", row); + + if (subRange) { + if (subRange.start.row <= startRow) { + break; + } else if (subRange.isMultiLine()) { + row = subRange.end.row; + } else if (startIndent == indent) { + break; + } + } + endRow = row; + } + } + + return new Range(startRow, startColumn, endRow, session.getLine(endRow).length); + }; + this.getCommentRegionBlock = function (session, line, row) { + var startColumn = line.search(/\s*$/); + var maxRow = session.getLength(); + var startRow = row; + + var re = /^\s*(?:\/\*|\/\/|--)#?(end)?region\b/; + var depth = 1; + while (++row < maxRow) { + line = session.getLine(row); + var m = re.exec(line); + if (m) { + if (m[1]) depth--; + else depth++; + + if (!depth) break; + } + } + + var endRow = row; + if (endRow > startRow) { + return new Range(startRow, startColumn, endRow, line.length); + } + }; + + }).call(FoldMode.prototype); + +}); + +ace.define('ace/mode/dbml', ["require","exports","module","ace/lib/oop","ace/mode/text", "ace/mode/dbml_highlighting_rules", "ace/mode/matching_brace_outdent", "ace/range", "ace/mode/behaviour/cstyle", "ace/mode/folding/cstyle"], function(require, exports, module) { + "use strict"; + + + var oop = require("../lib/oop"); + var TextMode = require("./text").Mode; + var DBMLHighlightRules = require("./dbml_highlight_rules").DBMLHighlightRules; + var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent; + var Range = require("../range").Range; + var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour; + var CStyleFoldMode = require("./folding/cstyle").FoldMode; + + var Mode = function() { + this.HighlightRules = DBMLHighlightRules; + + this.$outdent = new MatchingBraceOutdent(); + this.$behaviour = new CstyleBehaviour(); + + this.foldingRules = new CStyleFoldMode(); + }; + oop.inherits(Mode, TextMode); + + (function() { + this.lineCommentStart = "//"; + + this.getNextLineIndent = function (state, line, tab) { + var indent = this.$getIndent(line); + + var tokenizedLine = this.getTokenizer().getLineTokens(line, state); + var tokens = tokenizedLine.tokens; + var endState = tokenizedLine.state; + + if (tokens.length && tokens[tokens.length - 1].type == "comment") { + return indent; + } + + if (state == "start") { + var match = line.match(/^.*[\{\(\[]\s*$/); + if (match) { + indent += tab; + } + } else if (state == "doc-start") { + if (endState == "start") { + return ""; + } + var match = line.match(/^\s*(\/?)\*/); + if (match) { + if (match[1]) { + indent += " "; + } + indent += "* "; + } + } + + return indent; + }; + + this.checkOutdent = function (state, line, input) { + return this.$outdent.checkOutdent(line, input); + }; + + this.autoOutdent = function (state, doc, row) { + this.$outdent.autoOutdent(doc, row); + }; + + this.$id = "ace/mode/dbml" + }).call(Mode.prototype); + + exports.Mode = Mode; +}); +(function () { + ace.require(["ace/mode/dbml"], function (m) { + if (typeof module == "object" && typeof exports == "object" && module) { + module.exports = m; + } + }); +})(); diff --git a/ER-Model/plugins/er-model/web-ui/quasar.conf.js b/ER-Model/plugins/er-model/web-ui/quasar.conf.js new file mode 100644 index 000000000..79e9b1988 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/quasar.conf.js @@ -0,0 +1,168 @@ +/* + * This file runs in a Node context (it's NOT transpiled by Babel), so use only + * the ES6 features that are supported by your Node version. https://node.green/ + */ + +// Configuration for your app +// https://quasar.dev/quasar-cli/quasar-conf-js + +/* eslint-env node */ +/* eslint-disable @typescript-eslint/no-var-requires */ +const { configure } = require("quasar/wrappers"); + +module.exports = configure(function (ctx) { + return { + supportTS: false, + boot: [ + "i18n", + "ace", + "pinia", + "v3num", + "axios" + ], + css: [ + "app.scss" + ], + extras: [ + // 'ionicons-v4', + // 'mdi-v5', + // 'fontawesome-v5', + // 'eva-icons', + // 'themify', + // 'line-awesome', + // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both! + + "roboto-font", // optional, you are not bound to it + "material-icons" // optional, you are not bound to it + ], + build: { + env: require("dotenv").config().parsed, + distDir: 'dist', + publicPath: '/static-plugin/er-model/', + vueRouterMode: "history", + chainWebpack(/* chain */) { + } + }, + devServer: { + server: { + type: "http" + }, + port: 3210, + open: true // opens browser window automatically + }, + framework: { + config: { + + }, + iconSet: "material-icons", + // lang: 'en-US', // Quasar language pack + plugins: [ + "Dark", + "Notify", + "Dialog", + "Dark" + ] + }, + + animations: "all", + ssr: { + pwa: false, + + // manualStoreHydration: true, + // manualPostHydrationTrigger: true, + + prodPort: 3000, // The default port that the production server should use + // (gets superseded if process.env.PORT is specified at runtime) + + maxAge: 1000 * 60 * 60 * 24 * 30, + // Tell browser when a file from the server should expire from cache (in ms) + + chainWebpackWebserver (/* chain */) { + // + }, + + middlewares: [ + ctx.prod ? 'compression' : '', + 'render' // keep this as last one + ] + }, + pwa: { + workboxPluginMode: "GenerateSW", // 'GenerateSW' or 'InjectManifest' + workboxOptions: {}, // only for GenerateSW + chainWebpackCustomSW(/* chain */) { + }, + manifest: { + name: "DB Diagram OSS", + short_name: "DB Diagram OSS", + description: "An Open-Source dbdiagram.io", + display: "standalone", + orientation: "portrait", + background_color: "#ffffff", + theme_color: "#027be3", + icons: [ + { + src: "icons/icon-128x128.png", + sizes: "128x128", + type: "image/png" + }, + { + src: "icons/icon-192x192.png", + sizes: "192x192", + type: "image/png" + }, + { + src: "icons/icon-256x256.png", + sizes: "256x256", + type: "image/png" + }, + { + src: "icons/icon-384x384.png", + sizes: "384x384", + type: "image/png" + }, + { + src: "icons/icon-512x512.png", + sizes: "512x512", + type: "image/png" + } + ] + } + }, + cordova: { + + }, + capacitor: { + hideSplashscreen: true + }, + electron: { + bundler: "packager", // 'packager' or 'builder' + packager: { + // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options + + // OS X / Mac App Store + // appBundleId: '', + // appCategoryType: '', + // osxSign: '', + // protocol: 'myapp://path', + + // Windows only + // win32metadata: { ... } + }, + + builder: { + appId: "dbdiagram-oss" + }, + // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain + chainWebpack (/* chain */) { + // do something with the Electron main process Webpack cfg + // extendWebpackMain also available besides this chainWebpackMain + }, + + // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain + chainWebpackPreload (/* chain */) { + // do something with the Electron main process Webpack cfg + // extendWebpackPreload also available besides this chainWebpackPreload + } + } + }; +}); diff --git a/ER-Model/plugins/er-model/web-ui/src/App.vue b/ER-Model/plugins/er-model/web-ui/src/App.vue new file mode 100644 index 000000000..3eac3064f --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/App.vue @@ -0,0 +1,10 @@ + + diff --git a/ER-Model/plugins/er-model/web-ui/src/assets/logo-rounded.png b/ER-Model/plugins/er-model/web-ui/src/assets/logo-rounded.png new file mode 100644 index 000000000..017350387 Binary files /dev/null and b/ER-Model/plugins/er-model/web-ui/src/assets/logo-rounded.png differ diff --git a/ER-Model/plugins/er-model/web-ui/src/assets/logo.png b/ER-Model/plugins/er-model/web-ui/src/assets/logo.png new file mode 100644 index 000000000..117fbdfc1 Binary files /dev/null and b/ER-Model/plugins/er-model/web-ui/src/assets/logo.png differ diff --git a/ER-Model/plugins/er-model/web-ui/src/boot/.gitkeep b/ER-Model/plugins/er-model/web-ui/src/boot/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/boot/ace.js b/ER-Model/plugins/er-model/web-ui/src/boot/ace.js new file mode 100644 index 000000000..18cd7b042 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/boot/ace.js @@ -0,0 +1,82 @@ +import { boot } from "quasar/wrappers"; +import ace from "ace-builds/src-noconflict/ace"; +import "ace-builds/src-noconflict/ext-beautify.js"; +import "ace-builds/src-noconflict/ext-code_lens.js"; +import "ace-builds/src-noconflict/ext-elastic_tabstops_lite.js"; +import "ace-builds/src-noconflict/ext-emmet.js"; +import "ace-builds/src-noconflict/ext-error_marker.js"; +import "ace-builds/src-noconflict/ext-hardwrap.js"; +import "ace-builds/src-noconflict/ext-keybinding_menu.js"; +import "ace-builds/src-noconflict/ext-language_tools.js"; +import "ace-builds/src-noconflict/ext-linking.js"; +import "ace-builds/src-noconflict/ext-modelist.js"; +import "ace-builds/src-noconflict/ext-options.js"; +import "ace-builds/src-noconflict/ext-prompt.js"; +import "ace-builds/src-noconflict/ext-rtl.js"; +import "ace-builds/src-noconflict/ext-searchbox.js"; +import "ace-builds/src-noconflict/ext-settings_menu.js"; +import "ace-builds/src-noconflict/ext-spellcheck.js"; +import "ace-builds/src-noconflict/ext-split.js"; +import "ace-builds/src-noconflict/ext-static_highlight.js"; +import "ace-builds/src-noconflict/ext-statusbar.js"; +import "ace-builds/src-noconflict/ext-textarea.js"; +import "ace-builds/src-noconflict/ext-themelist.js"; +import "ace-builds/src-noconflict/ext-whitespace.js"; + +import "ace-builds/src-noconflict/keybinding-emacs.js"; +import "ace-builds/src-noconflict/keybinding-sublime.js"; +import "ace-builds/src-noconflict/keybinding-vim.js"; +import "ace-builds/src-noconflict/keybinding-vscode.js"; + +import "ace-builds/src-noconflict/theme-ambiance.js"; +import "ace-builds/src-noconflict/theme-chaos.js"; +import "ace-builds/src-noconflict/theme-chrome.js"; +import "ace-builds/src-noconflict/theme-clouds.js"; +import "ace-builds/src-noconflict/theme-clouds_midnight.js"; +import "ace-builds/src-noconflict/theme-cobalt.js"; +import "ace-builds/src-noconflict/theme-crimson_editor.js"; +import "ace-builds/src-noconflict/theme-dawn.js"; +import "ace-builds/src-noconflict/theme-dracula.js"; +import "ace-builds/src-noconflict/theme-dreamweaver.js"; +import "ace-builds/src-noconflict/theme-eclipse.js"; +import "ace-builds/src-noconflict/theme-github.js"; +import "ace-builds/src-noconflict/theme-gob.js"; +import "ace-builds/src-noconflict/theme-gruvbox.js"; +import "ace-builds/src-noconflict/theme-idle_fingers.js"; +import "ace-builds/src-noconflict/theme-iplastic.js"; +import "ace-builds/src-noconflict/theme-katzenmilch.js"; +import "ace-builds/src-noconflict/theme-kr_theme.js"; +import "ace-builds/src-noconflict/theme-kuroir.js"; +import "ace-builds/src-noconflict/theme-merbivore.js"; +import "ace-builds/src-noconflict/theme-merbivore_soft.js"; +import "ace-builds/src-noconflict/theme-monokai.js"; +import "ace-builds/src-noconflict/theme-mono_industrial.js"; +import "ace-builds/src-noconflict/theme-nord_dark.js"; +import "ace-builds/src-noconflict/theme-one_dark.js"; +import "ace-builds/src-noconflict/theme-pastel_on_dark.js"; +import "ace-builds/src-noconflict/theme-solarized_dark.js"; +import "ace-builds/src-noconflict/theme-solarized_light.js"; +import "ace-builds/src-noconflict/theme-sqlserver.js"; +import "ace-builds/src-noconflict/theme-terminal.js"; +import "ace-builds/src-noconflict/theme-textmate.js"; +import "ace-builds/src-noconflict/theme-tomorrow.js"; +import "ace-builds/src-noconflict/theme-tomorrow_night.js"; +import "ace-builds/src-noconflict/theme-tomorrow_night_blue.js"; +import "ace-builds/src-noconflict/theme-tomorrow_night_bright.js"; +import "ace-builds/src-noconflict/theme-tomorrow_night_eighties.js"; +import "ace-builds/src-noconflict/theme-twilight.js"; +import "ace-builds/src-noconflict/theme-vibrant_ink.js"; +import "ace-builds/src-noconflict/theme-xcode.js"; + +import "ace-builds/src-noconflict/worker-base.js"; + +import workerJsonUrl from "file-loader?type=commonjs|target=es5|esModule=false!app/public/mode-dbml.js"; // For webpack / vue-cli + +export default boot(({ app }) => { + + ace.config.setModuleUrl("ace/mode/dbml", workerJsonUrl); + + const themes = ace.require("ace/ext/themelist"); + + +}); diff --git a/ER-Model/plugins/er-model/web-ui/src/boot/axios.js b/ER-Model/plugins/er-model/web-ui/src/boot/axios.js new file mode 100644 index 000000000..ac38e272d --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/boot/axios.js @@ -0,0 +1,67 @@ +// src/boot/axios.js +import { boot } from 'quasar/wrappers' +import axios from 'axios' +import { Notify } from 'quasar' +import { getToken } from '../utils/auth' + +// 创建 axios 实例 +const { protocol, host, port } = window.location +let baseURL = `${protocol}//${host}/plugins/er-model/` + +if (port === '3211') { + // 本地插件独立开发环境 + baseURL = `${protocol}//${host}/plugins/er-model/` +} + +const api = axios.create({ + baseURL, + timeout: 10000 +}) + +// 请求拦截器 +api.interceptors.request.use( + (config) => { + const token = getToken() + if (token) { + config.headers = config.headers || {} + config.headers.Authorization = `Bearer ${token}` + } + return config + }, + (error) => Promise.reject(error) +) + +// 响应拦截器 +api.interceptors.response.use( + (response) => { + // 如果返回的是字符串(如导出 SQL),直接返回 + if (typeof response.data === 'string') { + return response + } + + const code = response.data.code || 200 + const msg = response.data.message || '接口返回错误' + + if (code === 401) { + Notify.create({ type: 'negative', message: '未登录或登录已过期' }) + return Promise.reject(new Error('请先登录')) + } else if (code !== 200) { + Notify.create({ type: 'negative', message: msg }) + return Promise.reject(new Error(msg)) + } + + return response.data + }, + (error) => { + Notify.create({ type: 'negative', message: error.message || '网络异常' }) + return Promise.reject(error) + } +) + +export default boot(({ app }) => { + // 在全局注册 axios 实例 + app.config.globalProperties.$axios = axios + app.config.globalProperties.$api = api +}) + +export { axios, api } diff --git a/ER-Model/plugins/er-model/web-ui/src/boot/i18n.js b/ER-Model/plugins/er-model/web-ui/src/boot/i18n.js new file mode 100644 index 000000000..f7d57c0c5 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/boot/i18n.js @@ -0,0 +1,13 @@ +import { boot } from 'quasar/wrappers' +import { createI18n } from 'vue-i18n' +import messages from 'src/i18n' + +export default boot(({ app }) => { + const i18n = createI18n({ + locale: 'zh-CN', + messages + }) + + // Set i18n instance on app + app.use(i18n) +}) diff --git a/ER-Model/plugins/er-model/web-ui/src/boot/pinia.js b/ER-Model/plugins/er-model/web-ui/src/boot/pinia.js new file mode 100644 index 000000000..5eda71a8a --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/boot/pinia.js @@ -0,0 +1,9 @@ +import { boot } from 'quasar/wrappers' +import { createPinia } from "pinia"; +import plugin from "src/store/plugin" + +export default boot(({ app }) => { + const pinia = createPinia(); + app.use(pinia); + pinia.use(plugin) +}); diff --git a/ER-Model/plugins/er-model/web-ui/src/boot/v3num.js b/ER-Model/plugins/er-model/web-ui/src/boot/v3num.js new file mode 100644 index 000000000..6c967cd52 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/boot/v3num.js @@ -0,0 +1,8 @@ +import { boot } from 'quasar/wrappers' +import VNumber from "src/components/VNumber" + +// "async" is optional; +// more info on params: https://v2.quasar.dev/quasar-cli/boot-files +export default boot(async ({ app }) => { + app.component('VNumber', VNumber) +}) diff --git a/ER-Model/plugins/er-model/web-ui/src/components/AceThemePreviewCard.vue b/ER-Model/plugins/er-model/web-ui/src/components/AceThemePreviewCard.vue new file mode 100644 index 000000000..4fec24302 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/AceThemePreviewCard.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/DbmlEditor.vue b/ER-Model/plugins/er-model/web-ui/src/components/DbmlEditor.vue new file mode 100644 index 000000000..cfdb2017e --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/DbmlEditor.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/DbmlGraph.vue b/ER-Model/plugins/er-model/web-ui/src/components/DbmlGraph.vue new file mode 100644 index 000000000..8743b453f --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/DbmlGraph.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/PreferenceDialogTabs/GeneralTab.vue b/ER-Model/plugins/er-model/web-ui/src/components/PreferenceDialogTabs/GeneralTab.vue new file mode 100644 index 000000000..feb820a1a --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/PreferenceDialogTabs/GeneralTab.vue @@ -0,0 +1,47 @@ + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/PreferenceDialogTabs/ThemeTab.vue b/ER-Model/plugins/er-model/web-ui/src/components/PreferenceDialogTabs/ThemeTab.vue new file mode 100644 index 000000000..85db37093 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/PreferenceDialogTabs/ThemeTab.vue @@ -0,0 +1,30 @@ + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/PreferencesDialog.vue b/ER-Model/plugins/er-model/web-ui/src/components/PreferencesDialog.vue new file mode 100644 index 000000000..43847fcec --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/PreferencesDialog.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbChart.vue b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbChart.vue new file mode 100644 index 000000000..9cf6bcb06 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbChart.vue @@ -0,0 +1,390 @@ + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbField.vue b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbField.vue new file mode 100644 index 000000000..5a4030195 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbField.vue @@ -0,0 +1,222 @@ + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbRef.vue b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbRef.vue new file mode 100644 index 000000000..c92d0e4f9 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbRef.vue @@ -0,0 +1,230 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbTable.vue b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbTable.vue new file mode 100644 index 000000000..e2a761f73 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbTable.vue @@ -0,0 +1,237 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbTableGroup.vue b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbTableGroup.vue new file mode 100644 index 000000000..6a73be631 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbTableGroup.vue @@ -0,0 +1,163 @@ + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbTableTooltip.vue b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbTableTooltip.vue new file mode 100644 index 000000000..1be7c4577 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbTableTooltip.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbTooltip.vue b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbTooltip.vue new file mode 100644 index 000000000..7f5a83b2a --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/VDbChart/VDbTooltip.vue @@ -0,0 +1,28 @@ + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/VDbExportDialog.vue b/ER-Model/plugins/er-model/web-ui/src/components/VDbExportDialog.vue new file mode 100644 index 000000000..0b0c0a6a7 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/VDbExportDialog.vue @@ -0,0 +1,6 @@ + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/VDbField.vue b/ER-Model/plugins/er-model/web-ui/src/components/VDbField.vue new file mode 100644 index 000000000..26b992f93 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/VDbField.vue @@ -0,0 +1,196 @@ + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/VDbStructure.vue b/ER-Model/plugins/er-model/web-ui/src/components/VDbStructure.vue new file mode 100644 index 000000000..6726aec08 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/VDbStructure.vue @@ -0,0 +1,50 @@ + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/VNumber.vue b/ER-Model/plugins/er-model/web-ui/src/components/VNumber.vue new file mode 100644 index 000000000..26914a507 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/VNumber.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/ace/dbml.iro b/ER-Model/plugins/er-model/web-ui/src/components/ace/dbml.iro new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/components/ace/dbml.js b/ER-Model/plugins/er-model/web-ui/src/components/ace/dbml.js new file mode 100644 index 000000000..ed499b6fe --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/ace/dbml.js @@ -0,0 +1,5 @@ +import { require } from 'ace-builds'; +import { ace } from 'ace-builds/src-noconflict/ace'; + + +define('') diff --git a/ER-Model/plugins/er-model/web-ui/src/components/ace/dbml.tmLanguage.xml b/ER-Model/plugins/er-model/web-ui/src/components/ace/dbml.tmLanguage.xml new file mode 100644 index 000000000..796e49acc --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/ace/dbml.tmLanguage.xml @@ -0,0 +1,393 @@ + + + + + + fileTypes + + mysample + + name + mysample + patterns + + + include + #main + + + scopeName + source.mysample + uuid + + repository + + main + + patterns + + + include + #structures + + + include + #comments + + + include + #strings + + + include + #keywords + + + include + #relationships + + + begin + (\{) + beginCaptures + + 1 + + name + paren.lparen.mysample + + + patterns + + + include + #main__1 + + + end + (\}) + endCaptures + + 1 + + name + paren.lparen.mysample + + + + + begin + (\[) + beginCaptures + + 1 + + name + paren.lparen.mysample + + + patterns + + + include + #main__2 + + + end + (\]) + endCaptures + + 1 + + name + paren.lparen.mysample + + + + + + comments + + patterns + + + match + (//.*$) + name + comment.mysample + + + + escapes + + patterns + + + match + (\\.) + name + constant.character.escape.dbml.mysample + + + + keywords + + patterns + + + match + \b(as|by|bool|boolean|bit|blob|decimal|double|enum|float|long|longblob|longtext|medium|mediumblob|mediumint|mediumtext|time|timestamp|timestamptz|tinyblob|tinyint|tinytext|text|bigint|int|int1|int2|int3|int4|int8|integer|float|float4|float8|double|char|varbinary|varchar|varcharacter|precision|date|datetime|year|unsigned|signed|numeric|ucase|lcase|mid|len|round|rank|now|format|coalesce|ifnull|isnull|nvl|true|false)\b + name + keyword.mysample + + + + main__1 + + patterns + + + include + #main + + + + main__2 + + patterns + + + include + #main + + + + relationships + + patterns + + + match + ([\x{003c}\x{003e}\x{005c}-]) + name + keyword.operator.mysample + + + + str_multiline_singlequote + + patterns + + + begin + ([\x{0027}]{3}) + beginCaptures + + 1 + + name + text.mysample + + + patterns + + + include + #str_multiline_singlequote__1 + + + end + ([\x{0027}]{3}) + endCaptures + + 1 + + name + text.mysample + + + + + + str_multiline_singlequote__1 + + patterns + + + include + #escapes + + + + str_singleline_backtick + + patterns + + + begin + ([\x{0060}]) + beginCaptures + + 1 + + name + text.mysample + + + patterns + + + include + #str_singleline_backtick__1 + + + end + ([\x{0060}]) + endCaptures + + 1 + + name + text.mysample + + + + + + str_singleline_backtick__1 + + patterns + + + include + #escapes + + + + str_singleline_doublequote + + patterns + + + begin + ([\x{0022}]) + beginCaptures + + 1 + + name + text.mysample + + + patterns + + + include + #str_singleline_doublequote__1 + + + end + ([\x{0022}]) + endCaptures + + 1 + + name + text.mysample + + + + + + str_singleline_doublequote__1 + + patterns + + + include + #escapes + + + + str_singleline_singlequote + + patterns + + + begin + ([\x{0027}]) + beginCaptures + + 1 + + name + text.mysample + + + patterns + + + include + #str_singleline_singlequote__1 + + + end + ([\x{0027}]) + endCaptures + + 1 + + name + text.mysample + + + + + + str_singleline_singlequote__1 + + patterns + + + include + #escapes + + + + strings + + patterns + + + include + #str_singleline_singlequote + + + include + #str_singleline_doublequote + + + include + #str_singleline_backtick + + + include + #str_multiline_singlequote + + + + structures + + patterns + + + match + \b([pP]roject|[tT]able|[tT]able[Gg]roup|[rR]ef|[eE]num|[iI]ndexes|[nN]ote|[Pp]k|[Nn]ot [Nn]ull|[Nn]ull|[Ii]ncrement|[Dd]efault)\b + name + entity.name.type.dbml.mysample + + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/components/ace/dbml_iro.js.nocompile b/ER-Model/plugins/er-model/web-ui/src/components/ace/dbml_iro.js.nocompile new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/components/ace/inline_annotation.js b/ER-Model/plugins/er-model/web-ui/src/components/ace/inline_annotation.js new file mode 100644 index 000000000..19b84e635 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/components/ace/inline_annotation.js @@ -0,0 +1,36 @@ +import { Ace, Range } from 'ace-builds' + +export class InlineAnnotation { + + constructor(session, info) { + this.session = session; + this.info = info; + this.type = info.type; + this.row = info.row; + this.text = info.text || info.message || undefined; + this.startAnchor = session.getDocument().createAnchor(info.row, info.column); + this.endAnchor = session.getDocument().createAnchor(info.endRow || info.row, info.endColumn); + this.startAnchor.on("change", this.update.bind(this)); + this.endAnchor.on("change", this.update.bind(this)); + this.marker = null; + this.update(); + } + + update() { + console.log("update", this); + const range = Range.fromPoints(this.startAnchor.getPosition(), this.endAnchor.getPosition()); + if (this.marker) { + this.session.removeMarker(this.marker); + } + this.marker = this.session.addMarker(range, `marker-highlight-${this.info.type}`, 'text'); + } + + remove() { + this.startAnchor.detach(); + this.endAnchor.detach(); + if (this.marker) { + this.session.removeMarker(this.marker); + } + } +} + diff --git a/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_fields.scss b/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_fields.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_refs.scss b/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_refs.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_tablegroups.scss b/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_tablegroups.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_tables.scss b/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_tables.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_tools.scss b/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_tools.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_tooltips.scss b/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_tooltips.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_variables.scss b/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/_variables.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/index.scss b/ER-Model/plugins/er-model/web-ui/src/css/VDbChart/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/css/_ace.scss b/ER-Model/plugins/er-model/web-ui/src/css/_ace.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/css/_dbml-graph.scss b/ER-Model/plugins/er-model/web-ui/src/css/_dbml-graph.scss new file mode 100644 index 000000000..66abf230f --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/css/_dbml-graph.scss @@ -0,0 +1,251 @@ +// 全局样式:还原 ER 图编辑区的视觉 +.dbml-graph-wrapper { + background-color: #fff; + color: #0f172a; + font-family: "HarmonyOS_Sans_SC", "HarmonyOS_Sans", "-apple-system", BlinkMacSystemFont, + "PingFang SC", "Microsoft YaHei", Arial, "Helvetica Neue", Helvetica, "Roboto", sans-serif; +} + +.db-table__background, +.db-table__highlight .db-table__background, +.db-table__dragging .db-table__background, +.db-table:hover .db-table__background { + rx: 24 !important; + ry: 24 !important; + stroke-linejoin: round !important; + shape-rendering: geometricPrecision !important; +} + +.db-chart { + shape-rendering: geometricPrecision; + cursor: grab; +} + +.db-chart:active { + cursor: grabbing; +} + +.db-chart__bg { + fill: #fff; +} + +.db-chart__bg-grid path, +.db-chart__bg-grid-small path { + stroke: rgba(15, 23, 42, 0.08); + stroke-width: 1; +} + +.db-chart__bg-grid-small path { + stroke-width: 0.5; + opacity: 0.6; +} + +.db-table__background { + fill: #ffffff; + stroke: #dbe3ef; + stroke-width: 1.5; + rx: 24; + ry: 24; + filter: drop-shadow(0 12px 24px rgba(15, 23, 42, 0.08)); + stroke-linejoin: round; +} + +.db-table { + cursor: grab; +} + +.db-table__dragging { + cursor: grabbing; +} + +.db-table__highlight .db-table__background, +.db-table__dragging .db-table__background { + stroke: #2563eb; + stroke-width: 2; +} + +.db-table-header rect { + fill: #1f3a5f; + rx: 14; + ry: 14; +} + +.db-table-header__name { + fill: #f8fafc; + font-size: 13px; + font-weight: 600; + letter-spacing: 0.2px; + text-transform: uppercase; + dominant-baseline: middle; + alignment-baseline: middle; + transform: translateY(4px); +} + +.db-field rect { + fill: #f8fafc; + stroke: transparent; + rx: 8; + ry: 8; +} + +.db-table__highlight .db-field rect, +.db-table__dragging .db-field rect { + fill: rgba(47, 122, 242, 0.1); +} + +.db-field__name, +.db-field__type, +.db-field__pk-icon, +.db-field__fk-icon { + font-size: 12px; + fill: #1f2937; + alignment-baseline: middle; + dominant-baseline: middle; + text-anchor: start; +} + +.db-field__type { + fill: #64748b; + font-weight: 500; + text-anchor: end; +} + +.db-field__pk-icon { + fill: #fbbf24; +} + +.db-field__fk-icon { + fill: #38bdf8; +} + +.db-field__nn-rect { + fill: rgba(14, 165, 233, 0.12); + stroke: rgba(14, 165, 233, 0.4); + stroke-width: 1; +} + +.db-field__nn-text { + fill: #0284c7; + font-size: 11px; + font-weight: 600; + dominant-baseline: middle; + alignment-baseline: middle; + text-anchor: middle; +} + +.db-field__tooltip { + padding: 8px 12px; + background: rgba(15, 23, 42, 0.9); + color: #f8fafc; + border-radius: 6px; + font-size: 12px; + line-height: 1.4; + box-shadow: 0 12px 24px rgba(15, 23, 42, 0.24); +} + +.db-ref__path { + fill: none; + stroke: rgba(30, 64, 175, 0.55); + stroke-width: 1.5; + transition: stroke 0.15s ease, stroke-width 0.15s ease; +} + +.db-ref__path-active { + fill: none; + stroke: rgba(59, 130, 246, 0.85); + stroke-width: 2; +} + +.db-ref__highlight .db-ref__path { + stroke: rgba(59, 130, 246, 0.9); + stroke-width: 2; +} + +.db-ref__active .db-ref__path, +.db-ref__active .db-ref__path-active { + stroke: rgba(37, 99, 235, 0.95); + stroke-width: 2.5; + stroke-dasharray: 8 6; + animation: db-ref-flow 1.4s linear infinite; +} + +.db-ref__hitbox { + fill: none; + stroke: transparent; + stroke-width: 12; +} + +.db-ref__endpoint { + fill: #1f2937; + stroke: #f8fafc; + stroke-width: 2; +} + +.db-ref__cardinality path { + fill: none; + stroke: #1f2937; + stroke-linecap: round; + stroke-width: 2; +} + +.db-ref__label { + fill: #475569; + font-size: 11px; + font-weight: 600; + dominant-baseline: middle; +} + +.db-tools, +#tools-layer { + display: none; +} + +.db-tooltip { + pointer-events: none; + overflow: visible; +} + +.db-tooltip__content { + overflow: visible; +} + +.db-tooltip__content-card { + background: rgba(15, 23, 42, 0.92); + color: #f8fafc; + border-radius: 8px; + padding: 8px 12px; + box-shadow: 0 12px 32px rgba(15, 23, 42, 0.35); + min-width: 160px; +} + +.db-tooltip__content-card .q-card__section { + padding: 4px 0; +} + +.db-tooltip__content-card .q-separator { + background: rgba(248, 250, 252, 0.16); + margin: 6px 0; +} + +.dbtt-title { + display: flex; + align-items: center; + gap: 4px; + text-transform: uppercase; + font-size: 12px; + letter-spacing: 0.12em; +} + +.dbtt-note { + font-size: 12px; + opacity: 0.9; +} + +@keyframes db-ref-flow { + from { + stroke-dashoffset: 0; + } + to { + stroke-dashoffset: -20; + } +} diff --git a/ER-Model/plugins/er-model/web-ui/src/css/_jointjs.scss b/ER-Model/plugins/er-model/web-ui/src/css/_jointjs.scss new file mode 100644 index 000000000..75c5a60f8 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/css/_jointjs.scss @@ -0,0 +1,186 @@ +/*! JointJS v3.5.3 (2022-02-21) - JavaScript diagramming library + + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ +/* +A complete list of SVG properties that can be set through CSS is here: +http://www.w3.org/TR/SVG/styling.html + +Important note: Presentation attributes have a lower precedence over CSS style rules. +*/ + + +/* .viewport is a node wrapping all diagram elements in the paper */ +.joint-viewport { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.joint-paper > svg, +.joint-paper-background, +.joint-paper-grid { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +/* +1. IE can't handle paths without the `d` attribute for bounding box calculation +2. IE can't even handle 'd' attribute as a css selector (e.g path[d]) so the following rule will + break the links rendering. + +path:not([d]) { + display: none; +} + +*/ + + +/* magnet is an element that can be either source or a target of a link */ +[magnet=true]:not(.joint-element) { + cursor: crosshair; +} +[magnet=true]:not(.joint-element):hover { + opacity: .7; +} + +/* + +Elements have CSS classes named by their types. E.g. type: basic.Rect has a CSS class "element basic Rect". +This makes it possible to easilly style elements in CSS and have generic CSS rules applying to +the whole group of elements. Each plugin can provide its own stylesheet. + +*/ + +.joint-element { + /* Give the user a hint that he can drag&drop the element. */ + cursor: move; +} + +.joint-element * { + user-drag: none; +} + +.joint-element .scalable * { + /* The default behavior when scaling an element is not to scale the stroke in order to prevent the ugly effect of stroke with different proportions. */ + vector-effect: non-scaling-stroke; +} +/* + +connection-wrap is a element of the joint.dia.Link that follows the .connection of that link. +In other words, the `d` attribute of the .connection-wrap contains the same data as the `d` attribute of the +.connection . The advantage of using .connection-wrap is to be able to catch pointer events +in the neighborhood of the .connection . This is especially handy if the .connection is +very thin. + +*/ + +.marker-source, +.marker-target { + /* This makes the arrowheads point to the border of objects even though the transform: scale() is applied on them. */ + vector-effect: non-scaling-stroke; +} + +/* Paper */ +.joint-paper { + position: relative; +} +/* Paper */ + +/* Highlighting */ +.joint-highlight-opacity { + opacity: 0.3; +} +/* Highlighting */ + +/* + +Vertex markers are `` elements that appear at connection vertex positions. + +*/ + +.joint-link .connection-wrap, +.joint-link .connection { + fill: none; +} + +/* element wrapping .marker-vertex-group. */ +.marker-vertices { + opacity: 0; + cursor: move; +} +.marker-arrowheads { + opacity: 0; + cursor: move; + cursor: -webkit-grab; + cursor: -moz-grab; + /* display: none; */ /* setting `display: none` on .marker-arrowheads effectivelly switches of links reconnecting */ +} +.link-tools { + opacity: 0; + cursor: pointer; +} +.link-tools .tool-options { + display: none; /* by default, we don't display link options tool */ +} +.joint-link:hover .marker-vertices, +.joint-link:hover .marker-arrowheads, +.joint-link:hover .link-tools { + opacity: 1; +} + +/* element used to remove a vertex */ +.marker-vertex-remove { + cursor: pointer; + opacity: .1; +} + +.marker-vertex-group:hover .marker-vertex-remove { + opacity: 1; +} + +.marker-vertex-remove-area { + opacity: .1; + cursor: pointer; +} +.marker-vertex-group:hover .marker-vertex-remove-area { + opacity: 1; +} + +/* +Example of custom changes (in pure CSS only!): + +Do not show marker vertices at all: .marker-vertices { display: none; } +Do not allow adding new vertices: .connection-wrap { pointer-events: none; } +*/ + +/* foreignObject inside the elements (i.e joint.shapes.basic.TextBlock) */ +.joint-element .fobj { + overflow: hidden; +} +.joint-element .fobj body { + background-color: transparent; + margin: 0px; + position: static; +} +.joint-element .fobj div { + text-align: center; + vertical-align: middle; + display: table-cell; + padding: 0px 5px 0px 5px; +} + +/* Links */ + +@font-face { + font-family: 'lato-light'; + src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAHhgABMAAAAA3HwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABqAAAABwAAAAcaLe9KEdERUYAAAHEAAAAHgAAACABFgAER1BPUwAAAeQAAAo1AAARwtKX0BJHU1VCAAAMHAAAACwAAAAwuP+4/k9TLzIAAAxIAAAAWQAAAGDX0nerY21hcAAADKQAAAGJAAAB4hcJdWJjdnQgAAAOMAAAADoAAAA6DvoItmZwZ20AAA5sAAABsQAAAmVTtC+nZ2FzcAAAECAAAAAIAAAACAAAABBnbHlmAAAQKAAAXMoAAK3EsE/AsWhlYWQAAGz0AAAAMgAAADYOCCHIaGhlYQAAbSgAAAAgAAAAJA9hCBNobXR4AABtSAAAAkEAAAOkn9Zh6WxvY2EAAG+MAAAByAAAAdTkvg14bWF4cAAAcVQAAAAgAAAAIAIGAetuYW1lAABxdAAABDAAAAxGYqFiYXBvc3QAAHWkAAAB7wAAAtpTFoINcHJlcAAAd5QAAADBAAABOUVnCXh3ZWJmAAB4WAAAAAYAAAAGuclXKQAAAAEAAAAAzD2izwAAAADJKrAQAAAAANNPakh42mNgZGBg4ANiCQYQYGJgBMIXQMwC5jEAAA5CARsAAHjafddrjFTlHcfxP+KCAl1XbKLhRWnqUmpp1Yba4GXV1ktXK21dby0erZumiWmFZLuNMaQQElgWJ00mtNxRQMXLcntz3GUIjsYcNiEmE5PNhoFl2GQgzKvJvOnLJk4/M4DiGzL57v/szJzn/P6/53ee80zMiIg5cXc8GNc9+vhTz0bna/3/WBUL4nrvR7MZrc+vPp7xt7/8fVXc0Dpqc31c1643xIyu/e1vvhpTMTWjHlPX/XXmbXi3o7tjbNY/O7pnvTv7ldm7bvh9R/eNKzq658Sc385+Zea7c9+avWvens7bZtQ7xjq/uOl6r+fVLZ1fXP5vuqur6983benqao0587aO7tbf9tHYN6/W+N+8XKf9mreno7s1zpVXe7z26+rjS695e2be1hq3pfvS39b/7XcejTnNvuhqdsTNzZ6Yr97i/+7ml7FIXawuwVLcg/tiWdyPHi4+rD7W/Dx+3RyJXjyBZ/AcVhlrNdZivXE2YAgbMYxNeBM5Y27FNmzHDuzEbuxzjfeMvx/v4wN8iI8wggOucxCHcBhHkGIUYziKAo7hODJjnlDHjXuKrjKm9HsO046rOI+Fui/rvKzzss7LOi/rsqbLmi5ruqzpskZ9mfoy9WXqy9SXqS9TX6auRl2Nuhp1Nepq1NWoq1FXo65GXY26GnU16srU1WJJzKJnLjrbczJIzTg149SMUzNOzXgsa/bGfbi/mY+e5uvxsOMVzXXxYrMUL6krnbvKuYPqanWNulbNOXcrtmE7dmAndmOfcTJ1XD3lu2Wcdt4ZnEWl7dMgnwb5NBgX/f8DanskqEJxD8U9kjQoRYNSVJGgymWlWyitxQPNk9Qm8WBzkuItVPZQ2ENdKyUVKalISUVKKlJSkZKKlFQoS6hKqOmhpjVrgxT1UNRj9lpKeuKVmCWPc5p7Y67aia7mI/zbQs0j1OyN7zVHYyFul97u5gR1e/k6wdeJuLP5Gm8neDsh05vN9mazvdlsb44nm9X4TfONeNq5fXjGe8+qz6nPqy80t8cfqPyj4xXN6Ugcv6S+3CzESjpW0TCovuHz1Y7XOF6rrnf9DRjCRgxjE95Ejo6t2Ibt2IGd2I33XHc/3scH+BAfYQQHcBCHcBhHkOJj1x5Vx3AUBRzDcXzisyI+xWfIXOOE90/RWMZpes9gio9nVXPK9UdkYYssbJGFLXHRe92y8KUZqMrCl/Edee5UuyRqPm7x/iIsaw7Jw4QsVGXhiCyksjARv/T9fqx0ziDWYL3vbMAQNmIYm/Am9jl3HKd97wymXOOsWsE5xxfVn1HUR00fJX2yUInbvdvt7MVYgju9lqr3tJXl4l5n3sf/+5sZdQOU7TWnBfNpLo2xyhiD6mp1jbpWzTl3K7ZhO3ZgJ3bjLeO9jT3Y277HBvhbpXyAvxX+VnTQp4M+6vuo7+Nrha8VvlZ00Rc3Ut7vyv2u2u+K/c7sd2a/b/b7Zr9v9sddnM9xu5fbvdzOyXsm75m8L+R8TsbvkOtUrlO5TuU5k+dMnlN5zuQ5ledMjjNZzbif436O+znu57if436O+zk5S+UslbNUzlI5S+UslbNMzlI5S+UslbNUzlI5S+Usk7NMzjI5y2QsNWu9ZqvX/TqHO11Wr/m4xfEirMcGDGEjhrEJb2LK987hp9w5+a05vTKfv25e0OsFvV5wD0/o84IeL7hXC+Z03Fo5bl7HOXuSsyc5e/Kac3nAuQdxCIdxBClGMYajKOAYjqM1zyfUU8YtYxpVnMevYtZXEzEXneiKe3SxMOart+upW64XYwmW4h4sa74gmX2S+bpkLpPMPh1O63Bah9O6m9bdtM7e0dkRnb0TK429yriD6mp1jbpWzfl8K7ZhO3ZgJ3Zjn7EPGOcgDuEwjiDFKMZwFAUcw3Fkzjuhjjv3lPHLOO1aZzClp7NqBeccT/usivO46L07zPywmb/VzN9q5ofN/LCs9lmHSzqs6rCqw6oOqzqsSsWwVAxLxbBUDEvFsFQMS8WwtbFkbSxZG0vWxpK1sWRtLFkbS7qq6qqqq6quqrqq6qqqq6quqrqq6qqqq6quWnNXlbJbpYwuczJpTibNyaQ5mTQnk+ZkwopR5eckPyf5OcnPSX5O8nOSn5NWgKoVoGoFqFoBqryajGe+vldv/tb9mrhfE1caat+vi9UluLO51BWHXHEoHvvqfzzp5kk3T7o9l+51Hyfu44Q/3e7jhEfd7uPEc+kh93IiEb0SMeC59Gep6PVcGpKKXvd4IhW9EtF7zXs95/tbsQ3bsQM7sRvv0bMf7+MDfIiPMIIDdBzEIRzGEaT42HVH1TEcRQHHcByf+KyIT/EZMtc44f1TNJZxZb2YRhXn8fDlJ3/xqid/nrM1zuY5W7QC/pCjRU7ul6pRDtY5WOdgnYO7OVfnWp1jZy4/sWvtJ/Zq9dLTusahIoeKHCpyqMihIoeKHCpK3ajUjUrdqNSNSt2o1I1K3SgX6lyoc6HOhToX6lyoc6DOgToH6hyoc6DOgbpu67qt6bZ21ZM3f9WTN6/7mu5ruq+1n7wvc2ABBwY4sIADCzjwOgcSDrzOgQHZystWvu1Ea3VZ5L0rK8ylfF1aZS7tfRLuJNxJuPOCfOXlK8+lRL7ynErkK8+tf8lXXr52ydeIfK2Tr10cXMDBhIMLZCzPxYSLC7iYcHGAiwNcHODiABcHuDjAxYFrrkrX3vMkHE44nHA44XDC4UTO8lxOuJxwOeFywuWEy4mc5eUsL2d5OctfXsESziect9Ok9wym+HdWreCc42mfVXEeF733Ey6nl10tcLTA0QI3C9wscLLEyRInS9wrca7EtTLHJjjVWptT7qScSXVf0H1B9wXdF3Rf0H1B9wUdlnRY0mFJhyUdlnRY0l1JdyXdlXRX0l1JdyXdFHRT0k2qm5TqlOqU6lQ6ZrXuFHRihQS92PwvNTX7m6K9TdG+pmhPUrQnKdqTFO1JivYhxfiuM0ecOWJvV3P2iOfRZs+jumfRZvu3mtEaUpAZrWEv1xpxxIgjRhwx4ogRR4w4YsQRI47ETXK7XGaXU7W8ndlWXlc6HsQanMYZXJqH5eZheXseLqrz+ZvxN+NvaxfT2sFkvMp4lfEq41XGq4xXrV1JxquMVxmvMl5lvGrtQrKY59rrXHtd+5lzrWfIlO+cw/fdbYWvz7rF8aL2fDfoadDToKdBT0PiCxJfkPiCxBckviDxBYlvzWuD1gatDVobtDZobdDaoLVBa4PWBq0NWhu0Nr5WcP3Xu6UrO6EZ8So/5+qm047iZv54asWiWBw/ih/b594Vd8fS+Lln8C+sGff6LX9/POC30IPxkDX0sXg8nogn46n4XTwdfZ5Rz8bzsSJejCReij+ZlVUxYF5Wm5e1sT42xFBsDE/eyMV/Ymtsi+2xI3bGW/F27Im9fr2/E+/F/ng/PogP46PwWz0OxeE4Eh/HaIzF0SjEsTgen8cJv8hPRdlcn7FbOGuOz8V0VON8XPw/fppwigAAAHjaY2BkYGDgYtBh0GNgcnHzCWHgy0ksyWOQYGABijP8/w8kECwgAACeygdreNpjYGYRZtRhYGVgYZ3FaszAwCgPoZkvMrgxMXAwM/EzMzExsTAzMTcwMKx3YEjwYoCCksoAHyDF+5uJrfBfIQMDuwbjUgWgASA55t+sK4GUAgMTABvCDMIAAAB42mNgYGBmgGAZBkYGELgD5DGC+SwMB4C0DoMCkMUDZPEy1DH8ZwxmrGA6xnRHgUtBREFKQU5BSUFNQV/BSiFeYY2ikuqf30z//4PN4QXqW8AYBFXNoCCgIKEgA1VtCVfNCFTN/P/r/yf/D/8v/O/7j+Hv6wcnHhx+cODB/gd7Hux8sPHBigctDyzuH771ivUZ1IVEA0Y2iNfAbCYgwYSugIGBhZWNnYOTi5uHl49fQFBIWERUTFxCUkpaRlZOXkFRSVlFVU1dQ1NLW0dXT9/A0MjYxNTM3MLSytrG1s7ewdHJ2cXVzd3D08vbx9fPPyAwKDgkNCw8IjIqOiY2Lj4hMYmhvaOrZ8rM+UsWL12+bMWqNavXrtuwfuOmLdu2bt+5Y++effsZilPTsu5VLirMeVqezdA5m6GEgSGjAuy63FqGlbubUvJB7Ly6+8nNbTMOH7l2/fadGzd3MRw6yvDk4aPnLxiqbt1laO1t6eueMHFS/7TpDFPnzpvDcOx4EVBTNRADAEXYio8AAAAAAAP7BakAVwA+AEMASQBNAFEAUwBbAF8AtABhAEgATQBVAFsAYQBoAGwAtQBPAEAAZQBZADsAYwURAAB42l1Ru05bQRDdDQ8DgcTYIDnaFLOZkMZ7oQUJxNWNYmQ7heUIaTdykYtxAR9AgUQN2q8ZoKGkSJsGIRdIfEI+IRIza4iiNDs7s3POmTNLypGqd+lrz1PnJJDC3QbNNv1OSLWzAPek6+uNjLSDB1psZvTKdfv+Cwab0ZQ7agDlPW8pDxlNO4FatKf+0fwKhvv8H/M7GLQ00/TUOgnpIQTmm3FLg+8ZzbrLD/qC1eFiMDCkmKbiLj+mUv63NOdqy7C1kdG8gzMR+ck0QFNrbQSa/tQh1fNxFEuQy6axNpiYsv4kE8GFyXRVU7XM+NrBXbKz6GCDKs2BB9jDVnkMHg4PJhTStyTKLA0R9mKrxAgRkxwKOeXcyf6kQPlIEsa8SUo744a1BsaR18CgNk+z/zybTW1vHcL4WRzBd78ZSzr4yIbaGBFiO2IpgAlEQkZV+YYaz70sBuRS+89AlIDl8Y9/nQi07thEPJe1dQ4xVgh6ftvc8suKu1a5zotCd2+qaqjSKc37Xs6+xwOeHgvDQWPBm8/7/kqB+jwsrjRoDgRDejd6/6K16oirvBc+sifTv7FaAAAAAAEAAf//AA942sR9B2Ab15H2vl0sOha76ABJgCgESIIESIAECPYqik2kSFEiqS5Rnaq2bMndlnvNJU7c27nKjpNdkO7lZPtK2uXSLOfuklxyyd0f3O9c7DgXRxIJ/fPeAiRFSy73N9kktoDYeTPzZr6ZN29A0VQnRdGT7CjFUCoqIiEq2phWKdjfxSQl+7PGNEPDISUx+DKLL6dVysLZxjTC1+OCVyjxCt5OujgbQPdmd7Kjp5/rVPw9BR9JvX/2Q3ScPU4JlIdaQaWNFBWWWH0mbaapMBKLoyJ1UtJaM/hn2qql1GHJZMiIpqhYEJescOSKSV4UlqwmwSQZ2VSKksysYBJdqarqZE0zHY+5aauFo/2+oFmIC3Ck8keY9zmnz2r2u4xGl99cmohtpBkl0wE/9GD+qsXn4hJMHd0792JkeHRDKrVhdBjT+zLzOp0AerWUlaqiYIBUWNTHZ1R6SqMIi6YYEm2EZobPiAwv6YA2js9IdhSmqqoxCSoOATGhkoXDl0c1NGfieBp5ckeM4ioUzr77kGCxCA/NHxF+jVGUYjU8P0HVoyEqHQN+iSXxtBHokHhzPD5To4gZDeFp1pOsC9jjUo0yMx2oqIwH7LEZrYrcUrpT9fiWFm7pBJMTbiGxISqWnZRKjJl0SZk2PN1a4tPAB/OSGQZgM2akRhQWE65Xmx/7ww8pa1grxiKcqD8hRdSnWJE/8WrzbX+YItdNcB3+LIyvm3jJqT4lxvhpNqY3w4PJbx3+LUb4aSHCm/Ezpt0lTrjuIb8D+LcY5qcrwib5bZXkbfAh8fwfJskVeE8dfs90Kv/OenydodL6cAT+oVYrq9TpeRih2xMIV1RGYvFkXao+cr5/YqsLy6cRtaC42ZtM2OPmZtSAGK85HrNaVExcpQz5GThWeRmQWW1N0uxlOBRGZjgr8Zq9YzTzL6uyc0pF+T+NK5ym8GZUvTlcjMb/XcmWvbHqf3jY7H9tKufMaCz7D2OsUwhveo0TUAJVr8r+A/oNq9Xy6K6QD6GHzZZsA/obj1qR3Q7n2YOuymy9IKgU6L7sVrsJ/a2hHt1FwSx8MHtK4VceoxqoZdRK6m+ptBVrIkyKdk1GDIJAh6Mif1JqFDJiIy/VgRRrOBB3TZ06PLOSo4pBWUMxsYaX+uFWRMhII7KAW/5j9hksSIUYAkm6Tkht7CnRdoKdtrbZgMshfrog5AKmB/FvsY2fbsfXGWra5gq1Eba/aLW5CoJt7QuclRpBCKIyJenq4FWbklbWwGt3SuwXRH9KjJgkrxtmblV1C0rAhFXYzRGmFiZvC8IyULmRXaX0+yJ0iHGzeDIbEeZ8MoLMFjdtN3MMaob3w/0HC/SCpjBU2z2R8i67fkdr7c57tmiQ0Vii3/Fgm13L68taN3a4q7aM99cVN+5/fKceGQ0l+mPvjFau2J4qWnHxihBKDl+zprJm9f7m50uNNl9pwMXQt9lqR46u7z62s4X5Omf+vmqg1S94y4Ls3EtGX1nt8g1NYw9e0s3+1GD+s3KS+X3L2taIha5VVA9sOfPXbN3aI12d69srzBTFUuNnf89+m32FMlMhsB2dMJe/TKVLYQanW7HZ62Uz6QqQYprFk9nPZmZWJVpZQ1haBYdOIzl0shkkjhMLYzFmRAsvuUF+WjjU8lI1HHbBYRcvDcJhA0zbCXh1WwRT2siWplIpabALjhOtlSlsKVf1gtFsqIbLficcaakUWE3zOVYzQieBx/FYM40Z7PdxtJkIBSn96DPeOB4dPtDSsn+kqnrVvuaWA8PRwUDTcCQy0hIItIxEIsNNgTKFUWnius783mCjV1atPNAK745Wj+xvajm4smpFoHk4GhlpCgSa4N0jzQHFwMQtayORtbdMjN+MX28eHzzQ7fN1HxgcPNDj8/UcODPJ3qPWnt5lQmMTt6yLRNbhd05EIhPwzv3Lvd7l+wcHDy33+ZYfAju69+wH7GGQRSs1TF1HpeNYCo1YCstUmbQBC8ANB24D2ELKbdOALxohXG8Dn9PGS2rgqx/mlh9MHByawNqDtSvHcwms/Sp4dfoF04yBbVy2ImBPiSZB7EuJ5aZ0qDpJeO9eBrcpdXUS35a5Dgpdm+OpXYk1PhiKMJiTVovNDlxPYsZzSIWdRhRxzGKmJ1EwxDF7a9dd3dvTU7P5xpGuy9YmaU7vMKg5RuVvHG9s2ra8dPVa9K1IUk3r9Sm6qwVVrzU5+B9F9l37lZUDX71k+dbGzYfrl199YH0oW65kO/f2l6GLem/cP1Y4fP/Y8ssm4tGhXSlGwRp0BV3N4WDXhrpV949lm3of7TMYN31vffZdtfHvayfaAvGtf7Fl8PBgyNswWI3+nlUVDW0+CK6LQth3IgPxnX7Zc+bcJhJ1eZ9JfvRLneW8h1zkF+HzvpH9kEbKAsoJMwqJLvIZBvj7AvnvMUvtNrDeSuCgCR8ZUYT5hrttajBsUF12xRWXq7jw4FSbm77hyL/+8tdHC1RGre5vsmv//d+ya/9apzWqXUf/9Ze/gudMZj9EL5HnJOTnaE+KVGzGIJtRAy+xsgrgB0sGLcwwWm0HKYusIDLYrtlrkglTbQ0dCoZqWpCbwVNGFQpOqi+//IqjKsSFV0y1FxW1T60Ic7/Q6v4aPflv/46e/BudllMXHP31L//1yJFf/fLXR1wqzMOrmHvoNHuKqqWSlFgSndHoKRXmYCIqlpyU1LFYbCZA6JK09lhMSgJFgRLBNM1yxWWgaZgvSTtY1AhqQnGrRalqBpdnBz6DmfUgVSiCQm5UhPy1NYkkh4woBFoHihm6quAt3sKpVbWsWm/l33KdMBaYTC7+Lec7RqtBiS/rbMYTrrc4l9ns4tiByEGt2WR2m/75n0xus2DRHIgc0GhpRqM+ED2oEQRTgfDP/yQUCEZBs7/ygFrDMFo10ZED1CuKasVfUjqYlyIVFVVxCSkzIhtLUwjjEkqrCacRhQ8Rg6elnoiDjkkasHyKWFqjxfc0KnibVoMPtZQGpCKrRK0XlMpr9Qp+4QB6eQi9ku0eom/pQ9/PxvqyVegHsp4ezM6hIPUNqoCKU2knNgqMHsxuIVYwkQPIC3gU/xQBc5UUuDIbTGjGSXwchp3gxGw5EWM2NjNJosYHq0srqmxlKb9RrVRoi4udCqVRE6xaE4g3VpePjazwGtVaVqvQlibbSmg6LtOynU7QHfQt4PF9mB8S0mTwDxIVUYlC4RnGimcQ1kB5fNbt6Od0YmQE/+0UYOsyGIdAlS1C1vkDhFH0ArrGSI/6BGieOhcpnwuP4Rlnz5x9lv5H9keUmjJSIhNFoiYqacknqVAC/ASMnKWvNJaWz12v9gqrlXTwNGWxUATL9p39UDGe84edOQqdmkzO/6mBwlLZ0xkWPJ05I5XlfFoO75/ju0zNCKhHJquFxjyPoE+4pb6Vd7w+NfXGHcPDd7y5Z+r1O1ZOdh66d9Wqew915l/pd99E9hfHx1/MZt58M5vBR8j+pnTqkeXLHzkliacf6el55DTm7yxg8RD7TYqnAIkrMfUqFaD+GLFt05wSqUE/haioBtNmyKQZNVZHhgXNVDP4UK0EzTTBaBg16A6CsSAODnR4JIjoKehrTRJ8rS80ix7vQ01zVjTAZN/SwrRRNKFDpx/q71fc4w9lfwNmAFHXAz1h4GeMWk+lKUxPpTaT9mBuGrHKxKOiS+ZmeSztsmASXDA5MG+12E4YMlIN5jHmLevBvK0E7ZYU5WDKjMI0a3MFiLOKY63OYS7MUuKr/KFmJq84KvBWcW/MVoSu12nQfzbtGqioHb+4teui8Xq91kMr6Wr9wOH7xkfuuagjtvpQc7be2x2gD/IWv86hRv/VfPjSK7qHLukPlPfubAog9fovT9ZUbf7y1uHbr72sJVutVpv5FJkb15/9QBGF8S6nbqfSnXi8HGgP14kHxoFxSMeIImkAPTk6Y3n01BMVK09KpcCFUlmnkiAbdxL/kdsB3HDzorn4pCC1ADt64XZpJfCAUQMP3MI0F2vsxGZUcoCkJKoFrjoFsTEl+k3p8krs2rGBxQbAg9zsvN7VnsusKFrEKzfKI6jrQ3q9zsKqlbZA7cDOjnW3rY+Ub3nskg1f2lQdX31Rc9dFYw2c2q1iY4b+w/ePj3zlQGvFwM6mRx9ffuXxySue3N2Atgis1mgxJesbIoVNGy9Jdlw0XL2Mjgztbx842Osr69nZkmMnxkbdh1bXG92v3TF+7/7m9j3Xw3xsA/05yj4H+myjeqm0DmMi4qYNgg4ZwiITlwyg4GqILuxRUXcSwl1JC8gHjK8D640up8WCAQ6olIgEsIx5XbYowwjMrhfceRK0OpFso3+6BmkMxt+NzY0aBWYzvZdm0G+Zd2Y7EjpDdhN61KBL0H8SSi1E1veCrBWAHaLUP1HpMJa1msmk7VjARdrMjNcUtgOF5rjkVWfEYqCwKioaTkpBEGJ1LnSd+yOJbEQ7BDYQ0UhFmlOc6D7xquFXb92Ib7BicURyF6nhGiuZbXDTekK08tMWq9kcflX7lRO/gnfpQD+mPe5iczgNv4tvLb7VrwRVSKXhXfBCzVhtbosnIgegGqvNXuQ2WzzFiwNNBFSB8jiceIaZYOqnKSZINEeOfxaZK6UqZMas83sZYtjmwfa9hVqLITY41b3qy3uaIuvv2lR/fU/rIfq2AvfcH9d0XVZ38OsXNwzd/OKOxr2bhg6WGj0l7sT2ezauOLa+BpvG68othdkiwdh68aMbLnrh6g5rIIrt8W3A4yrgcSFEJ2DRHJjLPnUmrcQ6wFU4lDCFOCVMoWpilotgChXxUghEbwY2x+A1VARQQ8c5VGSOVPjw2Mw6eVZgmyF7BNW5Y1lqoW9bvRXdJvhXZ4eKa22NT29Z//Ch1u4rpV3bnjnSvjG+7oaRsTsma2s2HRuauHNLDfr70ZM30BbH3PfKewPN3U0HHt665amjHW2XS2Mrb9maTG6+cXDkxvXxlq1Xy/70BtDxHpJvci3ScMmoJf4w5wSxHwVoRMJMlEiCzt7A/LVKObdTXWhvpx8ymGbf0PHs7pYKwaU5/TPeynoKrDz+fIa6HHhYBjYpBJH5IPUmlfYTOwyxBEnR9CkzM21JvxF0tS4utangqUOEmbI9Ehux5dHCsTYqNcomCvPVbchMW9wxNYQncHFZFBtxaaWs18Lzb1+J1ZcTWV7sOCGl7KdEJwTsdSknCcxZZ6qDqOMM66yTD0lQvqwRZGX0VyaJrJLYyrnBi0p9bXBk0abmoxKmdhEmUMno9byR4ZLzyyOrLu5q2drur9/7wOZND+xt8HduaVl20arosiue37nzG5cvm6zdcsvIyM1bEsv2Hmtqun5qWTQ4dNmqkcuGSsLDRwYGjo6E0dVDV65r4k2tY3uaB26aTKUmb+5vmhprNRmb1105tO7uncnkzrvX91wyGo2OXtKz8er+4uL+q+md9XtHY7HRqYbmqaHKyqEprNsiyD0GcnGDdwTdNlP5ODuizsy4AmYcXLtUspMEcXiAzR6eQA1tzi2WeTCMtrvMhF+RAOi2lrKnlsbMKgSGDkdrBH98gkli1+XHJzc9dnGrPdJenr3e6B9DX/fUWBuObxq/Z2/z5tj4Vf1rbtlQFV93Vd/QjRsTCuX6Rw63tx15envdju1TTXM/dtCrwwOB9uUNU/dNDl0zHm3cdKRpEKZ1fN01BFPdDZhvmPkF6LefqlxAfaI3Ktkx5gsQEIsNtzUjFpIXqeR8yE849/Ru42IgmDz3bEnWdGwJSiR0AaaW6aqkOnIW3Ap0GaMyFo1ERdNJiSqGmMUBlGnJixQFvjtM8+kLSrKGwbU4PpGmCJovBLqX0K08PwZnrj6H5DnqUzH5E8jIPKEYBD9JmWsRsRRKFYToOHB6gqH0/Nx3fKVhD50wGugHytGtHTpek/1XQavhs79UC7oOzI9n0X8yp5jLSD7dJSN7CHMA1LNYCdVRSTNviRD8PMsMzkrMIPrPvj7U2t9P6IB/RgWS6UAEkiVwpIaCTQhZEdIb6WRxmSUgzH27gKGQsUNnUqFiXsNyauTmbB3ZS8qBDt/ZD+kfwLwopeqpKSpdh+US0ecwuBdj8IaoaD4pmTic4Zi2m+IcTAWQUFlUiltJ1qMQTxKBpIglkxlPEm+kDic94oLIp8RCAOrE1XkjcI/SmoJyxmMeAimMyB8CG6PIzxGAu0vE6yvsGtlSv/yqTXVVvav7amh9B1vdM9pTHe7dVNu5pTOkMqpf5FzeRZEKGy6Ml9rDQxctX3FgtK2u3vfMN9nylsamgcmu5Jomj78ioD8zcB493X9WryxlR6gV1Gbq25TYG5Va2Ey6pRfDw5ZOgIfGqGiNS2FFRlwVE9dHJQ+bEWtBbBhabiG2ox5YVc9LLmDHIMSkgzzG+DNBOVsQ5KUqzC8uI22V7XdT5vffku33OC9OnJD8ylOi7wQ17fOPTxC7PX9EsINpUDC9yFo9tS2964GRUlUQT4/2bjI9jC0ksSqth2nygpZymarqc+klUyKwiJ8h2TjJht1mZzjQ4nPsFMIpE5siHktgMOtBSoXfFwjSJfl0kzmCsKT2H/khsj9yy+xbFzfsvG1wYi2d+otVqVV1Be3XvHZJYlNwvV5vD1a76vcMV2197tfX3D77xoGL/w5pvnrvme0qHafkL8q+/8zx7M/+8Ur0nqWssaxksKfFNuys8a+7Z1c9HXsOlbx32ejx008eePn6no3jG0dLuzYk13zz9jGTKftQtM9dWefVNR36y8l7//VrPVPvZD967IXs+69sXNbOcsH+4anvo4o1Zd1xt7N13yhqUqn7jn4NyxcMIusC/28AjFshR0mAa2WYq+EogLmSBs9AexRj2lxEZsZBD4qTXBSD8/5+sxfBVAMoY6RX7qJXruTM7HNzdc8qLMYP6VuyP1VxahWnYo+fXmM0oCeza3UCzdE/EyqdTpwJxjjhPfBHXwM6LJSHKqf25OI1K8QvBI+UQ9BS7CHkFGNywkSzrGaMbQGTkqSj0ZyZVhmdAAqCcD0YlVQQHFfAjaAVaNaDOnjwgTElFgtwKpabRBUeiOBdEnqUeGMJIneIN4kKBP3e99BjV7xwaX1p/97u515pv/LFi7NfRlN/9U7Nli+tzX4FNUzetTb86lvZv2OPV2+8dU1qz0S7yfXNv1j3lR2JVU9+tWtff9lAfNWeui/fQ+zl1Wc/YCMkLo1T6Qgep1ubszAW7bzLdVqIn6Uki1swzWgpQ7DsXN2VVwEUckY0p4cYSXrkXCiir97xOmIfHjx2cFtVsdqkKapoXn2w+/pfPDIx/sBPrlhx2faxMKtValVllbuvumfintMzk/S7TyL+r/fYK9rDEb21OFhsXXv8w6/e/+HT46COIYVSVVE1kCza9TYyEdsAMmMfAJnpKSdVl5OYgclJzMlk5nOQIA6DvHSmssjpSMmJY6J59ucTFCXe/JTzvkfzD2Rf3LbtxewD2Qn01LGf4mTET49lJ9jjk29k//j0M9k/vjE5uvqJ39137++eWE34inWoAejRUd05ajR5ahRMZoZVE/1hMWF6QpjGLKfISPpMowNrRsfkXFkuQSYnx+Sf95jJOSV92dyN9Gn2+Jq5F0fnnlhDnfNcDdUqP3fhmWqWPFONn6k9zzMhKs89ULfkgfLj7p6bwg97ZM3cdmped7aC7tRQ+6l0FdEdZkF3ZkrKqjByK8GOqjavRqKTl/zA/DAE9v4wfq6/FJ6YwDl7J1hLga3C2dmwIBm02GqWgMKJ4ZRkKSMOyuA8j97Np+JziocD2SbkFbDqgWG8evsbyPD0yO1Hd1UVagSN2tiw9Wu77/jNo2PjD//LjX2X7d5Ylf0PHY++lDh8w33rHspmX91Ov/sMEt7eZatoK680KpSV1aGJZz685/6Pjk8YPRUF6CZOk5qbCzaUWnPqJ/OdrSXybslZLpVsuUQ2PsNoCecZ1by0dWYcmos6sloBMiD2IS9nvCgfx/G48N5u5rZdu2YPs8fn1tFPnF5DvzjXKz9vDn5th+cxlHeRnHHqkWTr4dPwDzv/iXO7sMWT/3bt2Q/o78LfuiAOkiNJHZMBWkQljnAoiCoF8lkFZJnSDJ9TiKeJDqdTmZSoFEQFzqWSVY/5mFhewQcrvJZmEK3nNK5AxL3iyrHI7qb9j01GNhq4IqOGU6lV1dse2Ml8a7b+slevbuUIPX8C3vnY5ygflcrxzpbjnQF455V5h7XITwbnI7yTApgmxgs0mVLyGOXFFrIERnLmduIUUIQJI+FPO1ebixwWPb2cL7SOzt1kdpttPoF+cLTAZph7QGe2e53rwU1sZrScjh7nublLLKBbLuvccgCKh3SCjp1blpMz83vgHZv3UBKTm9dIVOZ5n2aofDpRUi0I1freTloEMYjj8zqj3A+f5cnPVVHIjdsYz9dXeAQS7OBMpAA4DtdTmCDYEdU4I4kzgOrClDx8wArIZgehEA6A+uDsZBj5QshmFd5bzgkaerlRrzRo6JRa4HrWK+b+hivgXca5Fxn2uNIwyxd5eS/H/N6gPL1G8eOColl9QQHzX+6CM5WL9duUt66iLkerBmg1E1pNAsGceP1NB7RaiI/GNCqNi2gMYlXx58iKA1nMs8y6mIObHQY6VPozDk+h4sTpNRbFf3gKzjRi237V2Q/ZXy/NRee9lF+7kIu2LOSiLf+7ueirtr2UvRes/uQkWP375l7atmf0gZPXHnvvvlWr7nvv2LUnHxil330arMTuXe9kfw8e4Pdv7wJrIDxz3wfPjI0988F99374zPj4Mx9i+kG/FfuIb7JT7Yutsh2QhM5A9FuHk8AOMgw9dlExUS97KRamnxNz0o69FCt7qWIFAQdeJ5oHBX9Cl1BnEdN9w19dmv0D4jbds7vu+9/N/oE9/i//sPHRi1vnXqYfrN1wTf/TMzKWvir7ltIDPMX5pMF8PinP0wrtQiLJMp9IwjydTySxVoeRBNs+B5BlTYkVQlprpFJL2YuDbjILP4vNFcOHe9HRMYtPn/1u211Dn8nxfW89fm0ku1fHoRUFhefnfJ73Pwfe28G6rM1prkHWXMkH7Lc5CPttqnnzYgf2O2KiXVYkzP4AViQ7aI9JKy8cCjjJbCP1EqJPyAslF+Pa8mYHhZETxRfkc/DMn1NT92xymtFHa3mHLlsllJa/Obvpvl113307+zF7/O3XRm7Z2a41uubugPiwz26aO0j/PLL6aP8DX5XtxfjZD5h3QWZN1D4q3YAlpgXbo20gK2k4p16ER1UK10qL8LVSP16Ea46KjpNSpSEjVvKSEYaSMGSkFnitdJBVMdEovKC1FJXEGnBcmDCJxTC6Ui12t47iBHG3udqPnNyU+dBEpVT5ZCmC61XmwpfxIj2vKSqr79vavPqmDdUt26+75bodzcndD00enO51agRD+fKpwcFLV5Y37yB3mi/9+v67/uH5SqMjUB5w1Exc0T2wtb0ynBi+YkPPjTubu3ujAgpGQpUrttf1buqMVCaGj4yvfezSzm0yTwIg31tAviqIkck6jyxaisGLPThYF5UnsRDTrBKzhMVsUrL4UInXHhciebzuGFBsyzI72aHx8dMiO0Q+/ztnf8+a4fOdVJJKW0luWyvbe5GL50ElmHxcUAb+W+LNuaVmhkyL3Fq5ZYmTjNDf2dV08KmdO5+8qHFn313fvfrq793ZT5cx18xeu+2b1/Usv1bcBsfXHPnB/WNj9/8A04FjIyfQwWN/z+NxUrKDxKtY2D1QEsXnYKw55wsSOWfoN45ADIT+02zQmdDvWLNxeO7ZDexxo+HMimhtslKR1gkADcBSU5Tqx/CMEPVzKh3Cz/AUB+PxOHmUxLnjcWxpsV3FsfHbH79/guTsqQgnKniR4iXGcYqFQynkOPVq4+/e30VuB3HV2QlJy58SdSdefcf3fiqf0OdE7wnJrD0lmk682lTxuyr5ugfXNvHY6Tl18HEumIe6UwwFGq7Q6kxmp8tbslAbhlp5Kn/d7Sn2lgRD5ysfk6gQYEuVzS/bp3gMJ4TmfWXMds4p8qNgSAlmS1jjVqN9Sg3L6lTofoWFK8JsvF+lY1m1Cu1lbNxQtm5DdpVaqdRkR9azxwvPjFuiLlfUonhaJwB7xy2VLmeEnIFPzTgLC51n7LLeAq8Vr5B8fnDB99N5tSqKYuNDSTT2niob8Z4aRMSap1IjWxmSCfcLtD6r38FxLHqZUbPouJLTTWZ1tGYHJ7DZpEKbbVWZ9fT/oN/Wa+ZuVBvV9ISam+ucMwMmeMDIzV2nETBNLqApTeLeqlwWlsqDEaucaALltuUySQSBUPJBXuUWMxGmk2steHf0MGdVq60celhp5tbNZXazxw2GuR2OCps97KDv0xlnn597ll6Nn38JPP9pEv+7c9gKcClZ4ZADJS6K7RdFFjmTyIsXAlTIa71Ez9w/e7HCzs3uZB4Omk2sak3AZjk9uwZ/5jQ4w1NKAT4zSjJ5ajYjqqISYsnn4cmr5jNpNcFragOJunIPMecXxuJ4sXQaLTNxP/4xZ8r+QeUJGIRT23hDCYXO/vnss/TJ/Bo7tXiNncFahmWkLi810leWCl41+6PgqazZiunaB3Sl83QZohIDdCnhT3N0KQAGAF0KPaZLgenS5Omy1yQwvJNDHO8+HlPFo87s6xkDr3yA5wJ/xnUxP2DizLcIXsvX81CkGoVYRXN0AZzll7TlBIqcOMFZlB+g9U1owzKdif1Yw7Esp/kTyxuYOH3J3K2cFr0peAS+WMi2q3lZn6nsb5nQ2QjEI3ZcayBRbAb/kFoIOQqxgo1lQrP/+COCo8cUT6KvgC/TgF8majaj1FNGXC1DQtMZ1koZFPlI1EzWbDGBYxucDv2jSb1Jzb7Cmf6o0mIfvw/84hqFHuxWkrqBShfg2eSN51Z32EzagiiSOUpryLq6htOEZ9i434IDcExi3aJVHoxwRDYmuXD9Mi8VGTN4MqbwWjNmlpASY0Kas2BDIhaZRDdMgjhenqHcqZSkYclb5Hx9Ert9kjGNotyimoCPlxSHQZS6r+ehj5+/7EjvjuWVRotOGBL3D1++sizkUXHlIxO7mmu29kU2+JK9pQ1bR3sDf/Hjm1s/bts3XK3Yc8e9ZdVl5qKh4ZrNt47O7Sy6rqy90u5u3dob76uyuyItJUirCDSPEhwknv1IwYKeWkAfVlJpDvOIiksO4IoSs6dYlRFRNLcGgau3JVqIkXQWrqTRGMhKhFRkxWiew3C6GNBDWiMwqRy0F/AYTbkYMARhedI9D358SpW4pTN94LUf1R96cs/u++uUjCNYf+e6iZvXRp55aNsTbeyP5i6d2Jmdy84eeOvO4ZGVV7p+MdbdfuTpyV+f3Lme6NfE2Y+YvQodRF1Ncl2mVACks5h0AQ4E4tIFPQY8lWQINiA5gpVcKAAoo6aK/fPFfAS7yFnWxXmD+WwVPdF8+Ln9Wx9IOVmtWhtoGG8du3l9LL7u2FDv1tagzqAucCyf2FW/+bGL2lD28InbBloSflZd6C1oPvzUjqknDzX6y/xar6c2ZF124zvA+3Gg/Rs53q+h0iY5eiK8JwPwAO81i3mP2Y5BhJqLxSRdjvcFmPesCfROJ4hGnEHEEqDUxkXLXDY7ia2iBG3TZosNJ4kFOR88Dryf2nFP3ZaES6HtfOHgaz+aJLxvuGti4qa1UXQGs36gh153OlLw6LoppEAKzH3ataa77cjTWIewDF4EGZSAf5ik0l4sBUt+EBXKzEyQ8+KMT1AxHz4YDbjiWTTmIgg+F0EYgXLW4sWTSCtIzkKsUBwuhaXwcUoMCgCtFy8kKf3eT4op6c0FERMth5/bu/rLU40Gbs6T2HLb6oGD/ZU6g6rAuXLrodTOr1/eMUk/Wjl8aNnglWvraNO+V27sbzj01B47b7no+UsavOU+LK2gbfnt3/7J8HUT1bF11xKd88Cgr2Rfg9c2Kl2IpQZwrygu2ZUwV2IYd6lVGUmHRwvBeiGpdCuAAdti6YJCrI8FToCY3hzEjC+GzcQyFCEZdoaCnucrhy9aVtzqZJBZX+6JjTb5UF/2pc1fcjPTpdeuuX6sQqeN4pxG+66Bq3pm9zFf0tJyrnogez3zM7B99dQQNYni4LexMDYpM9N28yZ1WHIpMmIiKrUCyX1RqQI0LRyDQEdajQ3fNiKjBj4jNvCSUgc2jicr3StxHoiDaB487kqBmMW1OAaCQzcvdcFhtZBJV3fhMVY7YIzbZUj4pw9OPCkvl/Tz4vITUrn6lBg5wU6HyyPm8KunzCc24SqN6Up8Cm+Z7ulfbg6n4XRRrQZcw7UaL/SXV0aW9+RQ3ov95eGFU3mxZW2pYGrVMGabX5doXb0JBy9uQSwATeprBU2qbsDBKISlOGXlB6tVCmerBUlXAq8u0zTnXrmWWATwp7nq3vkiX5vdiwtS89U/IbIEozzP2roixDFLl9YHdq+PN/LeiKdnZc2mm4Y7DlYituj+InftxhtWji0PVzdtv+7G67Y1tx55dtfUY/uSayLj165acePWVHzV3iNHa0LtVa6Wku7tbe3buwIly7a3tm3vLplaebhYaK+3RSNlfPltG3ovXR0tdvtctC60Odl7ZDRa4Oz0VERtSpU5MtLZcslEoqJvS0flQJ3X3zJWU9XgNQBANZbGGhkqtbGzpKRzQ738ulH23U+BIv0d2Ccr1ZXDovq47BWEnFewzVsmmvgEHOnoDWTrjGSwkjASDK2cH1zwBsTjCbL9F57a3P3CwVXXrApvOXbT5Nc7weJfvmZH7eSd43OH6dvuenzHxJwC25j7gaBB9gXKDDiimUpb5msBjPpM2opwms1xzsYjC9l4ZDeQLIlkn8/3fLJaHgdi93POYrPJ6+B5h9dk8jq5ss3shMnn5Dinz2Qqxq/Fp19mzsyyFH3277M35mgJ4ayuk6SbgAwtwnAdMJsGMFuMZJ80JzE/pu0aCwfzxConn/QaIMbpJ8QwpPAMzPFConQpfXEWGdRu18jQZk/j2mZ39KWltGYfrNarJ0YUV545VjvREdQqv7OEcpClCLJ8E2Tpns+lWuJpHRA8wxRROpxIZWWReggX3USkUjHJpRaB/Pj5XGrifKlUBHhY3FLFOXl0r85hXp1t1pp1vF2PfjrK2fTZVUKRO8r+aPZitRFdrzNmR7UmpdpumMvqDOg7Jm4uS/TtHfgVABoZsKwyjZigXOYaBIl/FjLX72xmf3Q6ktNT9ocEA+zLxQcOP0SnCEYny8QUl0pBY4tieRBQYcALHGIFT3I4fsP8pgCHjA6kCook1cQAdjhgJkQDKRo04RQIjr1YQz5z6SF1gTZ7bmk8p9jcOSpeW6DQuDsG1lQduMFh6li9rbb/6GjllmuP1G7pq9h86cGRO5PMGddXyrviBddd1LKuqSi25UvrsPp/7cHgwEX9+Ojuh7eOzWbzcxLGaqcGcjziciNV44lpVs2nC+3yGO1ycofLT4TcwIwCCdTM1HzykAzlE7MTk77slUMLExQovW9sz5IJKmOZ00DXObnYPAbwq85bF2z49FzsZ2xVabn0+X37nr+kpeUS/Hppy2R07c1r18rbTPBrFGWPvHVrb++tbx05cuLWnp5bTxzZ/uThlpbDT27f9hT+s6ewXXkqey/QrQcbF6DGqbSQp5uwVIOJ94Lm4ACuZB4BszYZAbtz1i6INzNSctLMLUgagVRO4FUrvUUpozCBRCrnQGEnOgcIP1VrEJAG8NfrP2w48OTUznuT9XetxQDs6Ye3PdmavZfdqjM+tG4qOytj4b6+rJHuHlsug+FdG/BYxmEs34CxYDw5LuNJAibxNF9AlNxSRMlhIF8AiNKQQ5TcPKI0yFpyXkSZJOGmcCFEueuBpAYVJbZ0Tu/PI8rkl9cuIMqhgUOu0w/RRRM75xFlwaoegihzc5r+PYzFga29nBmfl4hFlwEbyhefiMo10k4yGpi6JEDDJstIVhfs86sLMusXMpNYs+MCj9TVTxyJrPBzjKC0+6qLL747wpzhTO9dcbvZ3MEjjVZ9101zu/JrYwwL+t1I/ZBK15N1WyUEjvUkcFRowulCTFkIroUIxAv5cMjRFBXtYG0AH1XIfK4VMlKzDIren3zHIoMiMy8KJ6So85RYfQJOpk1mAXBQlJ+uilYDDoLfi3AQ3CQ4SDCZo1XVORx0zhlBQRU4L61UgAw5YVpTGMA1JWKtSfL4sHKGNDiNa/fU5tK4i9brzsnj+j+Zx13rYPU6Q2nz+q62LW2+6qFtU9uGqqNrrlyx/ktNNpVRV1I/2pRc1xqAO3vgTtXaG0anHpjyqTXeoDfQPBKJd0S93lDDaGtisr+yNukD9+Qqru0OVbVWFntLG1c3dRxaVd1JeF579gP6QXYT5aMOydG7HNIVkJDOpgnjLUieuKQmsDut1uXr80nG3k08r6iKpfVufEOPN6G4Sd7EjQvo9bzEcBmcksAugMHLyTRwRifki9Vqk2Q7KVnoztkeHGFgh1eL0yy133Aigz6CWrMnrMG4u6Q25ODVBaEjbTsu/rLOyDwb1KO9Gi57ec/cQHljyGxzWbXhcM2hI/TLBhjb7aBP32DOyHbcgPUbJ9YkZc70iNp43o6D18NJZA1ojTFG7A224xqG1LiIelyvRUlImfPRJKssT8aFiC9C37712I1bv961JVGENN2vHBq9elUYHaBvmzt81xPbJ+jsLFtwz9huMOpULt/HfA9oM+Gcsonk+1Au35fPEFGmCyb4/K5+zqRAQ1ody+o0aJg16Xuzw6uZM0bt7M8c5TZbhY0J6DhAUvhZdvDd/wAIr5z6M5Uux/6sME4eJ3EFOK8cjuLyGDxf3tG+f2w+r8ySvLLCcIqFQ6nccOrVt3/4u5Q8nXy86DkhCcpTouXEq43Z9x+S88eF8GcOXizkJTve6OyAUFp96tV3yt8vJiXiAsw7wQLzzsdPF/s85vC0F/9Ow8VFsw/uwIvoTVGtOgUrmCx2h6fY64sszjwbqdydgkJPcfk5N/PTExhYjtdo/amlLASjGsuv1+LKa7wgKiff8KKtvZczMwipNApWr0YmlbXUrkIGo1ahUSNaXbA8+9xyXpX9LatmGDWb/XeluXOB7WE7E7bbZ9+NhG0VdibgnGVtTIPRY4T/Z//GllszYW4DuRfM5575eJpGueWEwihO+eRzz9bFuefEeVLPAXQg+/B6nHoOKzhkZ3ntRPZBdGg9zjx/l9Vm31PxOlqD/qDXZIcEC7pVY8ia5/4gaNDbFmN2o8aIdQP82feBHhvBg7IKitboQqEXZb2gFpJ93vYhI2jiGqVWweqUaIQ16/rmXlRaTMtmCFt+aywW+GKecei4029wJnQnPKMfeLACnrko15xPhZEqzwvkmvuN9DVzX6F/aZw7Rh8KCVZm80CZTZj9ywHM17bsH9AZpUAtR4cosT4q1bAZUjwKIbgtKvG5DS4tELu0gheO8hmpMBKLpVuipIARacLTndEWCGZUHfG4VA63PWG4XU72zJSnwJYJMbzrhWyYeOOjdfJW8NaIGAZd46WI5pQY5qUOzalX31r1kYZMIW1E9ETw9uNCuOnhJRW+WfxHA5kJWn5arVXBBNDg3zBhposK8Xxw49+vNs/+8XHytgg/XREJw/VK/BueNN3W2gGn7fh3Go4Xpo3YnkrDu/BRRSoNn7boljuVhufgI0AarbxKrdEWFrk9eO9/a1t7x9JVG/SSWlPkrqic36uen081oJXleG8PBCIlKdFmknTFZHbV5kAj9moNiKTuc8m9RbXx+BQv+BTN11jiP2kLNJTbzHZzqGeqs86k9lUsr3Gb7CZnebLInSh3wqG7ZnmFT22q65zqCcEbbeWN9JYWW3nKW7dnz5765j0rKsI6vSc1HKvfP7UnGWyJFquUxVXNwcTU3n31seGUR68LVwzubknB2+t8deV4HiJ99l40DvrCyFXG8yGQMUN+5BAIgX1H+oHsvaqjf75JxkxT2T/QJUTPrqPE5fLaQV1USoKe+aNSKKdnEJJqC0HP2kGRIm2gSO1ky2V7HehZU7tGTZpfYD03OEHdmuBd1c3wLq6JbNFaDuoWXFC3b390j6xuzogIonDyUjVoVIQo1qtvRT/6K6JuhojYFsHldc1ws42XtPim4Y8XET0y8NM6gxYUR49/v9r84R93k+tOftrlLITrBfi3WM1PR6sjcFqFf7/6VtlHPydva+anW5rb4Hor/p2GP1mkXAWpNLwdH0VTaXjbolutqbQe7/tNiTqsd1qd3uB0FRRGAEY1t7S2fVLvdHpXQbSqpfVcvasDPyxx7aB3SQH7Y79JclSmUrnlmEWql9uTgU9BAYNN89tpSP7Sukglw2iK1/gqemrcZpvZWZ5wY12DQ3dNT4VPw9d17ukNWWwWe3l9IFBfbofDUO9UR92vZUVL7d8LitZcVaxUFUdbSxJTU/sa8oq2Yk9zamrP7hRWNNBSUDhQu1TznsEKoj93odcVFnoOrO1qCuyspFVn0layNdeKEZMrKrFwhXWRBXNeM9/rxWMktUg4zOSNci2S0YNDCCvGmi4t9nSOxTEdAZrxXGBHNtjd5W0eT9Xu272tItgcdgwWN0+kavbt2VYRagw7EHq9bvPystLq0oLqztK6zd34sBAOSS8amCvHAZdzVCHY7jSDDbVenwFvhVdLyTqeNYN/pgvUOCFUaMD3REucZGStMRLEFRQCiXoGU6uHQ9Ei733CpC6kZJJxMBWC//1E6aIuNPNNaDYyz5cmOJevFO7VzS2b7z8TmZN75jyenWPOKLJUlKqnbpL3UoglcakWAjJ7LF1LKh5rCzVynIZXARIqnDAmpfwwiCogtkpuVhAE1FpbfFIQw3HJDsdBXlLK1eliAudnbXCgi5HK/mCCRPeSHaPDEhhdohZwP0cJxfNrHov6dXCI9Osg6QycSs+37GCSuZYdj7dd9fJhHTJyJfrxWxMOVmPy1Q2nKgZ2dpXq1GqF07FsYk+DfH/LXx5u2VS19pqhyg1fnqxB2Yv+6tZB+kcGy5/UDVEfq3a4C9jZa2l/qVfBFrtjQTv9Hm7F0X/Da5dOPnKoTcVcybRe/ATWyS6KUkyxLwPXLpI7PkiVTEY+ADea1uHcm0uTmaEUcZ0hLBbH8eqiWCIzLnUSR4QhvC8olg6l8nFZOhXChykKF7am4powZhYlVeIOJ+UpyaUAbeDNsvMgi6r5Dg+Li0oFeY+fQLbjx+UTvGVU6DILxxO7Htm54tLxVltIYxA4S7RlrHno0uEy9B+CIVvT22oPO5ig0zrr8bfHi+ibvEYrqtz4xJHOYNtYtZ0VipuiBbUbb1yZ/XGpzpT99torKhSKMmNRh6GsYagWrZD1CVEQNm+ASD9JraAwIiqDMCgOU1Qpr1wWn5QCoAkBnuSzOC5DFivxFqiXaLVgcRX5daROK14GV9Q6coWW1SJpl6PlpJ1UmytVdlVIbuqgCpFceCKpWpKNeTz2cORAW8uByMOxh0rC5SUPxx+OHGyB80diD5eUl5WwFX3bU6ntfRX5V0V5/GF4Y+Ch+EO5P4yTNz6cP/95altvRUXvNnh3f0VF/3bQhTWgC+3scaqYuliuTMvXusy4ChyUvJUUr2tYYzNuD7lgjEtuuCCAOnhxuRPePYXzYqZY2u7AOmC3gmHjY2mHHZ85XHgvcUzy4USZg1TNALLwLJTPEIyZT4B6reQ/XJBbS/5bs7LAgLaoOVYjoC24nCa7Ak1mb0GXZm/ZLL/A5eOuuTWWgOAL0cd1xtnvNx5pzB5FN8ELqUtb5PtVME7i/dVk+5cihp2/qIxJKrCxmnkMwMg4YACQAFMw+2+K9Uzh7G/kGrc7z17GXEP2Wq+jHqHkuWJTZtI2EinbBBhsNCo1wJUGAjUbEtimrycGp4fPTCt7sMUsADTQw+NeQ1IALpYHRuBiK1xsjWIwipsrbMg3VYilxB5BTIDjNYl14GOFVr3OzHhC0YauwaHxCZyDGDGRMjlbg2B6QcmVx4YmcrYosWiZZWnmQTm/4zoYSp6brADjpAB9lRdd0J0bdtV1L8pGBBpGm1Ib2gLxVXv271kVX70q2UUyEg822VmDzhBq3bCsZWuHv3bswMX7xxJrSrsmtmyP9LSUNI+s21Sxtp/+58GrgsFt/cmtA5WJhN/g9LiKE8tLo8vqotWp7k0to1cFQpPdJGNR51ervcFiX/NIVc2KxupYbffavvL2RCRc4fJuaY4sT1WWl9pDm7FcShU/pKPsEYivS6gaCu9O8sXJhj9HDL9IjC0GChuMiogsZ2CcbiGL7Bm8WgpyN52bG0WBJeelBkcRRDZ2jrMX87zbgVYaHO75C4LbwZp8HnziEXi33WCwF517Ctq35uwflEVgdwvAY63DPY9IjZtXkUmrcFFGWEEFFOGZsX6ryhCWxkCF+sewCvWvxCjSqlKHZ2rbyb1abI+ITs0UytupCuXtVN1CRuzmcfJ0hpO7n2A1CnaDObJ6VeHa+tExYqCa+gXTi1xhsIrqHsUK1C6I9bLzUuDiQ7wZDW8xWZofti822osX9BO5rf5yYmRN7aabnnh9+/Y3nrxpYyKx8aYnX9+x7Y0nbtpU27j75Y/vuOPUK7t3v/LnO+/4+OXdH3Rd/uy22vH+do9DxWl9DeuXjd42mUhsvn5wzVVJvY7V0MWNT16y5anD7fS7297EH4E/+s1t29/IH7+x/c5Tr+7e/eqpO+889dqePa+dumP7s5d18kXlhT5dgacgse2u8XVf2lpTDngaPmt5x9Fn5Xm8lxmmO0AWQdCWq6m0Bc9jjWJx2Yroi85UEJGIsegMS47ymytC4AVCcqMpFuN+B7gCvK0ihON4TgDkWi3AR/nwqqjDJBblNoFLToBsYkyQqKLFFSzm81Sw2HAByyfbG9VyaG944z1Ty/oqGssKdUaVoXpv1449Xp2O1bpiiZaArzlauMziDTt8qViF7esPML8raY8V0zUrVtqdds5eHbl0W/Zqtb7LEXAaTMGGisJSl87o9FvuZJcRvjxC3UJ/h3mYzKMglZsxMy4rpQY+FMdIaYEL4aJks6Mo10in1my32S0qBm/+NMORES25hBd4H/nYzSP1awaNVv+aCgluDp+rXsfnr6sEN23g0DFea9Trsz+xaNWW7I91BqOWR9ef97Icmz2D1jKn6J9QLFWV3zma746j0Mh7BBSkm1JaQfqMKKj5PQK4A45feIZZuYq+pS97E4qAGzxnfi6jBqknLzBDu7rJLOwCrNTVjT+4qwrUpTE2Uz1IblSz+e3sS6bnMjDt3TFxGS/14bw1nNWeM1lXwtW+ZWDErd6wqo3sHa0VIKoSgyaxEXSou0swzcC0pcitQUGs/RyTlhTVyeZ+SbV0AnQujD7/bEVfnXvo0euP6C0aFBjWGpXZ/6l2FRy894qj+44+9bnn59zzzG2XHN1+TFCZjdmbVFq0Q8dl96MfTa7fsBpkamFpmJddC31+2IxcQLjQ50d9Tp8fC5h9uoPsJV7PjNF/y75K1svaqfn2cXhvNel4klst4xZWy7j/ndWy9VUjB1vbDo5UwWtb24GRqp6SltXV1WuaS0qaV8eqV7eUKG5pOTASjY7sxx3d4G37W/BV8q7VbSUlbatlW3SAGlZUKx6CMRupjYv2QOOQBaCnqImlFaTmSsHhYEZBYkUV1nA+KnInMX4xGHE/krSBw/cMDKijNpbmDCS9gONMQDqCvLtd3ki90P6JeWu2Jd8Carivj97Uhx7NburLbkMP4Dm2lbmf7lFeRVVSvYSyMuCnJSpq45irBQp5x7r2pFTMZdLa4vk+U1EM/stI15wgmDyLIClZ3D0HV7zLIUDLfOMcucfbfOEeaWxI+uYUoa1KzQdFsaDNUVpb1NJrVVloA+Pmrt5YOdTgdYbr3T8xl1qR08nc71ALqo+KUvVN3kCt39STMiPEbtlVEOurLlvW1uh5j2UdYWIzJpm/oPtgPC3USgrCGckAUNYenXHIhr4EMH4Ub2pGgMRE00mxICYlABpWgaK05TeGpClFghh2QYynpOISGGRBldzwhlhuD3IzizreoPlRqhaqExehrwg96VGoWLWRYRSWksZIeWuZzRbtS65fZy+tcbf1mpRmFe/krlpfuSJV3NPcNxhsH6tuGkl5FSsMNK1Wq/XlJUUFFbVOX23QGqMHWv1xH9/eaEGMYssuV1VnRee4RVjdWT1Y5/HUdGEe/ETxJC3k60EVuXrVC9aDknZ7uEr1J4/pnI5NP1cLBsWTfzRx2TmtSrbDt+M1UuYMVYRXSM1yTQvIe37VRSwAxO0mk88lkLIW1zlrLx7sU+T+YaKGZHz0pvkVGIm3pS60BhMMAROxn1y8FLP8Gzsnbw6yTLXFkX2HrVu8HDOxYbCnYqIkK9kI3cmzTYpfQexjxrU4xFroNfLqFplteo6UAiOs7xzpqCca+BlKdoVUFOfecLsoDZ+RrPOd9iBq9ZPthH4Bm4yWi5/ZTf/bv6/JimO7jl/comgbvmFDfNWp3yodp37L3JWavAXTcRz9GR2hvwV0RDBynWH1lAXcjPxCHg9C0VrJRfll8QMXWajjfGGJxRYqFITCkM1SUsjTG+bPgoU8D54DP++m7N3op+A1i6ijFMhmRk2UP60mi4Bq0k0OpCWcnDHJ3ssk9+/F7W89ub36sd91yjlKIcKJ/AmFZHKd4kTzCWqaF0xmktyDcD+/VV/A2aoCbF7VBaQlUq45FIGOpGNpMr4QjdykVWlZobDMXVPvirWXhpvdazcWxrrKyoeyf1Wk1xl0lSGX12Zgb9nCNzd6qn1mB4zpPrBTHcqjYEF7KHD8Myp5QjO4AzMelgrl7KWaJH0v0IRMWNSEDNMYF+JWb21cSOLJG7rvpw33ZK/4S8VX1Gqdmn39jbmRWIwuC16rRFpix8eZQfoJ9iWQo2fe/xQpiP+x5woXF/qVuuR+pSSz51rwP0X2T/E/NtlngzEZLx2YWtY51V9a2j/VuWxqoHTFnn27p6Z279ujONZ9cGU4vPJgd/718PXXH774hhtkXzMD+O6XgO8sVBkgPCSWk0BYG5sJyo41jOMFmItpJW9NkWqqZA1etMUdNZhgbU0LMluZULBk0cVQ/uKM6nUlXqBUvq4yuT/+2C0ghfo1+QpAPvnStE6PKnUGBcvpUIXOwGv47JVc9gpeI1zoBqZbQcFEYb/MPg/ydVKl4I0el3fmiP7czkhLXAryuHxB9MZnymThF8XSZUEs27JCTXhGpeSRIbygGMRzfZo24BXiAOh7eWzGn4NxMdKJJachYkBIuwrKsCvwk/1HUlmQtNzGu3YrU0v0BzfzyC+j+UsQvmMJI6u/1usjjcCSt/y08WvZK7F2aXSqx5i41mUJz35XV2hCZ9CuzmuFA63ZaQfdjkoYxYevz6ue5kyUvUEwn77UxJ1Cv856S/hvfYsvQWscRXLNKubbVI5v3dRjVNolr0FKHWwmz7mZsloX3phXBji3rJYwLEIY5lrCsOWfi2FSPbwhQKo4Ai6YVD3nsGzaGqttJUFohwu3WmoF9pUJaU+sPtc07kI88y4FDaoLgIZzGHmAqdE6rTIj6QGl+kOAE1Y7hhN9FqWVttIO7hqAE/U+gBOen5jLLMjlvAB/nWqeYIxmjDGE9hYzomnFlp0uDDK6W5sAZCidYayro0RX01Qb1UdNAKJ7jUq3Y66PxtOVmOPL4lKxIiONtRN9HYnPrJVZPBhLryUR/9oVwH5DU3slCAUAyozDjg9zIAWJm6JiwUmRj0kx3IwG56fr4CDGS6tBW9fFZkZlbV0RkzYD61fXwWzuH1iL9XRUELuB82vHQBr9KbFJEDem8pimLodpalNisSldUh5LfS5MU46X0s+Haj5d20fnMY+5pClS3lIOmKc/sX6tDTBPS79ZBbZDazIS1FPn7W3qW1GCUc+qOl9mYWYI6A9LZgZzXQ4SlQWLCsO1LoBEFoBEbf64V+hJWEBgzJZdzmqMiczCmo7qwZTbXds5+/iFphBIK3s7/Y8KHVjLBmoTlY7itZCUPgNIUbLjbfKNS3dja7jMtF1dzoWlGmtGaoIr5bgnP2sE7qoFXM6mMU3bS6IpMgdSdlw0pC4szpVHNytaUNyOQ7mFEnxbvgb/3E7TwXB1z+r+GlrXoYQD0gOopntze4lWo1G4SJ+g7qs31SEf5/JZFlZX2lbsG6yPJ/xPf4MNNyUS3Rs7kmONxYGKgEpZWhgvdZQPHlLUfqIfECP3i1FZSL+Y4k/tGOON4lzvZ3eMQfMbjT6td0z2Py922rn/6NEL2vO3kaHDGsOPFer/OzQyBPyycOnTaBzLcE7HRdl3tSb9+WlE7T82aH6uYvM0Kj8mNIY+lUZ59+fn4GMybifxE5zi5aVPJTU7++G6D/vUFtVxWkGrnlWZ1Rei+HvfY9kbYMKwN7ALdP+C0B2jDl6Qbgwo7HHJC2FiNCoVwksgRjrb2E/OxGS7FCNeYqZEznnglnKBmGB6AZnoQnM5mRW5IUtRL8wcD1n6vZCA5lc/E8mFxU/lp7Yj+jdzScLnb07VFoYrUdLkT/h9TfWJwnAFfQFeDPibI05vibeuItAYcXmD3vowwSQyT+YIT8qpRmrswlwJRnGfw0IwHJFYvoTRa82IXp4grriVlDBKYRjwNG1C5sVsuLDklwDEEnl5NX/6qXrwkcHu5nk5Q83jDDV6ttrHux0Gg8PNC3B+AV6c4D34PfhvbAaDzc37YovOqAW+qEpzfEl8mrYEozMR2fnVRGcKc/4tSbQlLGtLmKRZZ7yytuAvcKjGTb2ASYXBc9gk1URAW7z2z6Et50PUn8atLxVGmv3+lkhhYaTFD8pQmGivibe3x2vaL8ClB/2NYacz3OgPNIQdjnBDAL8bfggGP/s7ilL+hvTetFNfodL63P7AxU2LREtshjPpkbwAx6lwl4oZVq2fb2TkiOKSRRyLnbj24zOkIsQSETURHFooCk6JGl7Sw4uCn2YVGnN4Wo1/w81pgwV/+YgZ/2ZeUrBqjd5gtpz79R9+vAxnzv0AC5VwAfioMjPFzHuzb/bSR+a+MkA/Oqepn3s4Y3CjFrpySm3RzXdHQm9lx100x/QVRO2kd1H2btL3apC6lEr34dFG4ue0LwKJz7TLQWg7aUDc3oSjtaHFjYzwTqiYkXT7lLqceDuShXVHosn63j6iBe1J0IL6lNgniLHUf6t31sImpGBoSXQaoT9/U60dV9y9xp6PWAvOjWVLbs88te6zu21F+5NuNJCPbs2Lg95L1AfeQmoq34dL0QD+TkdZP7vzle2zOl/ZP9H5asFDL+qBNVe+yCHnBK6y5Hzw/wOa5j3yYpp+s9gD54hShnNOd4FX4Hd1VOFn01X0WXS5z0PXEi+8mLy6TzrdeSKX+FmZzjmg00NVUzs+nVLcNaoyLgngVvzgVmIXJJuYA5zCAZdj4/EWJKnUSha+458cyad7lcXjin62E8mP8/hn+g2awl/s8DjojgY8RxGV1uJqBB3p9sSRHLPBnMn3C5jXTLxUr5rXyMSunCqe+jZpwUVTb8EHr/t8nzmvWfgz31rQKP2uvCqdejfX2IsG7aboEdAnnmRSyB6XtIl8rhWnziRLrn2DRcBfg4F0ci7FvFRLcFrTulQ7Htx1rlrMPxb0Q4/HA/qB9+yV4V5WZNce+dIjYxRXP+E174JYLrGzeKkb99qx86RDeTHAjfB5M4iYHvO5AtcvFfKHu4bOlfInhHtqByZYefw8Mo4BNvhxrrfKjtyeJgG0myHJMtBuRBkZuegIAXh0w0h8UdFI9vsKZrzfLC0YyWaFYk04bRTwoRGvcAg82SGpsWRwz7tcMyyNXa44OqfZoFcwL7QbxEof+zktPDD30uTkS9n7536/Gz197D3cdPC9Y9lx9HB2C/1GO/3sQu9B+o25e/PtB+eea8/1Q6wFbGyiItQVn+jYhbEf+PAiGE04KjlYuS17dHHcaAaAE5HhToTMzhzcwfAw3+ELrx8WY4TjCKZSi3p9SeEivABRdoGuX+YLAOQl3cBOfQom/kSfMGXifICYkXuHwVzD62/V2Mqep3tY7Hzdw+K5NbhpI1taSbz5F2wgtuCpPruVGCqcNxefq6sY87Ts3P6/jm/eNn2O8Z1cMF2fa4D0m/OOMjdGsGt4jHUXGGPqfGOsXzTG8H9vjEts4+cYavlS0/k5B3yO01007l+QcXdQx84zblz8WBqXYiyp0qrE7Y5hHncu5kUpzNwOeeZ28FItnCXks8QCnzCOre2ACMbo9FeyDedySmqFSFiqav7cPLvA7P4crOu54Iz/fDz89vlsgCLHxznCxwZqgNp9Pk5CgNcTlyrBU7UAC1csYaEUs5JsJq627YTDzgXm4a9za4xhJXP62f+Wkn06uPkcfPN+Fub5fEal8TPxEKIeok4rGMUGwIKUWYOSGmTXIJUGPYSuyt6UQEfRpYnszejKmux12WtRFF2NjiazN6Ijyewt2WO16MrstbJe383+mn0fvG0llaI2UGkblkZ1XhpleD7Xy60+QQA+npQxCcDqBnj14UVZd0pMCC+pWZuT8wQjuPBEwFu3KamsWjC9RHGC06MuSeXDrFyVKymAtuUFEQypyN6hII647Uje0Wqe36orG+0r3h09pDdZ647vOIS5f8l3R240+ITKN/Yf3bN5DT3b89JezP//2f3N7VgeY0M5Pne23ccbf7Ml++sZwuzm+hmBp85uQSWvPXFmlYKtbwZuz/XUJDDzH/xoFcYgpM8c2HEn5cddWT/ZaS5wvk5zJblOc2mry5NDc+ftNreATc/Td+7jBd9zoQ507FbZ3/zfpnPBp5yHTiQtciIXolRxWd5x5GgFv+Gkys9Pa/h8tFYs0Fr06bQu8Q3nI1n5CWdwYcKXOAAmR/8c0F9JtVDrPjkCsSwqNsQlDxit6hgpD1kYDl7LDVjnC8MTcJhYGGRbrkZcsqo/TW0+3TKdZ8Bzn2mJLjj+P3+G9aHl/nSgexbK/ckOdZ75DnXFn79D3UIu/fy96poXx/Dna1vHvDuPUxb6vHIgsb5FfV5nDEYSHRs0mRnGKbcz1sx3JOeAZNoYi4kcj0soSCdouS25cb4t+QVavu5E3Pl7vmZ/Lnd9zf4zOkq6vk5j2/29sx8o2tjXqF7q8hx1xZTcuQkgg6TEBbx9hKReQ0bslb+Zlnyjs1xVWiBkpnUF1eqw1AIhQkuUhAD4K2rr8HeVlvlT+Ks0JWUnvLYAlLAVV9Q2En/YWYG/eajAH5K/oWzRt5coFm04X1LwrVj8rRNW4XsdR57esubmddGqnlU9Vb667r5lKV/NumsHd3y1ycZyOkOweW1r48Y2b+PEronG6r7VfdVFrbv6eq7enFSgHU8eaqwZ2R5v2diTqmsMlsRK3L7y5tHGZRevinTW5fast6yq6hquDcX722K9LY1do/XFvW3hiok7Ns0imIukxxz57qAk1UbdfZ4uc3X462E/q9Vc+2e2mus4p9XcDGfx1zVhB3ehZnNSHQBcsekLN51bcAlfuP3cjvkmfF+sEZ3i5lzLvs/Fz8b/T/xsxPys++L8nK9J+8L8/PV8EdsX4ydzcb7kLc/P44Sfy6kHzsPP1OfhZ89n8rP3HH6+gPlZ3zbPUNEliA3nZWvqv8tW7GWj+Ct0EfGyX5i7Vf+y5hftvP5RJUsr6cdYTvMFmXzF7Kz+aYVaoaSfZlWLdPdWwusR6t0v3HESW9m6uNQOdncoKjXBhS7w3qsWsx5M78yIHKeNLBbE9DJXTB2e6ZJvdUVnlslHC/IZXSSfOkHkUlLXCER2Fn9lkwavSkhFMeFCqj/UDldaV6S+uJQuEPN9YWElLKE6n78pUVNQUYkazcGk39dYV1MQrqS/oNSeLWmLunwhX11VSWu0wFfqa4iQdUBZdkeI7Hqp9dTbX1x63VFxIi41AegaArFtWCw2vPWuHZBW+zkyG8Uyk/rhej/Ix7p4Nm1cJK0UlpbYbpIqsSvtFySLBu/MMElDE3KZzP+RZqOftafoC4ss+VmbkL6g5H716VuW5mX4cyLDPmrNeWfgKMZdTfL63afLc2awm2syhGcGcyu9Y0vnYb88xfp5aRjO2uWz9guYx/Gl00/sN4n+lDgszFgqm7o1nzEDRwfhSnvdf38Gnm8Z+QuL9NbCqtZAoLWqqEh+LWzIry1/QYevKGmucDormktKGiudzsrGknhbW37NmdhRpVGhp9qpYZiJIpVuxlJMxKXlMMvKYqTdn1gQJ4vy47G0xjovvZFAs9UQFlfEpREF7gaVn4YdIIsOXhqQJRMAmDoSwxEQ/tL3Yj5DplsHRb4yRBwQ0py1GReYBUySA7+uEtIFZaSMvtgkRapxSjuwHNdCwTHZ0iiIxbhUSjLN73JfEFCu7s9mn68783uXdCzFXwO/WG5NcBXle5guFpLOyAqDz+299m571Ss3DtywpU7Lza2rnrh6Rc/2ZSEtp3Y6+tbtrL3x7SrLmv3/q7dzD46quuP4fe4z+7jZZ7J5bTbJ5r3Ze5MseUMChIQkBBLAPARDERGCgBgEX4hCK0lFKyhi29FSFehUu3fJjNba6YBV207/cqa0U1un49ROM+NMy1inLUjo+Z1z95l9JNX2D2DvJsy9v98595zfOef3+3wfWoaaxLeluG1YXHn/iATNx5xgtlf07GzvPTgs0prOAyMBrvvJFyrESr0GNdmxe+99vO3g6/c6zAdem2pxlxfrCgF++uQ3102uzC9cuWtd03opp2bzkfXH+YquMdqweXqr1HjHCWDwzp/GDN5u6igV6oK2KpNklyophjfo8802k9evGRedNjfA8fmaMJsXjvxwIpppDidjttnh+FzgXWVen9jZhdcNzT5SatolQLn20ji+dLqTczYj4Lf2h5M5Y3fkiasrKgdzdSodn51XkV/f4vJ3lpeOnNrVlIb72zLIrU96TH5Y1X/8J9DvMUcXxb7A0cX17hGSrp8JE9wScbotKXC6rQpOd5a3uv2g1pAGqCv7YZRpXAJYN7pIWBJidyayQFgUbJflo+uC1L5p+N/6pgF841+Cb+hIwL8k39DqSLS/KOfQ12LqWsL+uYj9syLOP2JK/3Sm8E9XrH/qM/hHXKp/FkTuS3LTcGLUvjhn/Ts+WOcUfx3C/uqiNlHT6bnVsIc2JMmNKLjrQbPK5gTPAby6xYZxyXBmMoA+DkT9eRukAbWgUcrqroaTAFnnhfraL0u3zhSxLcmvY5mitUX5mdmSPkhjKBSI0VtwPZeBqlRyHGCvDkMqI4kOBpLoIFN6BU8an0ThiYwj7RMK7/9GL4bzKnXBFP2HhHtwKe/B6SNlPuEXF+7xYuR1tE9EashujJG7MLc+hRvh3AAr1ajkVMCeXiibjkmsMMQlVmix3iedrdyPTXwR8GZrYv8+NcG9Ftt5bwwphrK3PkN2XsccATvJr8A7n1aa5FeUkfyKPJJfEUUJgHiUMtFCfoU7kl/BJPQfeJzEPmZI6CbvTNRkQAvc0MPzJn6L22ns1j/Yv/MvIv/1ArtHhPevVY21sjFrjWw6BtCzBsywMw0KwzXK3uKKAFq86vnc0nIRxwSgjB2ianRx2s6OWtqLtYU7YDMek0s6YKs34MBl3gtlsQME7jLWuv/VXY17dtzmNj29/4KgzjradmKtTkBNMj47+B0Lb7xvxe51VS33yVO3f/+B1RNNE492j57YIrGm1tHDA6NPjNfSH2x7/bG1ec2jbT/+V9/pfI1Ol7W3uM7MmIysnbMa28SZAo1Gb9hR9/C59w89+ZdXRjofkvdufW5H4+pjP7u/fucGqW3PM6QvEwb3NOWgJOpkCuIvnFc4JblYNRes8+HkDeDf1CdQgFFjz0pkkSKZ4eQlRt42TAhuiBKC5VIJ4qp8CzkgV0DBch2gAYpqm1Ijg1Ot+ReihL0pF/XJIMPch0mX7mjuw+xhRQfOTw3H0IfLI3MfRhCLyRDEaRIe5HKY3GoWUV8dHZ8yc4m/HRm9MhKK2U0kAkpnY/WXtLEabCxfhI3RwGYR7GVHZPjMaCTTGYlkwnZeVHI6Yu2siLezKZmdaRI75IrF2rkgQMls7vbEUTuz0b0J24cR26cT8zpiKNrhvA5VsrwOw+LyOgxLyuvI4KoU73pmj+1K+e5ndt2hFHt4xH+HsP+aY/M5Yj0Y8AV7ST7H8mg+B3FdRXw+xyr0cVXUaRnyOdI7KlOsltlhuzMFaJn99qMMO2jQB/dRH3N+DjTuLShWq6VAz0CdNRcGPbh9siNrDp/mc1eDVlHOskGIAdOJwrigY8+Cy4S4q33s5ZuXY/l5sZ+ZE2vXzr9ZvsycU2KxenJMAZaOuSDvxyXOwHXgeqlGaqOSH+ILbzSUw0FlANcI54uy24ArVqBkR0CtB2eW9W5AnfF2p7GglIyC5T6SFuIs0JQ0xu0fBBQsnqL0oSYoPDo2J8ROGpiM+KOnlo3orRbp6bbl0ISv3DNk8Aje6dXdW+tEhqs93D82vcX31Mj02PTtvg2kqcTa+03Gy6uuHIb2Wr9PML+16leP7brQwrxRVbvi4Pl5d/fyqVd3/HwKxwGYF43GfwflhhP/eGK0k1H46BgbXZwCG+1RsNEhixMSGBLQ0VBOmZ8aIB2d4JKgpN+NzmjJoNLcufA6PoMdeV+FHXkC4XcntyM6iSVDYq+IzlrJDGFPxqy5w7aAhmj5Qlty4mypSGFLZdQWVxJbctLasmCiSmLSyQUzU1LDnoufjVjFtkPItkqqDXh7SRnlQa8v2CzJ+WiAqBOxpGjUSqCUF9twnhakzjTYMEEoxnbQGsWkKYsKzTogirIolHmmoTSJE57NOHYmdcqNjOMlQxjVqD9DFSdaa7qYKC0do6rD1ZsKqjroEoKO1MBqNtI7U6OrhUgfTQ6x5o5EO6mib8F/gFnuir4biNoSonUBlrbAKivkZcsGfTeLKEJqh0vRd4PXzZUd0XcrsMfou1kS9d0SRS0mVob2pRC0UDffPDh6d1jbbbB/XhOvZ8Eqvj2EV7et1EAsAxwS1ZtIkaKPFCk644oU65UiRbeiQlwlyBo7PH4mZDiToXelbpefZupkKZrr0wy9DHSuP9PcjfpYEVVPPaEojtkkuYydC1pEgnU0hivU6ti5WVN2HmxbmaA8iDDg3FbsGUDA2KtEEdZ6wMA0YrivERiYWSL6IGircE6lDmpZebw/lQ2YCAfoxYQodxUMUcZsZZeKZLAyjph6HLeA96iSyDmPvfznma3nZ/aUsSPhkpwvzpftmTm/dfqTl8d2989cmTp4ebqvb/rywakrM/1KwqR//NgwvTFcqrdp+NhY3c4rtPnC2WvnR0bOXzv7/LWLo6MXr5HYWfUIp6dEajXq56epUC14CcXKy9RQY0KwugZJ7kSX/eJst70WXNQN26AbsIsk5BKJnD3A7ki3CBskayDTyTyH4ZdtaD0s1wIZyo46E3JFcE12yOAqbyL5TUWg5yTbl6GomiryVEk4maQbJIOCnUqPU0ILRSko+UEQnSx65MNbfiMt+87deer9KuuaOx7o7f/615bpTTdv948dGVh15+pKfZbG5ewbv6tx+r3aql88v/2lfS3bKzce2Tj8yHBlJfoLfaxkVcydFWt3tvdODYskCvnuzMrJgcqYg5/wtt7zz518KUkUaQmf+7Ak7051k7Ki+a+ZGorPvIMQsVGSc9EbWk1ovLarcqENk6ItOBMPJ5BBzO23kT35xSbnpc8+TJ6xt4ga4mR5fNzQInKf3dxrTAPeC6yJaqoKCodEwEQkBQWXHVFX1TaFK6xi5m934mQdv/UH9/Jyv2MCaI3oovqooMUHtbg6FJc7fTgFwSCCTgPc0EUWfS6c2hlm9oFkp8EF77YFOqsTk7nt8WTu+IVc6i2apNsxNLWDaWS6GOgdFKwGdtB/ZBqHhoif/tufnWGq2beZKaIhSxYi8CdGQxb+yxm2lKnu6SG/z7+f+ff5OuX3j3PNdAP/OerHzVQw2zfLZlE6jmziooFBb5oL6XGBoh64MZR51mSlJORN2NnVk0NjigBsYVtRDaKAZH+xlj4+0J6nUXmlEt603G7lfjN4qs2i0qhV9XcFWjs0WqPK5e0nNu7namk3/1f0DG34GbKiz8BflU2muaDJPKvFNw5qfSEtrivTAr4OHsMEextZ5DECQDwhm56E3uwt208eocNhHejIU3PrNCppZ6ClQ6MxqnO9fd7B060WFTzD/HXaTc1+6WdwZH6GTxY+QrYK5jrUFkwPbosKtBZFTxH0SkqDBJ2RUsFUbRLUk1zZIvTzIpwWUORCP7eZZ0usVL2CjFLaTLaZUPdnIZemSAh6U7ZhaeaGpa39HXBZDwamamdvisZnoO2Zetz2FdTusM3E+UE3sTm9/+EICud1I7NzS+DbXBuwzXMLtMRkpW0gC88LeQ0gYJOir5SGv/SmbDzagi49PG1uR9ft+Sk6lCZpL8P2zl9n6nE/+//a6/iK7E3aebXJezToeZTSy9hH2G/hmsugETPz1ISZp4bXy4IHbK0Nf0n+wSJLdX6oAIqZ2ehS34bJh/Zu8Pk27G1v27PBx2xr3wvMzns62ibh20myhzN56xpvp16nBMpDNQAvEO+CuSUJnwjJjgpRJF/xsJXTGFt8iyYoOQ+2dAgdqxbNzAHC4ozn+ZSmvZw05hTbojs79OemnGKrpSTHbM7xWNH1PzHnJ3K9Lo7hU57mioyVL1In6Hcx99dNhd1nslFGDmf3QP0w6L+hKDU58DeR7psC50vuNYvu9SFm0MG9bGECnYBvh8c9gSj/paLPLQDNXUoDj6OpolvXuGn+DbTaOUaFeqCRmrVzIROE9oUotKfoHpOhKuiTZIqbC9aLs1oN/qJCAiI05tesw2+PbgCF+dWWObmkAbV2Nc6/qfbDS1JdBmDWagxmhXdJI8qDeIXajIbDFSvRUrwQ9EmtTqUcGY7NAp4GiYStSmINplKoieqBymbFwrjoIwZvcdGzam/R92iGO3fBPH7yrf2de7cOlRVxOq3G7hFXjbWMv3Bfn4nZaRJuhliaZgSzad5i6D1wdrxjW29Daa5Wpy0r3bTzwTX3vT29ych0t1rL7aK/9Ru/fXbQUdNVXcKrbYVlhbblD795uFCfXSfZvbbCLOHI5aMrnGXVZTk6j68/kD949qOn8JjTy47zpShGU6N34gCJ0mStTSJ+ZMUwixnAihqHiBZDVAHkJaEgVnVV5o1odYXRjDyLnKfC3lSB83hS9OwxYgVROGJzkFALKpucHkAl5pNCmgYC28SEY4fF0aioy3mEAOqanmIv6xB66Y9/vYY+3azTqT/S89rf81pdy3L+TxohS9B8ouL3tLbe/BsjoD/9nGZ+psBspKc03M1L9Hs18w+aaYF+vGq+GfoQDAI32BtoJPDGaCcqMkIQisJAQ/5R4iG/4Bbgv8DBMta3Zh/lf4n+3aqsNh2SInFti0pcqxLlra0ihJtwpuwwzIUVFSiidC07UdgZ0giYLSBrQGRP35Sgfu0B9WtVPu1WmKQgfx3YdWaiuMfJ0QZ9dfG5ILNx27yJqF9v3nLm7qYsnV+nfvUHw1+Uss+E1a/J81/i36GKQY28kMLLkZABWlxAMbJghmefzc0v1JDa/VxsExYNLMTGgPhtjhgqKMRigXmgCWGWzTCsGObwsGguQMboNValDCxsBEhIoecm28OxIt4NO85u86ztbrP1TgQe8PcfHqqmvfMfEju6Rl/Yv5xXcdf7+H2Mpm7s6GBXRMj7P61y/VcAAHjaY2BkYGBgZOo//7DZK57f5iuDPAcDCFz2z/KA0f/P/mvhyGTXAHI5GJhAogBrnAx3AAB42mNgZGBg1/gXzcDA8eL/2f/PODIZgCIo4CUAogoHhnjabZNfSJNRGMaf7/z5VjD6A6bQjctWClFgEV1LiVR2FTHnMCjXruY/hCCCRdCwUApyYEWyZDUsKKUspJuI6MYKuggGIl5Eky4WXgQjarGe92uLJX7w4znnPd855z3vc44q4AhqPmcUUCkU1CrmTQZd5K7bhLC9ij7nLeZVDE9IVB9AgmODTgpDahoxalwtln8xdpyUyJUKbeQWGSVJcpHMOitICWzfJ49MxnFUEU3uTQzYZmy2AeTsPVxy65AzL8k4+yX2/cipKH7rKURsB4qmATlfO3ISd88wp1coilo/x/YhbB4jaJexIGv68thq3nlst1twnud4ppbKP6j9zOGj3s2zh9Clv7B/GrM6g25q2NSjW42j0WzECXMSWeZ9x/lc/qBXvXO8cXuQlTgJmw4q5+i9yOpBRNQiDjI+pvPcM48GPYOgFp1EJ/dtUzHHT41z/xtSf6k92xnSXtGQ/GMUrjO3FneY/Rn06QTSHJuWOV4shDodRI94oh6gl0QZ+yR72004pAJ4yP4I47dVifklMGef4prHC5xi7fd4dV8HX2/5m3jh+VADffCR12Qb8bud2F/1YS3Ma9LzRbyoQbwQz8wU3kvd18MdoIoX9f/D2u8kaWelXCDfzVFE/vmwFtal0h6rRbwQz0Q3fGWuy/yHObFWO0izTgG+FqCq6izfyAJp/Qvy1H7qOY7xHVTh2hO8FxN8F0l5I5V3kiSiQ7zvu+xlxGWuuoA0mZN1mWfAPscx/ZPtw7xzI2j8AyV25OAAAAB42mNgYNCBwxaGI4wnmBYxZ7AosXix1LEcYTVhLWPdw3qLjYdNi62L7RK7F/snDgeOT5wpnFO4EriucCtwt3Gv4D7F/YanhDeFdwWfHF8T3yl+Nn4b/kP8vwQkBBIEtgncETQSLBC8ICQl1Cf0RbhOeJ3wJxEVkVuiKqIpon2i+0RviXGJOYlFiTWIC4kXiV+QMJFYI/FPSkEqTWqNNJt0hHSJ9CsZM5lJMj9k42SXySXInZOXkQ9SkFBIUJilcETxjuIPZQnlIiA8ppKk8k41Q/WWGoPaGXU59ScaBRrHNN5pvNPcoHlOS0urQuuBdpJ2l/YzHS2dJJ0zuny6Cbp79CL0hfR/GNQYnDNUMKwxYjOaZKxkPMvEzWSCyR1TA9N1pjfMWMwczBaYc5n3mf+zKLB4YznByswqwuqRtZl1j/UbmxKbI7YitpvsouyZ7Hc4THOscIpxNnG+4ZLm8s21z83LrcZtndsH9wD3Rx4lHs88ozxveFV4S3lneD/z8fLZ4Cvnu8mPyS/B74l/WYBBwJaAV4FWOKBHYFhgSmBN4JTAa0ESQVFBV4J9go8E/wnJAcJFIbdCboW2hf4JkwmrCXsEAOI0m6EAAQAAAOkAZQAFAAAAAAACAAEAAgAWAAABAAGCAAAAAHja1VbNbuNkFL1OO5BJSwUIzYLFyKpYtFJJU9RBqKwQaMRI/GkG0SWT2E5iNYkzsd1MEQsegSUPwBKxYsWCNT9bNrwDj8CCc8+9jpOmw0yRWKAo9vX33d/znXttEbkV7MiGBJs3RYJtEZcDeQVPJjdkJwhd3pD7QdvlTXkt+MrlG/J+8K3Lz8H2T5efl4eNymdTOo2HLt+U242vXW7d+LHxvctb0mkOXd6WuPmNyy8EXzb/cnlHjluPXX5Rmq3vXH5JWq0fXP5ZbrV+cvkX6bR+d/lX2dnadPk32d562eQ/NuTVrdvyrmQylQuZSSoDGUohoexJJPu4vyEdOcI/lB40QuxdyCfQH0lXJhJj5QMp5QxPuXyBp/dwTSXBjt4jrMxxL+A1lPtYz/GfyTk1QrkLTxPG+wgexlgNZRceu1jLILXpX/0k0MvdqmRk9RPSs1o9kHvQDOVjVKK6y75XPRxg5TNa51jPqHuESEcezWKblaGheQ8QVWuePQWBy/WfPMHnyRK2V+2Hl6JelbFZv42nUyJbUEd3I/hQqy6kwpHS2otFrNeXYtXxU2iFeFJc1VpRHtPTGdYy6f8LBrSvbfG03fVsc3o2bqWLLJUJfWKgDOmTYSmyUB7HREwRmDirUiJX86mE9tixu9wFp8REo86BZI+5mpdVv7Nn6I+9FcaHjGnVaC8s57G7yNLQ1PqH6FLl7T1ypmD9CW0No4iZKg7KJKtd87WzMGRyaFrvTSEV7JQCfroLi4is6zNmxL0JKlT9GRk5Y49b5BNmWdDvEHsaN3b+KZtCeYS1lHG0QmOa1jv1XDX6LifH0Hu5XOBr9ffgN/Z5lMhjRutBq6BVHTMmRlNWe7FSaebTTv1pnRXjNa/8H2NbPw4WXZXiJLVuPYVPnT0RtXLuRu5fscqI8IxYZaz5gDtdX4sW/W64nzP/FLWN6HeVoyUsp8wjcgaqN63pnPuV3oidb3Ogz/hj1lh3RMqYoU+NMXO7YG9Zvyb0MVhwRmt9xxk3dA5V81vrGHsuFZo57RNOkfVeHSFexj2dNWfO34TVx86HOlLfp5qtdH3CVzNhTiSe3N9VJx94hGSBqLJmwPeUsTfGimUyYVeExG7EbOeOjfVGiUpmS3maHK8wIif3U0yLGSPZG6yaGAWZN2K0asqun12+crp1zV3mlvCUqs40L3M/T/V24KxOnUv1yRXMyezsqSTCJSupmFudRu5aXbDSuFOscKU62YydM6GFdceQlUwxIQ7xm/PX9kldvx3anDZjaFxX//LszbG2PH0/X5u+h//xt8/etWvY/199Ma1XmMNOsZyy89u0GOGecWYeItpdeN+/gg/PZllVWn+96LdPj71puduX0alX/qFP/lCO8e/geiJ35C1cj3GtzvhNoqOTRedvQXaX7IN8CZUH/uaybh/9DeeiFNJ42m3QV0xTcRTH8e+B0kLZe+Peq/eWMtwt5br3wK0o0FYRsFgVFxrBrdGY+KZxvahxz2jUBzXuFUfUB5/d8UF91cL9++Z5+eT3/+ecnBwiaK8/FZTzv/oEEiGRYiESC1FYsRFNDHZiiSOeBBJJIpkUUkkjnQwyySKbHHLJI58COtCRTnSmC13pRnd60JNe9KYPfelHfwbgQEPHSSEuiiimhFIGMojBDGEowxiOGw9leMM7GoxgJKMYzRjGMo7xTGAik5jMFKYyjelUMIOZzGI2c5jLPOazgEqJ4igttHKD/XxkM7vZwQGOc0ysbOc9m9gnNolml8Swldt8EDsHOcEvfvKbI5ziAfc4zUIWsYcqHlHNfR7yjMc84Wn4TjW85DkvOIOPH+zlDa94jZ8vfGMbiwmwhKXUUsch6llGA0EaCbGcFazkM6tYTRNrWMdarnKYZtazgY185TvXOMs5rvOWdxIrcRIvCZIoSZIsKZIqaZIuGZIpWZznApe5wh0ucom7bOGkZHOTW5IjueyUPMmXAquvtqnBr9lCdQGHw+E1o9OMbofSa+rRlerf41KWtqmH+5WaUlc6lYVKl7JIWawsUf6b5zbV1FxNs9cEfKFgdVVlo9980g1Tl2EpDwXr24PLKGvT8Jh7hNX/AtbOnHEAeNpFzqsOwkAQBdDdlr7pu6SKpOjVCIKlNTUETJuQ4JEILBgkWBzfMEsQhA/iN8qUbhc3507mZl60OQO9kBLMZcUpvda80Fk1gaAuIVnhcKrHoLNNRUDNclDZAqwsfxOV+kRhP5tZ/rC4gIEwdwI6wlgLaAh9LjBAaB8Buyv0+kIHl/ZNYIhw0g4UXPFDiKn7VBhXiwMyQIZbSR8ZTCW9tt+nMyKTqE3cY/NPYjyJ7pIJMt5LjpBJ2rOGhH0Bs3VX7QAAAAABVym5yAAA) format('woff'); + font-weight: normal; + font-style: normal; +} diff --git a/ER-Model/plugins/er-model/web-ui/src/css/_scrollbars.scss b/ER-Model/plugins/er-model/web-ui/src/css/_scrollbars.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/css/app.scss b/ER-Model/plugins/er-model/web-ui/src/css/app.scss new file mode 100644 index 000000000..794a06380 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/css/app.scss @@ -0,0 +1,28 @@ +html, +body, +#q-app { + height: 100%; + margin: 0; + background-color: #f5f7fa; +} + +#q-app { + display: flex; + flex-direction: column; +} + +.q-page-container, +#q-app > .q-layout, +#q-app > .q-layout > .q-page-container, +#q-app > .q-layout > .q-page-container > .q-page { + height: 100%; + display: flex; + flex-direction: column; +} + +.q-page { + flex: 1 1 auto; + min-height: 0; +} + +@import "./_dbml-graph.scss"; diff --git a/ER-Model/plugins/er-model/web-ui/src/css/quasar.variables.scss b/ER-Model/plugins/er-model/web-ui/src/css/quasar.variables.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/env.d.ts b/ER-Model/plugins/er-model/web-ui/src/env.d.ts new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/i18n/en-US/index.js b/ER-Model/plugins/er-model/web-ui/src/i18n/en-US/index.js new file mode 100644 index 000000000..b70b80f06 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/i18n/en-US/index.js @@ -0,0 +1,7 @@ +// This is just an example, +// so you can safely delete all default props below + +export default { + failed: 'Action failed', + success: 'Action was successful' +} diff --git a/ER-Model/plugins/er-model/web-ui/src/i18n/index.js b/ER-Model/plugins/er-model/web-ui/src/i18n/index.js new file mode 100644 index 000000000..7b5dead53 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/i18n/index.js @@ -0,0 +1,7 @@ +import enUS from './en-US' +import zhCN from "./zh-CN"; + +export default { + 'en-US': enUS, + 'zh-CN': zhCN +} diff --git a/ER-Model/plugins/er-model/web-ui/src/i18n/zh-CN/index.js b/ER-Model/plugins/er-model/web-ui/src/i18n/zh-CN/index.js new file mode 100644 index 000000000..2195e2091 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/i18n/zh-CN/index.js @@ -0,0 +1,7 @@ +export default { + export: '导出', + import: '导入', + zoom: '缩放', + pan: '平移', + position: '位置' +} diff --git a/ER-Model/plugins/er-model/web-ui/src/index.template.html b/ER-Model/plugins/er-model/web-ui/src/index.template.html new file mode 100644 index 000000000..c441e6f86 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/index.template.html @@ -0,0 +1,22 @@ + + + + <%= productName %> + + + + + + + + + + + + + + + +
+ + diff --git a/ER-Model/plugins/er-model/web-ui/src/layouts/DialogLayout.vue b/ER-Model/plugins/er-model/web-ui/src/layouts/DialogLayout.vue new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/layouts/MainLayout.vue b/ER-Model/plugins/er-model/web-ui/src/layouts/MainLayout.vue new file mode 100644 index 000000000..12f74b9a9 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/layouts/MainLayout.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/pages/Editor/Index.vue b/ER-Model/plugins/er-model/web-ui/src/pages/Editor/Index.vue new file mode 100644 index 000000000..4ef13f66f --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/pages/Editor/Index.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/pages/Editor/Toolbar.vue b/ER-Model/plugins/er-model/web-ui/src/pages/Editor/Toolbar.vue new file mode 100644 index 000000000..8b0d69374 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/pages/Editor/Toolbar.vue @@ -0,0 +1,582 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/pages/Error404.vue b/ER-Model/plugins/er-model/web-ui/src/pages/Error404.vue new file mode 100644 index 000000000..202e5ab0b --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/pages/Error404.vue @@ -0,0 +1,17 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/pages/Index.vue b/ER-Model/plugins/er-model/web-ui/src/pages/Index.vue new file mode 100644 index 000000000..c859cb26b --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/pages/Index.vue @@ -0,0 +1,17 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/pages/ShowDiagramPage.vue b/ER-Model/plugins/er-model/web-ui/src/pages/ShowDiagramPage.vue new file mode 100644 index 000000000..0ed41242d --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/pages/ShowDiagramPage.vue @@ -0,0 +1,283 @@ + + + + + diff --git a/ER-Model/plugins/er-model/web-ui/src/quasar.d.ts b/ER-Model/plugins/er-model/web-ui/src/quasar.d.ts new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/router/index.js b/ER-Model/plugins/er-model/web-ui/src/router/index.js new file mode 100644 index 000000000..adb7407f5 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/router/index.js @@ -0,0 +1,23 @@ +import { route } from 'quasar/wrappers' +import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router' +import routes from './routes' + +/* + * If not building with SSR mode, you can + * directly export the Router instantiation; + * + * The function below can be async too; either use + * async/await or return a Promise which resolves + * with the Router instance. + */ + +// routes/index.js +export default route(function () { + const Router = createRouter({ + scrollBehavior: () => ({ top: 0 }), + routes, + history: createWebHistory('/static-plugin/er-model/') + }) + return Router +}) + diff --git a/ER-Model/plugins/er-model/web-ui/src/router/routes.js b/ER-Model/plugins/er-model/web-ui/src/router/routes.js new file mode 100644 index 000000000..da09ce371 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/router/routes.js @@ -0,0 +1,33 @@ +const routes = [ + { + path: "/", + component: () => import("layouts/MainLayout.vue"), + redirect: '/editor', + children: [ + { + path: "", + redirect: "/editor" + }, + { + path: "editor", + components: { + default: () => import("pages/Editor/Index.vue"), + toolbar: () => import("pages/Editor/Toolbar.vue") + } + }, + { + path: 'show-diagram', // This creates the full URL + component: () => import('pages/ShowDiagramPage.vue') // Point to the new page component + } + ] + }, + + // Always leave this as last one, + // but you can also remove it + { + path: "/:catchAll(.*)*", + component: () => import("pages/Error404.vue") + } +]; + +export default routes; diff --git a/ER-Model/plugins/er-model/web-ui/src/shims-vue.d.ts b/ER-Model/plugins/er-model/web-ui/src/shims-vue.d.ts new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/store/chart.js b/ER-Model/plugins/er-model/web-ui/src/store/chart.js new file mode 100644 index 000000000..32cfd8d43 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/store/chart.js @@ -0,0 +1,393 @@ +import { defineStore } from "pinia"; +import { markRaw } from "vue"; +import { computeLayout } from "../utils/autoLayout"; + +const DEFAULT_LAYOUT_WIDTH = 220; +const DEFAULT_LAYOUT_HEIGHT = 120; +const DEFAULT_BASE_X = 160; +const DEFAULT_BASE_Y = 120; +const DEFAULT_H_GAP = 160; +const DEFAULT_V_GAP = 140; + +export const useChartStore = defineStore("chart", { + state: () => ({ + zoom: 1.0, + pan: { x: 0, y: 0 }, + ctm: [1, 0, 0, 1, 0, 0], + inverseCtm: [1, 0, 0, 1, 0, 0], + tableGroups: {}, + tables: {}, + refs: {}, + grid: { + size: 100, + divisions: 10, + snap: 5 + }, + loaded: false, + panEnabled: true, + schemaDefinition: { + tables: [], + refs: [], + tableGroups: [] + }, + tooltip: { + x: 0, + y: 0, + show: false, + target: null, + component: null, + binds: null, + width: 0, + height: 0 + }, + highlightedRefs: [] + }), + getters: { + subGridSize(state) { + return state.grid.size / state.grid.divisions; + }, + persistenceData(state) { + const { zoom, pan, ctm, inverseCtm, tables, refs } = state; + return { zoom, pan, ctm, inverseCtm, tables, refs }; + }, + getPan(state) { + return state.pan; + }, + getZoom(state) { + return state.zoom; + }, + getCTM(state) { + return state.ctm; + }, + getTable(state) { + return (tableId) => { + if (!(tableId in state.tables)) + state.tables[tableId] = { + x: 0, + y: 0, + width: 200, + height: 32 + }; + return state.tables[tableId]; + }; + }, + getTableGroup(state) { + return (tableGroupId) => { + if (!(tableGroupId in state.tableGroups)) + state.tableGroups[tableGroupId] = { + x: 0, + y: 0, + width: 200, + height: 32 + }; + return state.tableGroups[tableGroupId]; + }; + }, + getRef(state) { + return (refId) => { + if (!(refId in state.refs)) + state.refs[refId] = { + endpoints: [], + vertices: [], + auto: true + }; + return state.refs[refId]; + }; + }, + save(state) { + return { + zoom: state.zoom, + pan: state.pan, + ctm: state.ctm, + inverseCtm: state.inverseCtm, + tables: state.tables, + refs: state.refs, + grid: state.grid + }; + } + }, + actions: { + showTooltip(target, component, binds) { + const width = target?.width ?? 220; + const height = target?.height ?? 72; + const offsetX = target?.x ?? 0; + const offsetY = target?.y ?? 0; + this.tooltip = { + x: offsetX, + y: offsetY, + width, + height, + component: markRaw(component), + binds, + show: true + }; + }, + hideTooltip() { + this.tooltip = { + x: 0, + y: 0, + width: 0, + height: 0, + component: null, + binds: null, + show: false + }; + }, + loadDatabase(database = {}) { + this.tableGroups = {}; + this.tables = {}; + this.refs = {}; + this.loaded = false; + + const schema = database.schemas && database.schemas.length > 0 + ? database.schemas[0] + : { tableGroups: [], tables: [], refs: [] }; + + for (const tableGroup of schema.tableGroups || []) { + if (tableGroup?.id != null) { + this.getTableGroup(tableGroup.id); + } + } + for (const table of schema.tables || []) { + if (table?.id != null) { + this.getTable(table.id); + } + } + for (const ref of schema.refs || []) { + if (ref?.id != null) { + this.getRef(ref.id); + } + } + this.schemaDefinition = { + tables: schema.tables || [], + refs: schema.refs || [], + tableGroups: schema.tableGroups || [] + }; + this.loaded = true; + requestAnimationFrame(() => { + this.autoLayoutGrid(); + }); + }, + load(state) { + this.$reset(); + this.$patch({ + ...state, + ctm: DOMMatrix.fromMatrix(state.ctm), + inverseCtm: DOMMatrix.fromMatrix(state.inverseCtm).inverse() + }); + }, + updatePan(newPan) { + this.$patch({ + pan: { + x: newPan.x, + y: newPan.y + } + }); + }, + + updateZoom(newZoom) { + this.$patch({ + zoom: newZoom + }); + }, + + updateCTM(newCTM) { + this.$patch({ + ctm: DOMMatrix.fromMatrix(newCTM), + inverseCtm: DOMMatrix.fromMatrix(newCTM).inverse() + }); + }, + + updateTable(tableId, newTable) { + this.tables.$patch({ + [tableId]: newTable + }); + }, + updateRef(refId, newRef) { + this.refs.$patch({ + [refId]: newRef + }); + }, + setPanEnabled(enabled) { + this.panEnabled = enabled + }, + enablePan() { + this.panEnabled = true + }, + disablePan() { + this.panEnabled = false + }, + setHighlightedRefs(ids) { + this.highlightedRefs = Array.from(new Set(ids || [])) + }, + clearHighlightedRefs() { + this.highlightedRefs = [] + }, + // Apply positions from an auto-layout result: { [tableId]: {x, y} } + applyLayoutPositions(positions) { + Object.entries(positions).forEach(([id, p]) => { + const t = this.getTable(Number(id)) + t.x = p.x + t.y = p.y + }) + }, + autoLayoutGrid() { + const schema = this.schemaDefinition || { tables: [], refs: [] } + const tables = schema.tables || [] + if (!tables.length) return + + const refs = schema.refs || [] + const getSize = (id) => getTableSize(this, id) + + if (!refs.length) { + const fallbackPositions = buildBalancedGrid(tables, getSize, { + baseX: DEFAULT_BASE_X, + baseY: DEFAULT_BASE_Y, + hGap: DEFAULT_H_GAP, + vGap: DEFAULT_V_GAP + }) + this.applyLayoutPositions(fallbackPositions) + return + } + + const positions = computeLayout( + { + tables, + refs, + getSize + }, + { + direction: "LR", + baseX: DEFAULT_BASE_X, + baseY: DEFAULT_BASE_Y, + hGap: DEFAULT_H_GAP, + vGap: DEFAULT_V_GAP + } + ) + + this.applyLayoutPositions(positions) + }, + autoLayoutVertical() { + const schema = this.schemaDefinition || { tables: [], refs: [] } + const tables = schema.tables || [] + if (!tables.length) return + + const refs = schema.refs || [] + const getSize = (id) => getTableSize(this, id) + + if (!refs.length) { + const fallbackStack = buildVerticalStack(tables, getSize, { + baseX: DEFAULT_BASE_X + 40, + baseY: DEFAULT_BASE_Y, + vGap: DEFAULT_V_GAP + 40 + }) + this.applyLayoutPositions(fallbackStack) + return + } + + const positions = computeLayout( + { + tables, + refs, + getSize + }, + { + direction: "TB", + baseX: DEFAULT_BASE_X, + baseY: DEFAULT_BASE_Y, + hGap: DEFAULT_H_GAP, + vGap: DEFAULT_V_GAP + 40, + skipMultiplier: 1.25 + } + ) + + this.applyLayoutPositions(positions) + } + } +}); + +function getTableSize(store, id) { + const state = store.getTable(Number(id)) + const width = Number.isFinite(state?.width) ? state.width : DEFAULT_LAYOUT_WIDTH + const height = Number.isFinite(state?.height) ? state.height : DEFAULT_LAYOUT_HEIGHT + return { width, height } +} + +function buildBalancedGrid(tables, getSize, opts = {}) { + const { + baseX = DEFAULT_BASE_X, + baseY = DEFAULT_BASE_Y, + hGap = DEFAULT_H_GAP, + vGap = DEFAULT_V_GAP + } = opts + + if (!tables.length) return {} + + const cols = Math.ceil(Math.sqrt(tables.length)) + const rows = Math.ceil(tables.length / cols) + + const columnWidths = Array(cols).fill(0) + const rowHeights = Array(rows).fill(0) + const meta = [] + + tables.forEach((table, index) => { + const col = index % cols + const row = Math.floor(index / cols) + const size = getSize(table.id) + + columnWidths[col] = Math.max(columnWidths[col], size.width) + rowHeights[row] = Math.max(rowHeights[row], size.height) + meta.push({ id: table.id, col, row, size }) + }) + + const colOffsets = [] + let xCursor = baseX + columnWidths.forEach((width, idx) => { + colOffsets[idx] = xCursor + xCursor += width + hGap + }) + + const rowOffsets = [] + let yCursor = baseY + rowHeights.forEach((height, idx) => { + rowOffsets[idx] = yCursor + yCursor += height + vGap + }) + + const positions = {} + meta.forEach(({ id, col, row, size }) => { + positions[id] = { + x: colOffsets[col] + (columnWidths[col] - size.width) / 2, + y: rowOffsets[row] + (rowHeights[row] - size.height) / 2 + } + }) + + return positions +} + +function buildVerticalStack(tables, getSize, opts = {}) { + const { + baseX = DEFAULT_BASE_X, + baseY = DEFAULT_BASE_Y, + vGap = DEFAULT_V_GAP + } = opts + + if (!tables.length) return {} + + const widths = tables.map(table => getSize(table.id).width) + const maxWidth = widths.reduce((max, width) => Math.max(max, width), DEFAULT_LAYOUT_WIDTH) + + let yCursor = baseY + const positions = {} + + tables.forEach(table => { + const size = getSize(table.id) + positions[table.id] = { + x: baseX + (maxWidth - size.width) / 2, + y: yCursor + } + yCursor += size.height + vGap + }) + + return positions +} diff --git a/ER-Model/plugins/er-model/web-ui/src/store/editor.js b/ER-Model/plugins/er-model/web-ui/src/store/editor.js new file mode 100644 index 000000000..3ac930eec --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/store/editor.js @@ -0,0 +1,251 @@ +import { defineStore } from "pinia"; +import { Parser } from "@dbml/core"; +import { throttle } from "quasar"; +import { useChartStore } from "./chart"; + + +export const useEditorStore = defineStore("editor", { + state: () => ({ + source: { + format: "dbml", + text: "", + markers: { + selection: { + start: { + row: null, + col: null + }, + end: { + row: null, + col: null + } + } + } + }, + database: { + schemas: [ + { + tableGroups: [], + tables: [], + refs: [] + } + ] + }, + preferences: { + dark: false, + theme: "dracula", + split: 25.0 + }, + parserError: { + location: { + start: { row: undefined, col: undefined }, + end: { row: undefined, col: undefined } + }, + type: undefined, + message: undefined + } + }), + getters: { + findField(state) { + return ((fieldId) => { + let field = null; + for (const schema of state.database.schemas) { + for (const table of schema.tables) { + field = table.fields.find(f => f.id === fieldId); + if (field) { + return field; + } + } + } + return undefined; + }); + }, + findTable(state) { + return ((tableId) => { + let table = null; + for (const schema of state.database.schemas) { + table = schema.tables.find(t => t.id === tableId); + if (table) + return table; + } + return undefined; + }); + }, + getSourceFormat(state) { + return state.source.format; + }, + getSourceText(state) { + return state.source.text; + }, + getDatabase(state) { + return state.database; + }, + getPositions(state) { + return state.positions; + }, + getPreferences(state) { + return state.preferences; + }, + getDark(state) { + return state.preferences.dark; + }, + getTheme(state) { + return state.preferences.theme; + }, + getSplit(state) { + return state.preferences.split; + }, + save(state) { + return { + source: state.source, + preferences: state.preferences + } + } + }, + actions: { + load(state) { + this.clearDatabase(); + this.$patch(state); + this.clearParserError(); + this.updateDatabase(); + }, + updateSourceText(sourceText) { + if (sourceText === this.source.text) return; + this.$patch({ + source: { + ...this.source, + text: sourceText + } + }); + }, + updatePositions(positions) { + this.$patch({ + positions: positions + }); + }, + clearDatabase() { + this.database = { + schemas: [ + { + tableGroups: [], + tables: [], + refs: [] + } + ] + }; + this.clearParserError(); + }, + updateDatabase() { + console.log("updating database..."); + try { + const database = Parser.parse(this.source.text, this.source.format); + database.normalize(); + this.database = database; + console.log("database: ", database) + this.clearParserError(); + console.log("updated database"); + const chart = useChartStore(); + chart.loadDatabase(database); + } catch (e) { + // do nothing + console.error(e); + this.updateParserError(e); + } + }, + updatePreferences(preferences) { + this.$patch({ + preferences: preferences + }); + }, + updateDark(dark) { + this.$patch({ + preferences: { + dark: dark + } + }); + }, + updateTheme(theme) { + this.$patch({ + preferences: { + theme + } + }); + }, + updateSplit(split) { + this.$patch({ + preferences: { + split + } + }); + }, + updateSelectionMarker(start, end) { + this.$patch({ + source: { + markers: { + selection: { + start: start, + end: end + } + } + } + }); + }, + updateScale(scale) { + this.$patch({ + positions: { + scale: scale + } + }); + }, + updateTranslation(translation) { + this.$patch({ + positions: { + translation: translation + } + }); + }, + clearParserError() { + this.updateParserError(undefined); + }, + updateParserError(err) { + if (err && err.location && err.location.start && err.location.end) { + this.$patch({ + parserError: { + location: { + start: { row: err.location.start.line - 1, col: err.location.start.column - 1 }, + end: { row: err.location.end.line - 1, col: err.location.end.column - 1 } + }, + type: 'error', + message: err.message + } + }); + } else if (err) { + this.$patch({ + parserError: { + location: undefined, + type: 'error', + message: err.message || '解析失败' + } + }); + } else { + this.$patch({ + parserError: undefined + }); + } + }, + toExportDTO() { + const schema = this.database.schemas?.[0]; + if (!schema) return { + tables: [], + refs: [], + tableGroups: [] + }; + + return { + tables: schema.tables || [], + refs: schema.refs || [], + tableGroups: schema.tableGroups || [] + }; + } + } +}); diff --git a/ER-Model/plugins/er-model/web-ui/src/store/files.js b/ER-Model/plugins/er-model/web-ui/src/store/files.js new file mode 100644 index 000000000..debd69c57 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/store/files.js @@ -0,0 +1,124 @@ +import { defineStore } from "pinia"; +import { useEditorStore } from "./editor"; +import { useChartStore } from "./chart"; + +import localforage from "localforage"; + +const fs = localforage.createInstance({ + name: "dbdiagram-oss", + storeName: "files" +}); + +export const useFilesStore = defineStore("files", { + state: () => ({ + saving: false, + lastSave: 0, + currentFile: "", + files: [] + }), + getters: { + getFiles(state) { + return state.files; + }, + getCurrentFile(state) { + return state.currentFile; + } + }, + actions: { + loadFileList() { + console.log("loading file list"); + + fs.keys() + .then(keys => { + this.files = keys; + }); + }, + loadFile(fileName) { + this.loadFileList(); + console.log("loading file", fileName); + + fs.getItem(fileName) + .then(file => { + if (file && file.source) { + const fSource = file.source; + const fChart = file.chart || {}; + + const editor = useEditorStore(); + const chart = useChartStore(); + + chart.load(fChart); + editor.load({ + source: fSource + }); + + this.$patch({ + currentFile: fileName + }); + + } + }); + }, + saveFile(fileName) { + this.saving = true; + if (!fileName) { + fileName = this.currentFile; + } + if (!fileName) { + const list = this.files; + let i = 1; + fileName = `Untitled (${i})`; + + while (list.indexOf(fileName) >= 0) { + fileName = `Untitled (${i++})`; + } + } + console.log("saving file", fileName); + + const editor = useEditorStore(); + const chart = useChartStore(); + + const file = { + ...editor.save, + chart: chart.save + }; + + fs.setItem(fileName, JSON.parse(JSON.stringify(file))).then(() => { + this.loadFileList(); + this.saving = false; + this.lastSave = new Date(); + if (this.currentFile !== fileName) { + this.$patch({ + currentFile: fileName + }); + } + }); + }, + newFile() { + this.$patch({ + currentFile: undefined + }); + + const editor = useEditorStore(); + const chart = useChartStore(); + + editor.$reset(); + chart.$reset(); + this.saveFile(); + }, + deleteFile(fileName) { + if (!fileName) return; + fs.removeItem(fileName).then(() => { + this.loadFileList(); + }); + }, + renameFile(newName) { + const oldName = this.currentFile; + this.saveFile(newName); + if (oldName !== newName) { + this.deleteFile(oldName); + this.currentFile = newName; + } + this.loadFileList(); + } + } +}); diff --git a/ER-Model/plugins/er-model/web-ui/src/store/plugin.js b/ER-Model/plugins/er-model/web-ui/src/store/plugin.js new file mode 100644 index 000000000..8de8e64af --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/store/plugin.js @@ -0,0 +1,71 @@ +import { debounce, throttle, useQuasar, Dark } from "quasar"; +import { decode, encode, save, load } from "src/utils/storageUtils"; +import { useFilesStore } from "src/store/files"; + + +const throttledSave = debounce(save, 150); +const autoSave = debounce(() => { + console.log("autosave"); + const files = useFilesStore(); + files.saveFile() +}, 200); + +export default ({ store }) => { + if (store.$id === "editor") { + + const throttledDatabaseUpdate = debounce(() => store.updateDatabase(), 100); + const handlePreferencesUpdate = (payload) => { + if(!payload) { + return; + } + if (payload.dark !== undefined) { + Dark.set(payload.dark); + } + }; + + (() => { + const preferences = load("preferences") || {}; + + store.$patch({ + preferences: preferences + }); + handlePreferencesUpdate(preferences); + })(); + + store.$subscribe((mutation, state) => { + if (mutation.storeId === "editor" && mutation.payload) { + if ("source" in mutation.payload) { + throttledDatabaseUpdate(); + autoSave(); + } + if ("preferences" in mutation.payload) { + throttledSave("preferences", state.preferences); + handlePreferencesUpdate(mutation.payload.preferences); + } + } + }); + } else if (store.$id === "chart") { + store.$subscribe((mutation, state) => { + autoSave(); + }); + } + else if(store.$id === "files") { + + (() => { + const currentFile = load("currentFile") || 'Untitled'; + store.$patch({ + currentFile: currentFile + }); + store.loadFileList(); + store.loadFile(currentFile); + })(); + + store.$subscribe((mutation, state) => { + if (mutation.storeId === "files" && mutation.payload) { + if ("currentFile" in mutation.payload) { + throttledSave("currentFile", state.currentFile); + } + } + }); + } +} diff --git a/ER-Model/plugins/er-model/web-ui/src/store/store-flag.d.ts b/ER-Model/plugins/er-model/web-ui/src/store/store-flag.d.ts new file mode 100644 index 000000000..e69de29bb diff --git a/ER-Model/plugins/er-model/web-ui/src/utils/MathUtil.js b/ER-Model/plugins/er-model/web-ui/src/utils/MathUtil.js new file mode 100644 index 000000000..3f615aaca --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/utils/MathUtil.js @@ -0,0 +1,18 @@ + +function snapSingle(value, snapSize) { + return Math.round(value/snapSize)*snapSize; +} + +export function snap(value, snapSize) { + console.log("snap", value, snapSize); + if(typeof value === "number") { + return snapSingle(value, snapSize); + } + else if('x' in value && 'y' in value) { + value.x = snapSingle(value.x); + value.y = snapSingle(value.y); + return value; + } + return value; +} +export default { snap }; diff --git a/ER-Model/plugins/er-model/web-ui/src/utils/auth.js b/ER-Model/plugins/er-model/web-ui/src/utils/auth.js new file mode 100644 index 000000000..3809f0561 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/utils/auth.js @@ -0,0 +1,7 @@ +// src/utils/auth.js +const TOKEN_KEY = 'opengauss-token' + +export const isLogin = () => !!localStorage.getItem(TOKEN_KEY) +export const getToken = () => localStorage.getItem(TOKEN_KEY) +export const setToken = (token) => localStorage.setItem(TOKEN_KEY, token) +export const clearToken = () => localStorage.removeItem(TOKEN_KEY) diff --git a/ER-Model/plugins/er-model/web-ui/src/utils/autoLayout.js b/ER-Model/plugins/er-model/web-ui/src/utils/autoLayout.js new file mode 100644 index 000000000..9cf3c9c57 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/utils/autoLayout.js @@ -0,0 +1,249 @@ +// Simple layered auto-layout for ER diagrams (inspired by Sugiyama) +// Usage (pseudo): +// import { computeLayout } from '@/utils/autoLayout' +// const positions = computeLayout({ tables, refs, getSize: id => store.getTable(id) }, { direction: 'LR' }) +// Object.entries(positions).forEach(([id, p]) => Object.assign(store.getTable(Number(id)), p)) + +const DEFAULT_NODE_WIDTH = 220 +const DEFAULT_NODE_HEIGHT = 120 + +const defaultOpts = { + direction: 'LR', // 'LR' | 'TB' + hGap: 140, + vGap: 100, + baseX: 0, + baseY: 0, + skipMultiplier: 1.1 +} + +function buildGraph(tables, refs) { + const nodes = new Map() + tables.forEach(t => nodes.set(t.id, { id: t.id, out: new Set(), in: new Set() })) + for (const r of refs) { + try { + const a = r.endpoints?.[0]?.fields?.[0]?.table?.id + const b = r.endpoints?.[1]?.fields?.[0]?.table?.id + if (a != null && b != null) { + // direction: parent -> child (referenced -> referencing) + nodes.get(a)?.out.add(b) + nodes.get(b)?.in.add(a) + } + } catch (e) { /* ignore */ } + } + return nodes +} + +function topoLayers(nodes) { + const indeg = new Map() + nodes.forEach(n => indeg.set(n.id, n.in.size)) + const layers = [] + const q = [] + nodes.forEach(n => { if (indeg.get(n.id) === 0) q.push(n.id) }) + const visited = new Set() + while (q.length) { + const layer = [] + const next = [] + for (const id of q) { + if (!visited.has(id)) { + visited.add(id) + layer.push(id) + nodes.get(id).out.forEach(v => { + indeg.set(v, indeg.get(v) - 1) + if (indeg.get(v) === 0) next.push(v) + }) + } + } + if (layer.length) layers.push(layer) + q.splice(0, q.length, ...next) + } + // remaining (cycles): place them in subsequent layers + const remaining = [...nodes.keys()].filter(id => !visited.has(id)) + if (remaining.length) layers.push(remaining) + return layers +} + +function orderByBarycenter(layers, nodes) { + for (let i = 1; i < layers.length; i++) { + const prev = new Map() + layers[i-1].forEach((id, idx) => prev.set(id, idx)) + layers[i].sort((a, b) => { + const score = id => { + const ins = [...nodes.get(id).in] + if (!ins.length) return Number.MAX_SAFE_INTEGER + const sum = ins.reduce((acc, p) => acc + (prev.get(p) ?? 0), 0) + return sum / ins.length + } + return score(a) - score(b) + }) + } + return layers +} + +function sizeCacheFactory(getSize) { + const cache = new Map() + return (id) => { + if (!cache.has(id)) { + try { + const raw = getSize?.(Number(id)) || {} + const width = Number.isFinite(raw?.width) ? raw.width : DEFAULT_NODE_WIDTH + const height = Number.isFinite(raw?.height) ? raw.height : DEFAULT_NODE_HEIGHT + cache.set(id, { width, height }) + } catch (e) { + cache.set(id, { width: DEFAULT_NODE_WIDTH, height: DEFAULT_NODE_HEIGHT }) + } + } + return cache.get(id) + } +} + +function average(values) { + if (!values?.length) return Number.NaN + const sum = values.reduce((acc, v) => acc + v, 0) + return sum / values.length +} + +export function computeLayout(ctx, opts = {}) { + const o = { ...defaultOpts, ...opts } + const { tables = [], refs = [], getSize } = ctx + + if (!tables.length) { + return {} + } + + const nodes = buildGraph(tables, refs) + let layers = topoLayers(nodes) + layers = orderByBarycenter(layers, nodes) + + const getSizeCached = sizeCacheFactory(getSize) + const layerIndex = new Map() + layers.forEach((layer, idx) => { + layer.forEach(id => layerIndex.set(id, idx)) + }) + + const maxNodeWidth = tables.reduce((max, t) => Math.max(max, getSizeCached(t.id).width), DEFAULT_NODE_WIDTH) + + const positions = {} + if (o.direction === 'LR') { + // columns = layers, rows inside each column + let xCursor = o.baseX + layers.forEach((column) => { + const columnNodes = column.map((id, index) => { + const size = getSizeCached(id) + const parents = [...(nodes.get(id)?.in ?? [])] + const parentCenters = parents + .map(pid => { + const parentPos = positions[pid] + if (!parentPos) return Number.NaN + const parentSize = getSizeCached(pid) + return parentPos.y + parentSize.height / 2 + }) + .filter(Number.isFinite) + + const parentAvg = average(parentCenters) + + const desiredY = Number.isFinite(parentAvg) + ? parentAvg - size.height / 2 + : o.baseY + index * (size.height + o.vGap) + + return { + id, + order: index, + size, + desired: desiredY + } + }) + + // stable ordering by desired position + columnNodes.sort((a, b) => { + if (!Number.isFinite(a.desired) && !Number.isFinite(b.desired)) return a.order - b.order + if (!Number.isFinite(a.desired)) return 1 + if (!Number.isFinite(b.desired)) return -1 + if (a.desired === b.desired) return a.order - b.order + return a.desired - b.desired + }) + + const columnWidth = columnNodes.reduce((max, node) => Math.max(max, node.size.width), DEFAULT_NODE_WIDTH) + + let yCursor = o.baseY + columnNodes.forEach((node) => { + const safeDesired = Number.isFinite(node.desired) ? node.desired : o.baseY + const y = Math.max(safeDesired, yCursor) + positions[node.id] = { + x: xCursor + (columnWidth - node.size.width) / 2, + y + } + yCursor = y + node.size.height + o.vGap + }) + + xCursor += columnWidth + o.hGap + }) + } else { + // TB: rows = layers + let yCursor = o.baseY + layers.forEach((row, rowIdx) => { + const rowNodes = row.map((id, index) => { + const size = getSizeCached(id) + const parents = [...(nodes.get(id)?.in ?? [])] + const parentCenters = parents + .map(pid => { + const parentPos = positions[pid] + if (!parentPos) return Number.NaN + const parentSize = getSizeCached(pid) + return parentPos.x + parentSize.width / 2 + }) + .filter(Number.isFinite) + + const parentAvg = average(parentCenters) + + let desiredX = Number.isFinite(parentAvg) + ? parentAvg - size.width / 2 + : o.baseX + index * (size.width + o.hGap) + + const maxSkip = parents.reduce((acc, pid) => { + const parentLayer = layerIndex.get(pid) + if (parentLayer == null) return acc + return Math.max(acc, rowIdx - parentLayer - 1) + }, 0) + + if (maxSkip > 0) { + desiredX += maxSkip * (maxNodeWidth + o.hGap) * o.skipMultiplier + } + + return { + id, + order: index, + size, + desired: desiredX + } + }) + + rowNodes.sort((a, b) => { + if (!Number.isFinite(a.desired) && !Number.isFinite(b.desired)) return a.order - b.order + if (!Number.isFinite(a.desired)) return 1 + if (!Number.isFinite(b.desired)) return -1 + if (a.desired === b.desired) return a.order - b.order + return a.desired - b.desired + }) + + const rowHeight = rowNodes.reduce((max, node) => Math.max(max, node.size.height), DEFAULT_NODE_HEIGHT) + let xCursor = o.baseX + + rowNodes.forEach((node) => { + const safeDesired = Number.isFinite(node.desired) ? node.desired : xCursor + const x = Math.max(safeDesired, xCursor) + positions[node.id] = { + x, + y: yCursor + (rowHeight - node.size.height) / 2 + } + xCursor = x + node.size.width + o.hGap + }) + + yCursor += rowHeight + o.vGap + }) + } + return positions +} + +export default { + computeLayout +} diff --git a/ER-Model/plugins/er-model/web-ui/src/utils/download.js b/ER-Model/plugins/er-model/web-ui/src/utils/download.js new file mode 100644 index 000000000..bcba6cad9 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/utils/download.js @@ -0,0 +1,17 @@ +/** + * Triggers a browser download from a data URL. + * @param {string} dataUrl The data URL (e.g., "data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==") + * @param {string} filename The desired name for the downloaded file. + */ +export function triggerDownload(dataUrl, filename) { + const link = document.createElement('a'); + link.href = dataUrl; + link.setAttribute('download', filename); + document.body.appendChild(link); + link.click(); + + // Clean up the link element + setTimeout(() => { + document.body.removeChild(link); + }, 0); +} diff --git a/ER-Model/plugins/er-model/web-ui/src/utils/exportDtoBuilder.js b/ER-Model/plugins/er-model/web-ui/src/utils/exportDtoBuilder.js new file mode 100644 index 000000000..6acb3d895 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/utils/exportDtoBuilder.js @@ -0,0 +1,136 @@ +export function buildExportDto(database, dialect) { + const schema = database.schemas[0]; + const tablesDtoMap = new Map(); + + // === 阶段一:构建基础表结构 DTO === + schema.tables.forEach(table => { + // 1. 收集主键列 + const primaryKeyColumns = []; + table.fields.forEach(field => { + if (field.pk) primaryKeyColumns.push(field.name); + }); + if (primaryKeyColumns.length === 0) { + (table.indexes || []).forEach(index => { + if (index.pk) { + index.columns.forEach(col => primaryKeyColumns.push(col.value)); + } + }); + } + + // 2. 构建列信息,并做更健壮的类型 & 长度解析 + const columns = table.fields.map(field => { + let dataType = (field.type.name || field.type.type_name).split('(')[0]; + let args = Array.isArray(field.type.args) ? field.type.args : (field.type.args ? [field.type.args] : []); + + if (table.name === 'posts') { + console.log("post表信息:", table) + } + + if (table.name === 'categories') { + console.log("categories表信息:", table) + } + + return { + columnName: field.name, + dataType: dataType, + length: parseInt(args[0], 10) || null, + precision: parseInt(args[1], 10) || null, + nullable: !field.not_null, + primaryKey: !!field.pk, + unique: !!field.unique, + defaultValue: field.dbdefault?.value ?? field.settings?.default ?? null, + autoIncrement: !!field.increment, + comment: field.note || "" + }; + }); + + // 3. 构建索引信息 + const indexes = (table.indexes || []).map(index => ({ + indexName: index.name || "", + columnNames: index.columns.map(col => col.value), + unique: !!index.unique, + indexType: index.type || "btree", + comment: index.note || "", + pk: !!index.pk + })); + + // 4. 初始化 DTO + tablesDtoMap.set(table.name, { + tableName: table.name, + comment: table.note || "", + primaryKeys: primaryKeyColumns.length > 0 ? [primaryKeyColumns] : [], + columns, + indexes, + foreignKeys: [] + }); + }); + + // === 阶段二:重构!收集所有 Ref 并统一处理 === + const allRefs = new Map(); + const addedFkSignatures = new Set(); + + // 1. 从全局 schema.refs 收集 + (schema.refs || []).forEach(ref => { + // 假设 ref 有一个唯一的 id 属性,用于去重 + if (ref.id != null) allRefs.set(ref.id, ref); + }); + + // 2. 从每个表的每个字段内部收集(针对内联外键的特殊结构) + schema.tables.forEach(table => { + table.fields.forEach(field => { + if (Array.isArray(field.endpoints) && field.endpoints.length > 0) { + field.endpoints.forEach(endpoint => { + // 核心!从字段的端点中提取完整的 ref 对象 + if (endpoint.ref && endpoint.ref.id != null) { + allRefs.set(endpoint.ref.id, endpoint.ref); + } + }); + } + }); + }); + + // 3. 遍历收集到的所有唯一的 Ref + allRefs.forEach(ref => { + if (ref.endpoints.length !== 2) return; + const [epA, epB] = ref.endpoints; + + // 3.1 判断 childEp / parentEp (使用我们之前已修正的健壮逻辑) + let childEp, parentEp; + if (epA.relation === '*' && epB.relation === '1') { + childEp = epA; parentEp = epB; + } else if (epB.relation === '*' && epA.relation === '1') { + childEp = epB; parentEp = epA; + } else { + const tblA = schema.tables.find(t => t.name === epA.tableName); + const hasInlineA = tblA.fields.some(f => + epA.fieldNames.includes(f.name) && (f.ref || (Array.isArray(f.endpoints) && f.endpoints.length > 0)) + ); + if (hasInlineA) { childEp = epA; parentEp = epB; } + else { childEp = epB; parentEp = epA; } + } + + // 3.2 生成规范化签名并去重 + const signature = `${childEp.tableName}[${childEp.fieldNames.join(',')}]→` + + `${parentEp.tableName}[${parentEp.fieldNames.join(',')}]`; + if (addedFkSignatures.has(signature)) return; + + // 3.3 写入 DTO + const dto = tablesDtoMap.get(childEp.tableName); + if (dto) { + dto.foreignKeys.push({ + constraintName: ref.name || null, + columnNames: [...childEp.fieldNames], + referencedTable: parentEp.tableName, + referencedColumnNames: [...parentEp.fieldNames], + onUpdateAction: (ref.onUpdate || 'NO ACTION').toUpperCase(), + onDeleteAction: (ref.onDelete || 'NO ACTION').toUpperCase() + }); + addedFkSignatures.add(signature); + } + }); + + return { + dialect, + tables: Array.from(tablesDtoMap.values()) + }; +} diff --git a/ER-Model/plugins/er-model/web-ui/src/utils/exportSqlFile.js b/ER-Model/plugins/er-model/web-ui/src/utils/exportSqlFile.js new file mode 100644 index 000000000..6c586fdc4 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/utils/exportSqlFile.js @@ -0,0 +1,61 @@ +import { useEditorStore } from "../store/editor"; +import { buildExportDto } from "./exportDtoBuilder"; +import { api } from '../boot/axios'; +import { triggerDownload } from './download'; // ✅ Import the utility +import { Notify } from 'quasar'; + +export async function exportSqlFile(dialect) { + const editor = useEditorStore(); + const database = editor.database; + const exportDto = buildExportDto(database, dialect); + + try { + const response = await api.post('/export/sql', exportDto, { + responseType: 'text' + }); + + const sqlContent = response.data; + const dataUrl = `data:text/sql;charset=utf-8,${encodeURIComponent(sqlContent)}`; + + // ✅ Use the shared download utility + triggerDownload(dataUrl, 'er-diagram.sql'); + + } catch (err) { + console.error('导出 SQL 失败:', err); + } +} + +/** + * 调用后端 API,将 DBML 文本转换为 SQL。 + * @param {string} dbmlText - 包含 DBML 定义的字符串。 + * @param {string} dialect - 目标数据库方言 (例如 "mysql", "postgresql", "opengauss")。 + */ +export async function exportSqlFromDbml(dbmlText, dialect) { + try { + // 检查 dbmlText 是否为空 + if (!dbmlText || !dbmlText.trim()) { + console.error('DBML content is empty. Cannot export SQL.'); + Notify.create({ type: 'warning', message: 'DBML内容为空,无法导出。' }); + return; + } + + // 将数据库方言作为 URL 的查询参数 + const apiUrl = `/export/sql-from-dbml?dialect=${encodeURIComponent(dialect.toUpperCase())}`; + + const response = await api.post(apiUrl, dbmlText, { + headers: { 'Content-Type': 'text/plain' }, + responseType: 'json' // 改成json + }); + + console.log("response: ", response.data) + console.log("response content: ", response.data.sqlContent) + + const sql = response.data.sqlContent; + triggerDownload(`data:text/sql;charset=utf-t,${encodeURIComponent(sql)}`, `er-export-${dialect}.sql`); + } catch (err) { + console.error('导出 SQL 失败', err); + // 向用户显示更友好的错误信息 + const errorMessage = err.response?.data || '导出失败,请检查后端服务是否正常。'; + Notify.create({ type: 'negative', message: `导出 SQL 失败: ${errorMessage}` }); + } +} diff --git a/ER-Model/plugins/er-model/web-ui/src/utils/exportVisualFile.js b/ER-Model/plugins/er-model/web-ui/src/utils/exportVisualFile.js new file mode 100644 index 000000000..fe838b5e2 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/utils/exportVisualFile.js @@ -0,0 +1,222 @@ +import { jsPDF } from 'jspdf'; +import { triggerDownload } from './download'; // 仍然使用你现有的下载工具 +import { api } from '../boot/axios'; // 新增:调用后端 AjaxResult 接口 + +/** + * 内部工具:根据来源获取 PlantUML 文本 + * @param {'db'|'dbml'} plantumlSource 来源类型 + * @param {object} dbParams 当来源为 'db' 时需要:{ clusterId, nodeId, databaseName, schemaName } + * @param {string} dbmlText 当来源为 'dbml' 时需要:纯 DBML 文本 + * @returns {Promise} 纯 PlantUML 文本 + */ +async function fetchPlantUml({ plantumlSource, dbParams, dbmlText }) { + if (plantumlSource === 'db') { + const res = await api.get('/export/plantuml-from-db', { params: dbParams }); + console.log("plantUML-res-db: ", res) + if (res?.code !== 200) { + throw new Error(res?.msg || '导出 PlantUML 失败(DB)'); + } + return res.data; // AjaxResult.data + } + + if (plantumlSource === 'dbml') { + // 后端 ExportController 接口:@PostMapping("/plantuml-from-dbml"),@RequestBody String + const res = await api.post('/export/plantuml-from-dbml', dbmlText, { + headers: { 'Content-Type': 'text/plain; charset=utf-8' } + }); + console.log("plantUML-res-dbml: ", res) + if (res?.code !== 200) { + throw new Error(res?.msg || '导出 PlantUML 失败(DBML)'); + } + return res.data; // AjaxResult.data + } + + throw new Error('plantumlSource 必须是 "db" 或 "dbml"'); +} + +/** + * 将SVG元素及其所有子元素的计算样式(Computed Styles)应用为内联样式。 + */ +function inlineAllStyles(svgElement) { + for (const el of svgElement.querySelectorAll('*')) { + const computedStyle = window.getComputedStyle(el); + const inlineStyle = []; + const propertiesToInline = [ + 'fill', 'fill-opacity', 'stroke', 'stroke-width', 'stroke-opacity', + 'stroke-linecap', 'stroke-linejoin', 'font-family', 'font-size', + 'font-weight', 'font-style', 'color', 'text-anchor', 'dominant-baseline', + 'vertical-align', 'opacity', 'display', 'transform', 'cursor', + 'pointer-events', 'user-select', + ]; + const forceInlineProperties = new Set(['fill', 'stroke', 'display']); + for (const prop of propertiesToInline) { + const value = computedStyle.getPropertyValue(prop); + if (value && (forceInlineProperties.has(prop) || (value !== 'normal' && value !== 'auto'))) { + inlineStyle.push(`${prop}:${value}`); + } + } + if (inlineStyle.length) { + const existingStyle = el.getAttribute('style') || ''; + el.setAttribute('style', `${existingStyle}; ${inlineStyle.join('; ')}`); + } + } +} + +/** + * 精确计算所有可见内容元素的视觉边界框(Bounding Box)。 + */ +function calculateTightBBox(svgRoot, contentSelector) { + const elements = svgRoot.querySelectorAll(contentSelector); + if (elements.length === 0) { + return { x: 0, y: 0, width: svgRoot.width.baseVal.value, height: svgRoot.height.baseVal.value }; + } + const svgRect = svgRoot.getBoundingClientRect(); + let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; + elements.forEach(el => { + const rect = el.getBoundingClientRect(); + minX = Math.min(minX, rect.left); + minY = Math.min(minY, rect.top); + maxX = Math.max(maxX, rect.right); + maxY = Math.max(maxY, rect.bottom); + }); + const padding = 5; + return { + x: (minX - svgRect.left) - padding, + y: (minY - svgRect.top) - padding, + width: (maxX - minX) + (padding * 2), + height: (maxY - minY) + (padding * 2), + }; +} + +/** + * 根据SVG元素创建一个高分辨率的Canvas,并填充白色背景。 + */ +function createHighResCanvas(svgElement, tightBBox, scale) { + return new Promise((resolve, reject) => { + const scaledWidth = tightBBox.width * scale; + const scaledHeight = tightBBox.height * scale; + + const svgStr = ` + ${svgElement.innerHTML} + `; + + const canvas = document.createElement('canvas'); + canvas.width = scaledWidth; + canvas.height = scaledHeight; + const ctx = canvas.getContext('2d'); + + ctx.fillStyle = '#FFFFFF'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + const img = new Image(); + const svgBlob = new Blob([svgStr], { type: 'image/svg+xml;charset=utf-8' }); + const url = URL.createObjectURL(svgBlob); + + img.onload = function () { + ctx.drawImage(img, 0, 0); + URL.revokeObjectURL(url); + resolve(canvas); + }; + img.onerror = function (e) { + console.error("加载SVG图片到Image对象失败", e); + URL.revokeObjectURL(url); + reject(new Error("Image loading failed")); + }; + img.src = url; + }); +} + +/** + * 导出视觉文件(PNG / PDF / PlantUML) + * + * @param {object} options + * @param {'png'|'pdf'|'plantuml'} options.format + * @param {string} options.fileName 不含扩展名 + * @param {number} options.scale PNG/PDF 分辨率倍数(默认 3) + * @param {'db'|'dbml'} [options.plantumlSource] 当 format='plantuml' 时必填 + * @param {object} [options.dbParams] 当 plantumlSource='db':{ clusterId, nodeId, databaseName, schemaName } + * @param {string} [options.dbmlText] 当 plantumlSource='dbml':DBML 文本 + */ +export async function exportVisualFile({ + format = 'png', + fileName = 'diagram', + scale = 3, + plantumlSource, + dbParams, + dbmlText + }) { + // --------- 新增:PlantUML 分支(不依赖 SVG/Canvas)--------- + if (format === 'plantuml') { + try { + const puml = await fetchPlantUml({ plantumlSource, dbParams, dbmlText }); + if (!puml || !puml.trim()) { + throw new Error('后端未返回有效的 PlantUML 内容'); + } + + // 用 Blob/URL 触发下载(更稳,避免超长 data: URL) + const blob = new Blob([puml], { type: 'text/plain;charset=utf-8' }); + const url = URL.createObjectURL(blob); + triggerDownload(url, `${fileName}.puml`); + // 交给浏览器下载后再释放 URL + setTimeout(() => URL.revokeObjectURL(url), 0); + } catch (err) { + console.error('导出 PlantUML 失败:', err); + } + return; + } + + // --------- 以下为原有 PNG / PDF 流程(依赖 SVG/Canvas)--------- + const svgElement = document.querySelector('svg.db-chart'); + if (!svgElement) { + console.error('未找到 SVG 元素'); + return; + } + + const backgroundLayer = svgElement.querySelector('#background-layer'); + const toolsLayer = svgElement.querySelector('#tools-layer'); + const hitboxes = svgElement.querySelectorAll('.db-ref__hitbox'); + + try { + if (backgroundLayer) backgroundLayer.style.display = 'none'; + if (toolsLayer) toolsLayer.style.display = 'none'; + hitboxes.forEach(box => { box.style.display = 'none'; }); + + inlineAllStyles(svgElement); + const tightBBox = calculateTightBBox(svgElement, '.db-table, .db-ref__path'); + + const canvas = await createHighResCanvas(svgElement, tightBBox, scale); + if (!canvas) return; + + if (format === 'png') { + const pngDataUrl = canvas.toDataURL('image/png'); + triggerDownload(pngDataUrl, `${fileName}.png`); + + } else if (format === 'pdf') { + const imageData = canvas.toDataURL('image/jpeg', 0.9); + const { width, height } = canvas; + const orientation = width > height ? 'l' : 'p'; + + const pdf = new jsPDF({ + orientation: orientation, + unit: 'pt', + format: [width, height] + }); + + pdf.addImage(imageData, 'JPEG', 0, 0, width, height); + + const pdfBlob = pdf.output('blob'); + const pdfUrl = URL.createObjectURL(pdfBlob); + triggerDownload(pdfUrl, `${fileName}.pdf`); + URL.revokeObjectURL(pdfUrl); + } else { + console.error('不支持的导出格式:', format); + } + + } catch (error) { + console.error("导出文件时发生错误:", error); + } finally { + if (backgroundLayer) backgroundLayer.style.display = ''; + if (toolsLayer) toolsLayer.style.display = ''; + hitboxes.forEach(box => { box.style.display = ''; }); + } +} diff --git a/ER-Model/plugins/er-model/web-ui/src/utils/storageUtils.js b/ER-Model/plugins/er-model/web-ui/src/utils/storageUtils.js new file mode 100644 index 000000000..c26a681c1 --- /dev/null +++ b/ER-Model/plugins/er-model/web-ui/src/utils/storageUtils.js @@ -0,0 +1,34 @@ + +// export const encode = (value) => btoa(JSON.stringify(value)); +// export const decode = (encoded) => JSON.parse(atob(encoded)); +export const encode = (value) => JSON.stringify(value); +export const decode = (encoded) => JSON.parse(encoded); + +export const del = (key) => { + const item = localStorage.getItem(`dbml-${key}`); + if (item) { + localStorage.removeItem(`dbml-${key}`); + } +}; +export const save = (key, value) => { + localStorage.setItem(`dbml-${key}`, encode(value)); +}; + +export const load = (key) => { + const value = localStorage.getItem(`dbml-${key}`); + if (value && value !== 'undefined') { + return decode(value); + } + return undefined; +}; + +export const list = () => { + const items = []; + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + if (/^dbml-.*/.test(key)) { + items.push(key.replace(/^dbml-(.*)/, "$1")); + } + } + return items; +}; diff --git a/ER-Model/pom.xml b/ER-Model/pom.xml new file mode 100644 index 000000000..20bad5797 --- /dev/null +++ b/ER-Model/pom.xml @@ -0,0 +1,622 @@ + + + 4.0.0 + org.opengauss + visualtool-parent + ${admin.version} + + + UTF-8 + 17 + ${java.version} + ${java.version} + true + ${user.home}/datakit/npm-cache + 4.1.2 + 3.5.6 + 7.0.0-RC3 + + 1.2.27 + + 2.1.4 + 3.5.14 + 2.1.1 + 4.3.1 + + 6.0.0-og + 9.4.0 + 3.49.1.0 + 42.7.7 + + 1.10.0 + 2.20.0 + 1.6.0 + 3.2.2 + 1.28.0 + 3.18.0 + 3.11.1 + 1.5.0 + 1.19.0 + 1.3.5 + 2.11.1 + 2.15.0 + 4.4.16 + 4.5.13 + 5.2.1 + 4.1.127.Final + + 33.5.0-jre + 1.18.34 + 0.9.1 + 1.21 + 2.27.2 + 2.3 + 2.1.1 + 1.15.0 + 5.8.38 + 5.7.17 + 4.0.0 + 1.44.1 + 6.4.6 + 1.7.36 + + 2.19.2 + 1.2.83 + 3.0.6 + 5.2.0 + 4.13.2 + 6.0.0 + 4.0.3 + 5.4.0 + 1.15.1 + 2.5.0 + 3.2.2 + 0.8.13 + 3.11.0 + 3.3.0 + 3.2.1 + 3.2.0 + 3.0.0-M7 + 3.5.1 + 2.7 + + + + false + + visualtool + + openGauss All-in-one platform + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + org.springframework.boot + spring-boot-starter + ${spring-boot.version} + + + spring-boot-starter-logging + org.springframework.boot + + + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + spring-boot-starter-logging + org.springframework.boot + + + + + org.springframework.boot + spring-boot-starter-websocket + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-dation + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-aop + ${spring-boot.version} + + + spring-boot-starter-logging + org.springframework.boot + + + + + org.springframework.boot + spring-boot-starter-security + ${spring-boot.version} + + + spring-boot-starter-logging + org.springframework.boot + + + + + org.springframework.boot + spring-boot-starter-log4j2 + ${spring-boot.version} + + + + + org.springframework.boot + spring-boot-starter-actuator + ${spring-boot.version} + + + + com.baomidou + mybatis-plus-spring-boot3-starter + ${mybatis-plus.version} + + + com.baomidou + mybatis-plus-generator + ${mybatis-plus.version} + + + com.baomidou + mybatis-plus-jsqlparser + ${mybatis-plus.version} + + + com.baomidou + mybatis-plus-extension + ${mybatis-plus.version} + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.boot.version} + + + com.baomidou + dynamic-datasource-spring-boot3-starter + ${dynamic-datasource.version} + + + + com.alibaba + druid-spring-boot-3-starter + ${druid.version} + + + com.github.mwiede + jsch + ${jsch.version} + + + org.yaml + snakeyaml + ${snakeyaml.version} + + + commons-validator + commons-validator + ${commons-validator.version} + + + commons-io + commons-io + ${commons.io.version} + + + commons-fileupload + commons-fileupload + ${commons.fileupload.version} + + + org.apache.commons + commons-compress + ${commons.compress.version} + + + commons-collections + commons-collections + ${commons.collections.version} + + + org.apache.commons + commons-pool2 + ${commons.pool2.version} + + + commons-codec + commons-codec + ${commons-codec.version} + + + commons-logging + commons-logging + ${commons-logging.version} + + + org.apache.commons + commons-lang3 + ${common.lang3.version} + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + org.apache.httpcomponents + httpcore + ${httpcore.version} + + + org.apache.httpcomponents.client5 + httpclient5 + ${httpclient5.version} + + + org.apache.httpcomponents.client5 + httpclient5-fluent + ${httpclient5.version} + + + org.apache.httpcomponents.core5 + httpcore5-h2 + ${httpclient5.version} + + + org.opengauss + visualtool-api + ${admin.version} + + + org.opengauss + visualtool-framework + ${admin.version} + + + org.opengauss + visualtool-service + ${admin.version} + + + org.opengauss + visualtool-common + ${admin.version} + + + org.opengauss + jsch-session-pool + ${admin.version} + + + org.projectlombok + lombok + ${lombok.version} + + + com.alibaba + fastjson + ${fastjson.version} + + + com.google.guava + guava + ${guava.version} + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + + + com.github.oshi + oshi-core + ${oshi.version} + + + eu.bitwalker + UserAgentUtils + ${bitwalker.version} + + + com.github.mwiede + jsch + ${jsch.version} + + + + io.micrometer + micrometer-registry-prometheus + ${prometheus.version} + + + com.gitee.starblues + spring-brick-bootstrap + ${spring-brick.version} + + + com.alibaba + easyexcel + ${easyexcel.version} + + + org.apache.poi + poi + ${poi.version} + + + org.apache.poi + poi-ooxml + ${poi.version} + + + com.gitee.starblues + spring-brick-maven-packager + ${spring-brick.version} + + + cn.hutool + hutool-all + ${hutool.version} + + + org.opengauss + opengauss-jdbc + ${opengauss-jdbc.version} + + + com.mysql + mysql-connector-j + ${mysql-jdbc.version} + + + org.xerial + sqlite-jdbc + ${sqlite.version} + + + org.postgresql + postgresql + ${postgresql-jdbc.version} + + + jakarta.websocket + jakarta.websocket-all + ${jakarta.websocket.version} + + + + com.lmax + disruptor + ${disruptor.version} + + + io.opentelemetry + opentelemetry-sdk + ${opentelemetry.version} + + + commons-net + commons-net + ${commons-net.version} + + + org.quartz-scheduler + quartz + ${quartz.version} + + + org.mockito + mockito-inline + ${mockito.version} + test + + + + + openGauss-datakit/visualtool-api + openGauss-datakit/visualtool-framework + openGauss-datakit/visualtool-service + openGauss-datakit/visualtool-common + openGauss-datakit/cov-analysis + openGauss-datakit/jsch-session-pool + openGauss-datakit-agent + plugins/base-ops + plugins/er-model + + pom + + + + dev + + true + + + dev + + + + + prod + + prod + + + + + + + + org.codehaus.mojo + versions-maven-plugin + 2.7 + + false + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + ${java.home}/lib/rt.jar + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + 4096m + 4096m + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + -Xmx512m + + + + org.apache.maven.plugins + maven-resources-plugin + ${maven-resources-plugin.version} + + + @ + + false + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + default-prepare-agent + + prepare-agent + + + + post-unit-test + package + + report + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.0.0 + + + ensure-cache-dir + initialize + + run + + + + + + + + + + + org.codehaus.mojo + exec-maven-plugin + ${exec-maven-plugin.version} + + ${build.frontend.skip} + + ${npm.cache} + + + + + exec-npm-install + generate-resources + + exec + + + npm + + install + --frozen-lockfile + --prefer-offline + --no-optional + + ${web.path} + + + + + exec-npm-run-build + generate-resources + + exec + + + npm + + run + build + + ${web.path} + + + + + + + + diff --git a/ER-Model/run.sh b/ER-Model/run.sh new file mode 100644 index 000000000..38a492a64 --- /dev/null +++ b/ER-Model/run.sh @@ -0,0 +1,188 @@ +#!/bin/bash + +APP_NAME="datakit" +JAR_PATTERN="openGauss-datakit-*.jar" +PID_FILE="datakit.pid" +LOG_DIR="logs" +LOG_FILE="${LOG_DIR}/visualtool-main.out" + +mkdir -p "${LOG_DIR}" + +get_jar_path() { + local jar_file=$(ls -t ${JAR_PATTERN} 2>/dev/null | head -n 1) + if [ -z "${jar_file}" ]; then + echo "error : No matching JAR file found." + exit 1 + fi + + if [ -f "${jar_file}" ]; then + echo $(realpath "${jar_file}") + else + echo "error : JAR file not exist : ${jar_file}" + exit 1 + fi +} + +refresh_pid() { + JAR_FILE=$(get_jar_path) + PID=$(ps aux | grep java | grep -F -- "${JAR_FILE}" | grep -v grep | awk '{print $2}' | head -n 1) + if [ -n "${PID}" ]; then + echo "${PID}" > "${PID_FILE}" + else + rm -f "${PID_FILE}" + fi +} + +start_up() { + JAR_FILE=$(get_jar_path) + + if [ -z "${DATA_KIT_AES_KEY}" ]; then + echo "error : DATA_KIT_AES_KEY environment variable is not set." + echo " Please set it with --aes-key option" + exit 1 + fi + DEFAULT_JAVA_OPTS="-Xms2048m -Xmx4096m" + JAVA_OPTS=${JAVA_OPTS:-$DEFAULT_JAVA_OPTS} + echo "Starting ${APP_NAME} with DATA_KIT_AES_KEY..." + export DATA_KIT_AES_KEY=${DATA_KIT_AES_KEY} + nohup java ${JAVA_OPTS} -jar "${JAR_FILE}" --spring.profiles.active=temp > "${LOG_FILE}" 2>&1 & + + echo $! > "${PID_FILE}" + echo "${APP_NAME} started. PID: $(cat ${PID_FILE})" +} + +start() { + refresh_pid + + if [ -s "${PID_FILE}" ]; then + PID=$(cat "${PID_FILE}") + if ps -p "${PID}" > /dev/null; then + echo "Datakit is already running with PID : ${PID}" + exit 1 + else + echo "Stale PID file. Cleaning up." + rm -f "${PID_FILE}" + fi + fi + + start_up +} + +stop() { + refresh_pid + + if [ -s "${PID_FILE}" ]; then + PID=$(cat "${PID_FILE}") + kill -9 "${PID}" >/dev/null 2>&1 + rm -f "${PID_FILE}" + echo "${APP_NAME} stopped." + else + echo "${APP_NAME} is not running." + fi +} + +restart() { + stop + sleep 2 + start_up +} + +status() { + refresh_pid + + if [ -s "${PID_FILE}" ]; then + PID=$(cat "${PID_FILE}") + if ps -p "${PID}" > /dev/null; then + echo "status : ${APP_NAME} is running with PID: ${PID}" + echo "JAR PATH : $(get_jar_path)" + else + echo "warn : Stale PID file, ${APP_NAME} may not be running." + rm -f "${PID_FILE}" + fi + else + echo "status : ${APP_NAME} is not running." + fi +} + +show_help() { + echo "Usage: $0 {start|stop|restart|status} [--aes-key ]" + echo "Commands:" + echo " start Start datakit service" + echo " stop Stop datakit service" + echo " restart Restart datakit service" + echo " status Show service status" + echo "" + echo "Options:" + echo " --aes-key Set DATA_KIT_AES_KEY for this session" + echo "" + echo "Environment:" + echo " If DATA_KIT_AES_KEY is already set in environment," + echo " --aes-key option will be ignored" +} + +COMMAND="$1" +shift + +while [[ $# -gt 0 ]]; do + case "$1" in + --aes-key) + if [[ -z "$2" || "$2" == --* ]]; then + echo "Error: --aes-key requires a value" + show_help + exit 1 + fi + + if [ -z "${DATA_KIT_AES_KEY}" ]; then + export DATA_KIT_AES_KEY="$2" + echo "Using command-line AES key" + else + echo "Warning: DATA_KIT_AES_KEY already set in environment. Ignoring --aes-key option" + fi + shift 2 + ;; + *) + echo "Unknown option: $1" + show_help + exit 1 + ;; + esac +done + +case "${COMMAND}" in + start) + if [ -z "${DATA_KIT_AES_KEY}" ]; then + echo "Error: AES key is required for 'start' command" + echo "Set it via:" + echo " export DATA_KIT_AES_KEY='your_key'" + echo "Or use: $0 start --aes-key your_key" + exit 1 + fi + + if [ ${#DATA_KIT_AES_KEY} -lt 6 ]; then + echo "Error: AES key must be at least 6 characters" + exit 1 + fi + + echo "Starting service with AES key (length: ${#DATA_KIT_AES_KEY})" + start + ;; + stop) + stop + ;; + restart) + restart + ;; + status) + status + ;; + help|--help|-h) + show_help + ;; + *) + echo "Unknown command: ${COMMAND}" + show_help + exit 1 + ;; +esac + +exit 0 \ No newline at end of file