diff --git a/.env.example b/.env.example index 5979522a..886ce7a2 100644 --- a/.env.example +++ b/.env.example @@ -15,6 +15,8 @@ MYSQL_ROOT_PASSWORD=Data@123456 REDIS_HOST=redis REDIS_PASSWORD=Data@123456 +REDIS_DATABASE=0 +REDIS_DATA_VERSION=1 GUACD_HOST=guacd GUACD_PORT=4822 diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index a21ba24f..716a564b 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -3,127 +3,131 @@ name: Docker Publish on: push: tags: - - 'v*' # Trigger on version tags like v1.0.0 - workflow_dispatch: # Allow manual trigger + - 'v*' + workflow_dispatch: jobs: - build-and-push: + build-project: runs-on: ubuntu-latest permissions: - contents: read # To read repository content - packages: write # To push packages to GitHub Container Registry + contents: read + packages: write - env: - DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_ORGNAME }} steps: - - name: Checkout repository + - name: 🌱 Checkout repository uses: actions/checkout@v4 - - name: Set up QEMU + - name: ⚙️ Set up JDK 8 + uses: actions/setup-java@v4 + with: + java-version: '8' + distribution: 'temurin' + cache: 'maven' + + - name: ⚙️ Set up Node.js 18 + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: 🔧 Install pnpm + run: npm i -g pnpm + + - name: 📦 Build Java + run: mvn -U clean install -DskipTests + + - name: 📦️ Build UI + working-directory: ./orion-visor-ui + run: | + pnpm install + pnpm build + + - name: 📁 Prepare build context + run: | + cp -r ./sql ./docker/mysql/sql + cp -r ./orion-visor-ui/dist ./docker/ui/dist + cp ./orion-visor-launch/target/orion-visor-launch.jar ./docker/service/orion-visor-launch.jar + + - name: 📤 Upload build context + uses: actions/upload-artifact@v4 + with: + name: docker-context + path: docker + + build-and-push: + needs: build-project + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + strategy: + matrix: + service: [ adminer, guacd, mysql, redis, service, ui ] + + env: + GITHUB_REGISTRY: ghcr.io + ALIYUN_REGISTRY: registry.cn-hangzhou.aliyuncs.com + ALIYUN_NAMESPACE: ${{ vars.ALIYUN_NAMESPACE }} + DOCKERHUB_NAMESPACE: ${{ vars.DOCKERHUB_NAMESPACE }} + + steps: + - name: 📥 Download build context + uses: actions/download-artifact@v4 + with: + name: docker-context + path: docker + + - name: ⚙️ Set up QEMU uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx + - name: 🔧 Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Login to Docker Hub + - name: 🐳 Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Login to GitHub Container Registry + - name: 🐳 Login to GitHub Container Registry uses: docker/login-action@v3 with: - registry: ghcr.io + registry: ${{ env.GITHUB_REGISTRY }} username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract Docker metadata - id: meta # Giving an ID to this step to reference its outputs later + - name: 🐳 Login to Aliyun Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.ALIYUN_REGISTRY }} + username: ${{ secrets.ALIYUN_USERNAME }} + password: ${{ secrets.ALIYUN_TOKEN }} + + - name: 📦 Extract Docker metadata + id: meta uses: docker/metadata-action@v5 with: - images: | # Define base image names for metadata generation - orion-visor-adminer - orion-visor-guacd - orion-visor-mysql - orion-visor-redis - orion-visor-service - orion-visor-ui - tags: | # Define how tags are generated - type=semver,pattern={{version}} # Main strategy: git tag v1.2.3 will produce tag 1.2.3 - type=semver,pattern={{major}}.{{minor}} # e.g., v1.2.3 -> 1.2 - type=semver,pattern={{major}} # e.g., v1.2.3 -> 1 + images: | + ${{ env.DOCKERHUB_NAMESPACE }}/orion-visor-${{ matrix.service }} + ${{ env.GITHUB_REGISTRY }}/${{ github.repository_owner }}/orion-visor-${{ matrix.service }} + ${{ env.ALIYUN_REGISTRY }}/${{ env.ALIYUN_NAMESPACE }}/orion-visor-${{ matrix.service }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} - # --- Build and push generic images --- - - - name: Build and push orion-visor-adminer + - name: 🛠️ Build and push Docker image for orion-visor-${{ matrix.service }} uses: docker/build-push-action@v5 with: - context: . - file: ./docker/adminer/Dockerfile + context: ./docker + file: ./docker/${{ matrix.service }}/Dockerfile push: true tags: | - ${{ env.DOCKERHUB_USERNAME }}/orion-visor-adminer:${{ steps.meta.outputs.version }} - ghcr.io/${{ github.repository_owner }}/orion-visor-adminer:${{ steps.meta.outputs.version }} + ${{ env.DOCKERHUB_NAMESPACE }}/orion-visor-${{ matrix.service }}:${{ steps.meta.outputs.version }} + ${{ env.DOCKERHUB_NAMESPACE }}/orion-visor-${{ matrix.service }}:latest + ${{ env.GITHUB_REGISTRY }}/${{ github.repository_owner }}/orion-visor-${{ matrix.service }}:${{ steps.meta.outputs.version }} + ${{ env.GITHUB_REGISTRY }}/${{ github.repository_owner }}/orion-visor-${{ matrix.service }}:latest + ${{ env.ALIYUN_REGISTRY }}/${{ env.ALIYUN_NAMESPACE }}/orion-visor-${{ matrix.service }}:${{ steps.meta.outputs.version }} + ${{ env.ALIYUN_REGISTRY }}/${{ env.ALIYUN_NAMESPACE }}/orion-visor-${{ matrix.service }}:latest labels: ${{ steps.meta.outputs.labels }} platforms: linux/amd64,linux/arm64 - - - name: Build and push orion-visor-guacd - uses: docker/build-push-action@v5 - with: - context: . - file: ./docker/guacd/Dockerfile - push: true - tags: | - ${{ env.DOCKERHUB_USERNAME }}/orion-visor-guacd:${{ steps.meta.outputs.version }} - ghcr.io/${{ github.repository_owner }}/orion-visor-guacd:${{ steps.meta.outputs.version }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64 - - - name: Build and push orion-visor-mysql - uses: docker/build-push-action@v5 - with: - context: . - file: ./docker/mysql/Dockerfile - push: true - tags: | - ${{ env.DOCKERHUB_USERNAME }}/orion-visor-mysql:${{ steps.meta.outputs.version }} - ghcr.io/${{ github.repository_owner }}/orion-visor-mysql:${{ steps.meta.outputs.version }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64 - - - name: Build and push orion-visor-redis - uses: docker/build-push-action@v5 - with: - context: . - file: ./docker/redis/Dockerfile - push: true - tags: | - ${{ env.DOCKERHUB_USERNAME }}/orion-visor-redis:${{ steps.meta.outputs.version }} - ghcr.io/${{ github.repository_owner }}/orion-visor-redis:${{ steps.meta.outputs.version }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64 - - - name: Build and push orion-visor-service - uses: docker/build-push-action@v5 - with: - context: . - file: ./docker/service/Dockerfile - push: true - tags: | - ${{ env.DOCKERHUB_USERNAME }}/orion-visor-service:${{ steps.meta.outputs.version }} - ghcr.io/${{ github.repository_owner }}/orion-visor-service:${{ steps.meta.outputs.version }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64 - - - name: Build and push orion-visor-ui - uses: docker/build-push-action@v5 - with: - context: . - file: ./docker/ui/Dockerfile - push: true - tags: | - ${{ env.DOCKERHUB_USERNAME }}/orion-visor-ui:${{ steps.meta.outputs.version }} - ghcr.io/${{ github.repository_owner }}/orion-visor-ui:${{ steps.meta.outputs.version }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64 # Uncomment for multi-platform builds diff --git a/README.md b/README.md index fef29570..8e591ad0 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ **`orion-visor`** 提供一站式自动化运维解决方案。 * **资产管理**:支持对资产进行分组,实现对主机、密钥和身份的统一管理和授权。 -* **在线终端**:提供在线终端 SSH/RDP 等多种协议,支持快捷命令、自定义快捷键和主题风格。 +* **在线终端**:提供在线终端 SSH/RDP/VNC 等多种协议,支持快捷命令、自定义快捷键和主题风格。 * **文件管理**:支持远程主机 SFTP 大文件的批量上传、下载和在线编辑等操作。 * **批量操作**:支持批量执行主机命令、多主机文件分发等功能。 * **计划任务**:支持配置 cron 表达式,定时执行主机命令。 diff --git a/build_docker.sh b/build_docker.sh deleted file mode 100755 index 6411fc32..00000000 --- a/build_docker.sh +++ /dev/null @@ -1,38 +0,0 @@ -#/bin/bash -set -e - -# ./build_docker.sh --push 这样使用会编译完成后自动推送镜像到阿里云仓库 -version=2.4.1 -push_images=false - -# 解析参数 -while [[ $# -gt 0 ]]; do - case "$1" in - --push) - push_images=true - shift - ;; - *) - echo "未知参数: $1" - exit 1 - ;; - esac -done - -docker build -f ./docker/ui/Dockerfile -t orion-visor-ui:${version} -t registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-ui:${version} . && \ -docker build -f ./docker/service/Dockerfile -t orion-visor-service:${version} -t registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-service:${version} . && \ -docker build -f ./docker/mysql/Dockerfile -t orion-visor-mysql:${version} -t registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-mysql:${version} . && \ -docker build -f ./docker/redis/Dockerfile -t orion-visor-redis:${version} -t registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-redis:${version} . && \ -docker build -f ./docker/adminer/Dockerfile -t orion-visor-adminer:${version} -t registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-adminer:${version} . && \ -docker build -f ./docker/guacd/Dockerfile -t orion-visor-guacd:${version} -t registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-guacd:${version} . - - -# 如果需要推送镜像 -if [ "$push_images" = true ]; then - docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-adminer:${version} - docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-mysql:${version} - docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-redis:${version} - docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-guacd:${version} - docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-service:${version} - docker push registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-ui:${version} -fi \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index a1f6fd61..1b731345 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,12 @@ version: '3.3' -# latest = 2.4.1 +# latest = 2.4.2 + +# 支持以下源 +# lijiahangmax/* +# ghcr.io/dromara/* +# registry.cn-hangzhou.aliyuncs.com/orionsec/* + services: ui: image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-ui:latest @@ -27,6 +33,8 @@ services: MYSQL_PASSWORD: ${MYSQL_PASSWORD:-Data@123456} REDIS_HOST: ${REDIS_HOST:-redis} REDIS_PASSWORD: ${REDIS_PASSWORD:-Data@123456} + REDIS_DATABASE: ${REDIS_DATABASE:-0} + REDIS_DATA_VERSION: ${REDIS_DATA_VERSION:-1} GUACD_HOST: ${GUACD_HOST:-guacd} GUACD_PORT: ${GUACD_PORT:-4822} GUACD_DRIVE_PATH: ${GUACD_DRIVE_PATH:-/drive} diff --git a/docker-upgrade.sh b/docker-upgrade.sh index 5d6b09cb..9996bf96 100644 --- a/docker-upgrade.sh +++ b/docker-upgrade.sh @@ -1,16 +1,51 @@ #!/bin/bash -# 停止并移除现有容器 -docker compose down --remove-orphans +# 初始化标志变量 +PULL_IMAGES=false +DEMO_MODE=false -if [ "$1" == "demo" ]; then - # 设置 DEMO_MODE 环境变量为 true +# 解析命令行参数 +for arg in "$@" +do + case $arg in + --pull) + PULL_IMAGES=true + shift + ;; + --demo) + DEMO_MODE=true + shift + ;; + *) + echo "Unknown argument: $arg" + exit 1 + ;; + esac +done + +# 停止并移除现有容器 +echo "Stopping all services..." +docker compose down --remove-orphans +echo "Stopped all services..." + +# 拉取镜像 +if [ "$PULL_IMAGES" = true ]; then + echo "Pulling latest images..." + docker compose pull + echo "Pulled latest images..." +fi + +if [ "$DEMO_MODE" = true ]; then + # 启用 demo 模式 export DEMO_MODE=true echo "Starting services for demo mode..." + # 启动指定的服务 - docker compose up -d --remove-orphans mysql redis ui service adminer + docker compose up -d --remove-orphans mysql redis ui service guacd adminer + echo "Started services for demo mode..." else + # 启动所有服务 echo "Starting all services..." - # 正常启动所有服务 docker compose up -d --remove-orphans + echo "Started all services..." fi diff --git a/docker/builder/Dockerfile.service b/docker/builder/Dockerfile.service new file mode 100644 index 00000000..3ca3d34e --- /dev/null +++ b/docker/builder/Dockerfile.service @@ -0,0 +1,16 @@ +FROM maven:3.9.10-eclipse-temurin-8-alpine AS builder + +# 设置阿里云镜像加速 +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories + +# 拷贝 settings.xml +COPY ./docker/builder/maven-settings.xml /root/.m2/settings.xml + +WORKDIR /build + +COPY . . + +# 复制 POM 文件先进行依赖下载 (利用 Docker 缓存) +RUN mvn dependency:go-offline --settings=/root/.m2/settings.xml +# 构建 +RUN mvn clean package -DskipTests --settings=/root/.m2/settings.xml diff --git a/docker/builder/Dockerfile.ui b/docker/builder/Dockerfile.ui new file mode 100644 index 00000000..cc458006 --- /dev/null +++ b/docker/builder/Dockerfile.ui @@ -0,0 +1,25 @@ +FROM node:18-alpine AS builder + +# 设置阿里云镜像加速 +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories + +# 安装 pnpm +RUN corepack enable && corepack prepare pnpm@latest --activate + +WORKDIR /build + +# 设置 pnpm 使用指定的 registry +ARG REGISTRY_URL=https://registry.npmmirror.com +RUN pnpm config set registry $REGISTRY_URL + +# 复制项目文件 +COPY ./orion-visor-ui/package.json ./orion-visor-ui/pnpm-lock.yaml* ./ + +# 安装依赖 (利用 Docker 缓存层) +RUN pnpm install --frozen-lockfile + +# 复制源代码 +COPY ./orion-visor-ui/ . + +# 构建项目 +RUN pnpm build diff --git a/docker/builder/maven-settings.xml b/docker/builder/maven-settings.xml new file mode 100644 index 00000000..11432b16 --- /dev/null +++ b/docker/builder/maven-settings.xml @@ -0,0 +1,54 @@ + + + + + repos + + + + aliyun + Aliyun Repository + https://maven.aliyun.com/repository/public + + true + + + true + + + + + + central + Maven Central Repository + https://repo.maven.apache.org/maven2 + + true + + + false + + + + + + + aliyun-plugin + https://maven.aliyun.com/repository/public + + + central-plugin + https://repo.maven.apache.org/maven2 + + + + + + + + repos + + + \ No newline at end of file diff --git a/docker/docker-build.sh b/docker/docker-build.sh new file mode 100644 index 00000000..9c6b8e66 --- /dev/null +++ b/docker/docker-build.sh @@ -0,0 +1,207 @@ +#!/bin/bash +set -e + +# DockerContext: orion-visor/docker + +# 加载项目构建 +source ./project-build.sh "$@" + +# 版本号 +version=2.4.2 +# 是否推送镜像 +push_image=false +# 是否构建 latest +latest_image=false +# 是否本地构建 +locally_build=false +# 备份后缀 +backup_suffix=".bak" +# 镜像命名空间 +namespace="registry.cn-hangzhou.aliyuncs.com/orionsec" + +# 解析命令行参数 +while [[ $# -gt 0 ]]; do + case "$1" in + -l|--locally) + locally_build=true + shift + ;; + -latest|--latest-image) + latest_image=true + shift + ;; + -push|--push-image) + push_image=true + shift + ;; + *) + shift + ;; + esac +done + +# 要处理的 Dockerfile 列表及对应的镜像名称 +declare -A images=( + ["./ui/Dockerfile"]="orion-visor-ui" + ["./service/Dockerfile"]="orion-visor-service" + ["./mysql/Dockerfile"]="orion-visor-mysql" + ["./redis/Dockerfile"]="orion-visor-redis" + ["./adminer/Dockerfile"]="orion-visor-adminer" + ["./guacd/Dockerfile"]="orion-visor-guacd" +) + +# 准备 service jar +function prepare_app_jar() { + local source_file="../orion-visor-launch/target/orion-visor-launch.jar" + local target_file="./service/orion-visor-launch.jar" + if [ ! -f "$target_file" ]; then + echo "警告: $target_file 不存在, 正在尝试从 $source_file 复制..." + if [ -f "$source_file" ]; then + cp "$source_file" "$target_file" + echo "已成功复制 $source_file 至 $target_file" + else + echo "错误: $source_file 不存在, 无法继续构建." + exit 1 + fi + else + echo "$target_file 已存在, 无需复制." + fi +} + +# 准备前端 dist 目录 +function prepare_dist_directory() { + local source_dir="../orion-visor-ui/dist" + local target_dir="./ui/dist" + if [ ! -d "$target_dir" ]; then + echo "警告: $target_dir 不存在, 正在尝试从 $source_dir 复制..." + if [ -d "$source_dir" ]; then + cp -r "$source_dir" "$target_dir" + echo "已成功复制 $source_dir 至 $target_dir" + else + echo "错误: $source_dir 不存在, 无法继续构建." + exit 1 + fi + else + echo "$target_dir 已存在, 无需复制." + fi +} + +# 准备 mysql sql 目录 +function prepare_sql_directory() { + local source_dir="../sql" + local target_dir="./mysql/sql" + if [ ! -d "$target_dir" ]; then + echo "警告: $target_dir 不存在, 正在尝试从 $source_dir 复制..." + if [ -d $source_dir ]; then + cp -r $source_dir "$target_dir" + echo "已成功复制 ../sql 至 $target_dir" + else + echo "错误: $source_dir 不存在!根据预期它应该存在, 请确认路径或项目结构是否正确" + exit 1 + fi + else + echo "$target_dir 已存在, 无需复制." + fi +} + +# 修改 Dockerfile 前的备份 +function modify_dockerfiles() { + if [ "$locally_build" = false ]; then + echo "跳过 Dockerfile 修改" + return + fi + echo "正在备份并修改 Dockerfile..." + for file in "${!images[@]}"; do + if [ -f "$file" ]; then + echo "备份并修改: $file" + cp "$file" "$file$backup_suffix" + sed -i 's/--platform=\$BUILDPLATFORM//g' "$file" + else + echo "文件不存在 -> $file" + fi + done +} + +# 恢复原始 Dockerfile +function restore_dockerfiles() { + if [ "$locally_build" = false ]; then + return + fi + echo "开始恢复 Dockerfile" + for file in "${!images[@]}"; do + if [ -f "$file$backup_suffix" ]; then + echo "恢复: $file" + rm -rf "$file" + mv "$file$backup_suffix" "$file" + fi + done + echo "Dockerfile 已恢复为原始版本" +} + +# 构建镜像 +function build_images() { + echo "构建镜像开始..." + for dockerfile in "${!images[@]}"; do + image_name="${images[$dockerfile]}" + echo "Building $image_name with version $version." + # 构建 Docker 镜像 + docker build -f "$dockerfile" -t "${image_name}:${version}" -t "${namespace}/${image_name}:${version}" . + # 添加 latest 标签 + if [ "$latest_image" = true ]; then + echo "Tag $image_name with latest version." + docker tag "${image_name}:${version}" "${image_name}:latest" + docker tag "${namespace}/${image_name}:${version}" "${namespace}/${image_name}:latest" + fi + done + echo "构建镜像结束..." +} + +# 推送镜像 +function push_image_to_registry() { + if [ "$push_image" = true ]; then + echo "推送镜像开始..." + for image_name in "${images[@]}"; do + # 推送版本 + docker push "${namespace}/${image_name}:${version}" + # 推送 latest + if [ "latest_image" = true ]; then + docker push "${namespace}/${image_name}:latest" + fi + done + echo "推送镜像结束..." + fi +} + +# 构建项目-service +if [ "$build_service" = true ]; then + run_build_service +fi + +# 构建项目-ui +if [ "$build_ui" = true ]; then + run_build_ui +fi + +# 检查资源 +echo "正在检查并准备必要的构建资源..." +prepare_app_jar +prepare_dist_directory +prepare_sql_directory +echo "所有前置资源已准备完毕" + +# 修改镜像文件 +modify_dockerfiles + +# 设置异常捕获, 确保失败时恢复 Dockerfile +trap 'restore_dockerfiles; echo "构建失败, 已恢复原始 Dockerfile"; exit 1' ERR INT + +# 构建镜像 +build_images + +# 推送镜像 +push_image_to_registry + +# 恢复原始 Dockerfile +restore_dockerfiles +trap - ERR INT +echo "构建完成" \ No newline at end of file diff --git a/docker/guacd/Dockerfile b/docker/guacd/Dockerfile index b9427b6b..310f7abd 100644 --- a/docker/guacd/Dockerfile +++ b/docker/guacd/Dockerfile @@ -1,10 +1,17 @@ FROM --platform=$BUILDPLATFORM guacamole/guacd:1.6.0 + USER root + # 系统时区 ARG TZ=Asia/Shanghai -# 设置时区 -RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \ - echo '${TZ}' > /etc/timezone + +# 添加包 & 设置时区 +RUN \ + sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \ + apk update && \ + apk add --no-cache tzdata && \ + ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \ + echo "${TZ}" > /etc/timezone # 创建所需目录 RUN mkdir -p /home/guacd/drive /usr/share/guacd/drive diff --git a/docker/mysql/Dockerfile b/docker/mysql/Dockerfile index a8b72c65..79874967 100644 --- a/docker/mysql/Dockerfile +++ b/docker/mysql/Dockerfile @@ -1,16 +1,14 @@ FROM --platform=$BUILDPLATFORM mysql:8.0.28 + # 系统时区 ARG TZ=Asia/Shanghai + # 设置时区 RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \ - echo '${TZ}' > /etc/timezone -# 复制配置 -COPY ./docker/mysql/my.cnf /etc/mysql/conf.d/my.cnf + echo "${TZ}" > /etc/timezone + +# 复制配置文件 +COPY ./mysql/my.cnf /etc/mysql/conf.d/my.cnf + # 复制初始化脚本 -COPY ./sql /tmp -# 设置初始化脚本 -RUN cat /tmp/init-1-schema-databases.sql >> /tmp/init.sql && \ - cat /tmp/init-2-schema-tables.sql >> /tmp/init.sql && \ - cat /tmp/init-3-schema-quartz.sql >> /tmp/init.sql && \ - cat /tmp/init-4-data.sql >> /tmp/init.sql && \ - cp /tmp/init.sql /docker-entrypoint-initdb.d +COPY ./mysql/sql/init-*.sql /docker-entrypoint-initdb.d/ diff --git a/docker/project-build.sh b/docker/project-build.sh new file mode 100644 index 00000000..7a94559f --- /dev/null +++ b/docker/project-build.sh @@ -0,0 +1,125 @@ +#!/bin/bash +set -e + +# DockerContext: orion-visor + +# 版本号 +version=2.4.2 +# 是否构建 service +export build_service=false +# 是否构建 ui +export build_ui=false + +# 解析命令行参数 +for arg in "$@"; do + case "$arg" in + -service|--build-service) + export build_service=true + ;; + -ui|--build-ui) + export build_ui=true + ;; + esac +done + +# 执行构建 service +function run_build_service() { + echo "开始执行 service 构建流程..." + + local builder_dockerfile="./builder/Dockerfile.service" + local builder_image="orion-visor-service-builder" + local builder_container="orion-visor-service-builder-ctn" + local builder_output="/build/orion-visor-launch/target/orion-visor-launch.jar" + local target_dir="../orion-visor-launch/target" + local target_jar="$target_dir/orion-visor-launch.jar" + + # 确保目标目录存在 + if [ ! -d "$target_dir" ]; then + echo "创建目标目录: $target_dir" + mkdir -p "$target_dir" + else + # 如果 jar 已存在, 先删除 + if [ -f "$target_jar" ]; then + echo "删除已有文件: $target_jar" + rm -f "$target_jar" + fi + fi + + # 清理旧容器 + local container_id=$(docker ps -a -f "name=$builder_container" --format "{{.ID}}") + if [ -n "$container_id" ]; then + echo "删除旧容器: $builder_container" + docker rm -f "$container_id" + fi + + # 构建构建镜像 + echo "正在构建 service builder image..." + docker build \ + -f "$builder_dockerfile" \ + -t "$builder_image:$version" ../ + + # 创建一个临时容器用于拷贝文件 + echo "创建临时容器以提取 jar 文件..." + docker create --name "$builder_container" "$builder_image:$version" > /dev/null + + # 拷贝构建好的 jar 文件到目标路径 + echo "正在从容器中拷贝 jar 文件..." + docker cp "$builder_container:$builder_output" "$target_jar" + + # 清理临时容器 + docker rm -f "$builder_container" > /dev/null + echo "后端构建完成, jar 文件已保存至: $target_jar" +} + +# 执行构建 ui +function run_build_ui() { + echo "开始执行 ui 构建流程..." + + local builder_dockerfile="./builder/Dockerfile.ui" + local builder_image="orion-visor-ui-builder" + local builder_container="orion-visor-ui-builder-ctn" + local builder_output="/build/dist" + local target_dir="../orion-visor-ui/dist" + + # 如果 dist 已存在, 先删除 + if [ -d "$target_dir" ]; then + echo "删除已有目录: $target_dir" + rm -rf "$target_dir" + fi + + # 清理旧容器 + local container_id=$(docker ps -a -f "name=$builder_container" --format "{{.ID}}") + if [ -n "$container_id" ]; then + echo "删除旧容器: $builder_container" + docker rm -f "$container_id" + fi + + # 构建前端镜像 + echo "正在构建 ui builder image..." + docker build \ + -f "$builder_dockerfile" \ + -t "$builder_image:$version" ../ + + # 创建临时容器用于拷贝文件 + echo "创建临时容器以提取 dist 文件..." + docker create --name "$builder_container" "$builder_image:$version" > /dev/null + + # 拷贝 dist 目录 + echo "正在从容器中拷贝 dist 文件..." + docker cp "$builder_container:$builder_output" "$target_dir" + + # 清理临时容器 + docker rm "$builder_container" > /dev/null + + echo "前端构建完成, dist 已保存至: $target_dir" +} + +# 构建项目-service +if [ "$build_service" = true ]; then + run_build_service +fi + +# 构建项目-ui +if [ "$build_ui" = true ]; then + run_build_ui +fi diff --git a/docker/redis/Dockerfile b/docker/redis/Dockerfile index ff4bc473..251240a3 100644 --- a/docker/redis/Dockerfile +++ b/docker/redis/Dockerfile @@ -1,15 +1,22 @@ FROM --platform=$BUILDPLATFORM redis:6.0.16-alpine + WORKDIR /data + # 系统时区 ARG TZ=Asia/Shanghai -# 添加包 + +# 添加包 & 设置时区 RUN \ sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \ apk update && \ - apk add tzdata -# 设置时区 -RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \ - echo '${TZ}' > /etc/timezone -# redis 配置 -COPY ./docker/redis/redis.conf /tmp -RUN cat /tmp/redis.conf > /usr/local/redis.conf + apk add --no-cache tzdata && \ + ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \ + echo "${TZ}" > /etc/timezone && \ + rm -rf /var/cache/apk/* && \ + rm -f /usr/local/redis.conf + +# 复制配置文件 +COPY ./redis/redis.conf /usr/local/redis.conf + +# 启动 Redis 并加载自定义配置 +CMD ["redis-server", "/usr/local/redis.conf"] \ No newline at end of file diff --git a/docker/service/Dockerfile b/docker/service/Dockerfile index 2598500f..5bddabf8 100644 --- a/docker/service/Dockerfile +++ b/docker/service/Dockerfile @@ -1,36 +1,24 @@ -# 第一阶段:Maven构建阶段 -FROM --platform=$BUILDPLATFORM maven:3.9.10-eclipse-temurin-8-alpine AS builder - -# 设置阿里云镜像加速 -RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories - -# 复制POM文件先进行依赖下载(利用Docker缓存) -WORKDIR /build -COPY . . -RUN mvn dependency:go-offline - -# 构建 -RUN mvn clean package -DskipTests - FROM --platform=$BUILDPLATFORM openjdk:8-jdk-alpine + USER root + WORKDIR /app + # 系统时区 ARG TZ=Asia/Shanghai -# 添加包 + +# 添加包 & 设置时区 RUN \ sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \ apk update && \ apk add curl && \ apk add udev && \ apk add tzdata && \ - apk add dmidecode -# 设置时区 -RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \ - echo '${TZ}' > /etc/timezone + ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \ + echo "${TZ}" > /etc/timezone -# 从构建阶段复制jar包 -COPY --from=builder /build/orion-visor-launch/target/orion-visor-launch.jar /app/app.jar +# 复制 jar 包 +COPY ./service/orion-visor-launch.jar /app/app.jar # 启动 -CMD ["java", "-jar", "/app/app.jar"] \ No newline at end of file +CMD ["java", "-jar", "/app/app.jar"] diff --git a/docker/ui/Dockerfile b/docker/ui/Dockerfile index 8ddf0845..ce02e252 100644 --- a/docker/ui/Dockerfile +++ b/docker/ui/Dockerfile @@ -1,40 +1,23 @@ -FROM --platform=$BUILDPLATFORM node:18-alpine AS builder - -# 设置阿里云镜像加速 -RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories - -# 安装pnpm -RUN corepack enable && corepack prepare pnpm@latest --activate - -WORKDIR /app - -# 复制项目文件(包括package.json等) -COPY ./orion-visor-ui/package.json ./orion-visor-ui/pnpm-lock.yaml* ./ - -# 安装依赖(利用Docker缓存层) -RUN pnpm install --frozen-lockfile - -# 复制源代码 -COPY ./orion-visor-ui/ . - -# 构建项目 -RUN pnpm build - FROM --platform=$BUILDPLATFORM nginx:alpine + # 系统时区 ARG TZ=Asia/Shanghai -# 添加包 + +# 添加包 & 设置时区 RUN \ sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \ apk update && \ - apk add tzdata -# 设置时区 -RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \ - echo '${TZ}' > /etc/timezone -# 删除原 nginx 配置 -RUN rm -rf /etc/nginx/conf.d/* + apk add --no-cache tzdata && \ + ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \ + echo "${TZ}" > /etc/timezone && \ + rm -rf /var/cache/apk/* && \ + rm -rf /etc/nginx/conf.d/* + # 复制包 -COPY --from=builder /app/dist /usr/share/nginx/html -COPY ./docker/ui/nginx.conf /etc/nginx/conf.d +COPY ./ui/dist /usr/share/nginx/html + +# 复制配置 +COPY ./ui/nginx.conf /etc/nginx/conf.d + # 启动 -CMD ["nginx", "-g", "daemon off;"] +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..279cf091 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,5 @@ +## 文档已迁移至网页端 + +* https://visor.dromara.org +* https://visor.dromara.org.cn +* https://visor.orionsec.cn diff --git a/pull.sh b/git-pull.sh similarity index 82% rename from pull.sh rename to git-pull.sh index 457127c2..89d34433 100644 --- a/pull.sh +++ b/git-pull.sh @@ -1,4 +1,4 @@ -#/bin/bash +#!/bin/bash git clean -df git reset --hard HEAD git pull diff --git a/orion-visor-common/src/main/java/org/dromara/visor/common/constant/AppConst.java b/orion-visor-common/src/main/java/org/dromara/visor/common/constant/AppConst.java index 133ecb40..56fab02a 100644 --- a/orion-visor-common/src/main/java/org/dromara/visor/common/constant/AppConst.java +++ b/orion-visor-common/src/main/java/org/dromara/visor/common/constant/AppConst.java @@ -36,7 +36,7 @@ public interface AppConst extends OrionConst { /** * 同 ${orion.version} 迭代时候需要手动更改 */ - String VERSION = "2.4.1"; + String VERSION = "2.4.2"; /** * 同 ${spring.application.name} diff --git a/orion-visor-common/src/main/java/org/dromara/visor/common/constant/Const.java b/orion-visor-common/src/main/java/org/dromara/visor/common/constant/Const.java index 1fcd64f8..e0b5ba56 100644 --- a/orion-visor-common/src/main/java/org/dromara/visor/common/constant/Const.java +++ b/orion-visor-common/src/main/java/org/dromara/visor/common/constant/Const.java @@ -51,9 +51,6 @@ public interface Const extends cn.orionsec.kit.lang.constant.Const, FieldConst, String SYSTEM_USERNAME = "system"; - // FIXME KIT - String ADMINISTRATOR = "Administrator"; - Long ALL_HOST_ID = -1L; int BATCH_COUNT = 500; diff --git a/orion-visor-common/src/main/java/org/dromara/visor/common/constant/HttpHeaderConst.java b/orion-visor-common/src/main/java/org/dromara/visor/common/constant/CustomHeaderConst.java similarity index 87% rename from orion-visor-common/src/main/java/org/dromara/visor/common/constant/HttpHeaderConst.java rename to orion-visor-common/src/main/java/org/dromara/visor/common/constant/CustomHeaderConst.java index 3e87a5ea..3f1017cc 100644 --- a/orion-visor-common/src/main/java/org/dromara/visor/common/constant/HttpHeaderConst.java +++ b/orion-visor-common/src/main/java/org/dromara/visor/common/constant/CustomHeaderConst.java @@ -22,16 +22,14 @@ */ package org.dromara.visor.common.constant; -import cn.orionsec.kit.lang.constant.StandardHttpHeader; - /** - * http 请求头 + * 自定义请求头 * * @author Jiahang Li * @version 1.0.0 * @since 2025/7/1 1:02 */ -public interface HttpHeaderConst extends StandardHttpHeader { +public interface CustomHeaderConst { String APP_VERSION = "X-App-Version"; diff --git a/orion-visor-common/src/main/java/org/dromara/visor/common/session/config/VncConnectConfig.java b/orion-visor-common/src/main/java/org/dromara/visor/common/session/config/VncConnectConfig.java new file mode 100644 index 00000000..b0d80640 --- /dev/null +++ b/orion-visor-common/src/main/java/org/dromara/visor/common/session/config/VncConnectConfig.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * 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.dromara.visor.common.session.config; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * VNC 连接参数 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2025/4/1 16:57 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +@Schema(name = "VncConnectConfig", description = "VNC 连接参数") +public class VncConnectConfig extends BaseConnectConfig { + + @Schema(description = "低带宽模式") + private Boolean lowBandwidthMode; + + @Schema(description = "交换红蓝") + private Boolean swapRedBlue; + + @Schema(description = "时区") + private String timezone; + + @Schema(description = "剪切板编码") + private String clipboardEncoding; + +} diff --git a/orion-visor-common/src/main/java/org/dromara/visor/common/session/ssh/SessionStores.java b/orion-visor-common/src/main/java/org/dromara/visor/common/session/ssh/SessionStores.java index c0de57e7..e9a715b7 100644 --- a/orion-visor-common/src/main/java/org/dromara/visor/common/session/ssh/SessionStores.java +++ b/orion-visor-common/src/main/java/org/dromara/visor/common/session/ssh/SessionStores.java @@ -110,7 +110,10 @@ public class SessionStores { } } // 超时时间 - session.timeout(config.getTimeout()); + Integer timeout = config.getTimeout(); + if (timeout != null) { + session.timeout(timeout); + } return session; } diff --git a/orion-visor-dependencies/pom.xml b/orion-visor-dependencies/pom.xml index 7dc53199..72642b7d 100644 --- a/orion-visor-dependencies/pom.xml +++ b/orion-visor-dependencies/pom.xml @@ -14,11 +14,11 @@ https://github.com/dromara/orion-visor - 2.4.1 + 2.4.2 2.7.17 2.7.15 1.5.0 - 2.0.1 + 2.0.2 1.9.7 1.18.26 1.6.15 diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-redis/pom.xml b/orion-visor-framework/orion-visor-spring-boot-starter-redis/pom.xml index a33a408a..10fa2ffc 100644 --- a/orion-visor-framework/orion-visor-spring-boot-starter-redis/pom.xml +++ b/orion-visor-framework/orion-visor-spring-boot-starter-redis/pom.xml @@ -37,7 +37,7 @@ netty-all - + com.github.fppt jedis-mock diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/java/org/dromara/visor/framework/redis/configuration/OrionNoRedisAutoConfiguration.java b/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/java/org/dromara/visor/framework/redis/configuration/OrionMockRedisAutoConfiguration.java similarity index 95% rename from orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/java/org/dromara/visor/framework/redis/configuration/OrionNoRedisAutoConfiguration.java rename to orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/java/org/dromara/visor/framework/redis/configuration/OrionMockRedisAutoConfiguration.java index c5d2303b..be3188ec 100644 --- a/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/java/org/dromara/visor/framework/redis/configuration/OrionNoRedisAutoConfiguration.java +++ b/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/java/org/dromara/visor/framework/redis/configuration/OrionMockRedisAutoConfiguration.java @@ -38,17 +38,17 @@ import java.net.InetAddress; import java.util.function.Supplier; /** - * noRedis 配置 + * MockRedis * 仅用于本地调试无 redis 的情况 * * @author Jiahang Li * @version 1.0.0 * @since 2024/12/26 10:02 */ -@ConditionalOnProperty(value = "no.redis", havingValue = "true") +@ConditionalOnProperty(value = "spring.redis.mock", havingValue = "true") @AutoConfiguration @AutoConfigureOrder(AutoConfigureOrderConst.FRAMEWORK_REDIS - 10) -public class OrionNoRedisAutoConfiguration { +public class OrionMockRedisAutoConfiguration { /** * @return mocked redis server diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/java/org/dromara/visor/framework/redis/configuration/OrionRedisAutoConfiguration.java b/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/java/org/dromara/visor/framework/redis/configuration/OrionRedisAutoConfiguration.java index 96652341..55ca9b83 100644 --- a/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/java/org/dromara/visor/framework/redis/configuration/OrionRedisAutoConfiguration.java +++ b/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/java/org/dromara/visor/framework/redis/configuration/OrionRedisAutoConfiguration.java @@ -22,6 +22,7 @@ */ package org.dromara.visor.framework.redis.configuration; +import cn.orionsec.kit.lang.define.cache.key.CacheKeyDefine; import org.dromara.visor.common.constant.AutoConfigureOrderConst; import org.dromara.visor.common.interfaces.Locker; import org.dromara.visor.common.utils.LockerUtils; @@ -31,6 +32,7 @@ import org.dromara.visor.framework.redis.core.utils.RedisUtils; import org.redisson.api.RedissonClient; import org.redisson.config.SingleServerConfig; import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -101,5 +103,15 @@ public class OrionRedisAutoConfiguration { return redisLocker; } + /** + * 设置 redis 数据版本 + * + * @param dataVersion dataVersion + */ + @Value("${spring.redis.data-version}") + public void setDataVersion(String dataVersion) { + CacheKeyDefine.setGlobalPrefix("v" + dataVersion + ":"); + } + } diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 51558866..c4acc2b4 100644 --- a/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -26,9 +26,15 @@ "defaultValue": "16" }, { - "name": "no.redis", + "name": "spring.redis.data-version", + "type": "java.lang.String", + "description": "redis 数据版本.", + "defaultValue": "1" + }, + { + "name": "spring.redis.mock", "type": "java.lang.Boolean", - "description": "是否无 redis.", + "description": "是否使用 mock redis, 一般用于无 redis 调试时使用.", "defaultValue": false } ] diff --git a/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index cccc4c8f..62154a02 100644 --- a/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/orion-visor-framework/orion-visor-spring-boot-starter-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,3 +1,3 @@ -org.dromara.visor.framework.redis.configuration.OrionNoRedisAutoConfiguration +org.dromara.visor.framework.redis.configuration.OrionMockRedisAutoConfiguration org.dromara.visor.framework.redis.configuration.OrionRedisAutoConfiguration org.dromara.visor.framework.redis.configuration.OrionCacheAutoConfiguration \ No newline at end of file diff --git a/orion-visor-launch/src/main/resources/application-dev.yaml b/orion-visor-launch/src/main/resources/application-dev.yaml index 1d3509f1..2c59d903 100644 --- a/orion-visor-launch/src/main/resources/application-dev.yaml +++ b/orion-visor-launch/src/main/resources/application-dev.yaml @@ -13,6 +13,9 @@ spring: host: ${REDIS_HOST:127.0.0.1} port: ${REDIS_PORT:6379} password: ${REDIS_PASSWORD:Data@123456} + database: ${REDIS_DATABASE:10} + data-version: ${REDIS_DATA_VERSION:1} + mock: false redisson: threads: 2 netty-threads: 2 @@ -40,6 +43,3 @@ mybatis-plus: configuration: # 日志打印 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl - -no: - redis: false diff --git a/orion-visor-launch/src/main/resources/application-prod.yaml b/orion-visor-launch/src/main/resources/application-prod.yaml index 244f939c..c86b4f11 100644 --- a/orion-visor-launch/src/main/resources/application-prod.yaml +++ b/orion-visor-launch/src/main/resources/application-prod.yaml @@ -24,6 +24,8 @@ spring: host: ${REDIS_HOST:127.0.0.1} port: ${REDIS_PORT:6379} password: ${REDIS_PASSWORD:Data@123456} + database: ${REDIS_DATABASE:0} + data-version: ${REDIS_DATA_VERSION:1} redisson: threads: 4 netty-threads: 4 diff --git a/orion-visor-launch/src/test/java/org/dromara/visor/launch/ReplaceVersion.java b/orion-visor-launch/src/test/java/org/dromara/visor/launch/ReplaceVersion.java index 56bc5286..d4b056fd 100644 --- a/orion-visor-launch/src/test/java/org/dromara/visor/launch/ReplaceVersion.java +++ b/orion-visor-launch/src/test/java/org/dromara/visor/launch/ReplaceVersion.java @@ -39,20 +39,15 @@ import java.util.function.Function; */ public class ReplaceVersion { - private static final String TARGET_VERSION = "2.4.0"; + private static final String TARGET_VERSION = "2.4.1"; - private static final String REPLACE_VERSION = "2.4.1"; + private static final String REPLACE_VERSION = "2.4.2"; private static final String PATH = new File("").getAbsolutePath(); private static final String[] DOCKER_FILES = new String[]{ - "docker/push.sh", - "docker/adminer/build.sh", - "docker/mysql/build.sh", - "docker/redis/build.sh", - "docker/guacd/build.sh", - "docker/service/build.sh", - "docker/ui/build.sh", + "docker/docker-build.sh", + "docker/project-build.sh", "docker-compose.yml", "docker-compose-testing.yml" }; diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/api/HostConnectApi.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/api/HostConnectApi.java index 02764b9a..c94db8b4 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/api/HostConnectApi.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/api/HostConnectApi.java @@ -24,6 +24,7 @@ package org.dromara.visor.module.asset.api; import org.dromara.visor.common.session.config.RdpConnectConfig; import org.dromara.visor.common.session.config.SshConnectConfig; +import org.dromara.visor.common.session.config.VncConnectConfig; import org.dromara.visor.module.asset.entity.dto.host.HostDTO; /** @@ -87,4 +88,30 @@ public interface HostConnectApi { */ RdpConnectConfig getRdpConnectConfig(HostDTO host, Long userId); + /** + * 获取 VNC 连接配置 + * + * @param hostId hostId + * @return session + */ + VncConnectConfig getVncConnectConfig(Long hostId); + + /** + * 使用用户配置获取 VNC 连接配置 + * + * @param hostId hostId + * @param userId userId + * @return session + */ + VncConnectConfig getVncConnectConfig(Long hostId, Long userId); + + /** + * 使用用户配置获取 VNC 连接配置 + * + * @param host host + * @param userId userId + * @return session + */ + VncConnectConfig getVncConnectConfig(HostDTO host, Long userId); + } diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/entity/dto/host/HostSshConfigDTO.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/entity/dto/host/HostSshConfigDTO.java index 4717c811..691f400d 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/entity/dto/host/HostSshConfigDTO.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/entity/dto/host/HostSshConfigDTO.java @@ -71,7 +71,7 @@ public class HostSshConfigDTO implements GenericsDataModel, UpdatePasswordAction private Long keyId; @NotNull - @Min(value = 1) + @Min(value = 0) @Max(value = 100000) @Schema(description = "连接超时时间") private Integer connectTimeout; diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/entity/dto/host/HostVncConfigDTO.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/entity/dto/host/HostVncConfigDTO.java new file mode 100644 index 00000000..3f3c4be5 --- /dev/null +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/entity/dto/host/HostVncConfigDTO.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * 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.dromara.visor.module.asset.entity.dto.host; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.dromara.visor.common.handler.data.model.GenericsDataModel; +import org.dromara.visor.common.security.UpdatePasswordAction; + +import javax.validation.constraints.*; + +/** + * 主机 VNC 配置 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/9/13 16:18 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(name = "HostVncConfigDTO", description = "主机 VNC 配置业务对象") +public class HostVncConfigDTO implements GenericsDataModel, UpdatePasswordAction { + + @NotNull + @Min(value = 1) + @Max(value = 65535) + @Schema(description = "主机端口") + private Integer port; + + @Size(max = 128) + @Schema(description = "用户名") + private String username; + + @NotBlank + @Size(max = 12) + @Schema(description = "认证方式") + private String authType; + + @Schema(description = "密码") + private String password; + + @Schema(description = "身份id") + private Long identityId; + + @Schema(description = "无用户名") + private Boolean noUsername; + + @Schema(description = "无密码") + private Boolean noPassword; + + @Schema(description = "时区") + private String timezone; + + @Schema(description = "剪切板编码") + private String clipboardEncoding; + + @Schema(description = "是否使用新密码 仅参数") + private Boolean useNewPassword; + + @Schema(description = "是否已设置密码 仅返回") + private Boolean hasPassword; + +} diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/enums/HostTypeEnum.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/enums/HostTypeEnum.java index 206ba242..e4bf6760 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/enums/HostTypeEnum.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-provider/src/main/java/org/dromara/visor/module/asset/enums/HostTypeEnum.java @@ -27,6 +27,7 @@ import lombok.AllArgsConstructor; import org.dromara.visor.common.constant.Const; import org.dromara.visor.module.asset.entity.dto.host.HostRdpConfigDTO; import org.dromara.visor.module.asset.entity.dto.host.HostSshConfigDTO; +import org.dromara.visor.module.asset.entity.dto.host.HostVncConfigDTO; import java.util.ArrayList; import java.util.Arrays; @@ -54,6 +55,11 @@ public enum HostTypeEnum { */ RDP(HostRdpConfigDTO.class), + /** + * VNC + */ + VNC(HostVncConfigDTO.class), + ; private final Class clazz; diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/api/impl/HostConnectApiImpl.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/api/impl/HostConnectApiImpl.java index 6ffcc687..4f13662f 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/api/impl/HostConnectApiImpl.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/api/impl/HostConnectApiImpl.java @@ -25,6 +25,7 @@ package org.dromara.visor.module.asset.api.impl; import lombok.extern.slf4j.Slf4j; import org.dromara.visor.common.session.config.RdpConnectConfig; import org.dromara.visor.common.session.config.SshConnectConfig; +import org.dromara.visor.common.session.config.VncConnectConfig; import org.dromara.visor.module.asset.api.HostConnectApi; import org.dromara.visor.module.asset.convert.HostProviderConvert; import org.dromara.visor.module.asset.entity.dto.host.HostDTO; @@ -77,4 +78,19 @@ public class HostConnectApiImpl implements HostConnectApi { return hostConnectService.getRdpConnectConfig(HostProviderConvert.MAPPER.to(host), userId); } + @Override + public VncConnectConfig getVncConnectConfig(Long hostId) { + return hostConnectService.getVncConnectConfig(hostId); + } + + @Override + public VncConnectConfig getVncConnectConfig(Long hostId, Long userId) { + return hostConnectService.getVncConnectConfig(hostId, userId); + } + + @Override + public VncConnectConfig getVncConnectConfig(HostDTO host, Long userId) { + return hostConnectService.getVncConnectConfig(HostProviderConvert.MAPPER.to(host), userId); + } + } diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/config/HostConfigStrategyEnum.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/config/HostConfigStrategyEnum.java index 17018a08..e479fe2b 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/config/HostConfigStrategyEnum.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/config/HostConfigStrategyEnum.java @@ -49,6 +49,11 @@ public enum HostConfigStrategyEnum implements GenericsStrategyDefinition { */ RDP(HostRdpConfigStrategy.class), + /** + * VNC + */ + VNC(HostVncConfigStrategy.class), + ; private final Class> strategyClass; diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/config/HostVncConfigStrategy.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/config/HostVncConfigStrategy.java new file mode 100644 index 00000000..290e64f0 --- /dev/null +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/config/HostVncConfigStrategy.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * 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.dromara.visor.module.asset.handler.host.config; + +import cn.orionsec.kit.lang.utils.Booleans; +import cn.orionsec.kit.lang.utils.Strings; +import org.dromara.visor.common.constant.Const; +import org.dromara.visor.common.constant.ErrorMessage; +import org.dromara.visor.common.utils.Valid; +import org.dromara.visor.module.asset.dao.HostIdentityDAO; +import org.dromara.visor.module.asset.entity.domain.HostIdentityDO; +import org.dromara.visor.module.asset.entity.dto.host.HostVncConfigDTO; +import org.dromara.visor.module.asset.enums.HostAuthTypeEnum; +import org.dromara.visor.module.asset.enums.HostIdentityTypeEnum; +import org.dromara.visor.module.asset.enums.HostTypeEnum; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * 主机 VNC 配置策略 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/9/19 14:26 + */ +@Component +public class HostVncConfigStrategy extends AbstractHostConfigStrategy { + + @Resource + private HostIdentityDAO hostIdentityDAO; + + public HostVncConfigStrategy() { + super(HostVncConfigDTO.class); + } + + @Override + public HostVncConfigDTO getDefault() { + return HostVncConfigDTO.builder() + .port(5900) + .username(Const.ROOT) + .authType(HostAuthTypeEnum.PASSWORD.name()) + .noUsername(false) + .noPassword(false) + .clipboardEncoding(Const.UTF_8) + .build(); + } + + @Override + protected void preValid(HostVncConfigDTO model) { + // 验证剪切板编码格式 + String clipboardEncoding = model.getClipboardEncoding(); + if (!Strings.isBlank(clipboardEncoding)) { + this.validCharset(clipboardEncoding); + } + // 检查主机身份是否存在 + Long identityId = model.getIdentityId(); + if (identityId != null) { + HostIdentityDO identity = Valid.notNull(hostIdentityDAO.selectById(identityId), ErrorMessage.IDENTITY_ABSENT); + Valid.eq(HostIdentityTypeEnum.PASSWORD.name(), identity.getType(), ErrorMessage.CHECK_IDENTITY_PASSWORD); + } + } + + @Override + protected void valid(HostVncConfigDTO model) { + // 验证填充后的参数 + Valid.valid(model); + } + + @Override + protected void updateFill(HostVncConfigDTO beforeModel, HostVncConfigDTO afterModel) { + // 无密码设置认证方式为密码验证 + if (Booleans.isTrue(afterModel.getNoPassword())) { + afterModel.setAuthType(HostAuthTypeEnum.PASSWORD.name()); + } + // 加密密码 + this.checkEncryptPassword(afterModel.getAuthType(), beforeModel, afterModel); + afterModel.setHasPassword(null); + afterModel.setUseNewPassword(null); + } + + @Override + public HostVncConfigDTO parse(String serialModel) { + return HostTypeEnum.VNC.parse(serialModel); + } + + @Override + public void toView(HostVncConfigDTO model) { + if (model == null) { + return; + } + model.setHasPassword(Strings.isNotBlank(model.getPassword())); + model.setPassword(null); + } + +} diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/HostExtraItemEnum.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/HostExtraItemEnum.java index 2f8b6db9..dd855591 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/HostExtraItemEnum.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/HostExtraItemEnum.java @@ -27,10 +27,7 @@ import lombok.Getter; import org.dromara.visor.common.handler.data.GenericsStrategyDefinition; import org.dromara.visor.common.handler.data.model.GenericsDataModel; import org.dromara.visor.common.handler.data.strategy.GenericsDataStrategy; -import org.dromara.visor.module.asset.handler.host.extra.strategy.HostLabelExtraStrategy; -import org.dromara.visor.module.asset.handler.host.extra.strategy.HostRdpExtraStrategy; -import org.dromara.visor.module.asset.handler.host.extra.strategy.HostSpecExtraStrategy; -import org.dromara.visor.module.asset.handler.host.extra.strategy.HostSshExtraStrategy; +import org.dromara.visor.module.asset.handler.host.extra.strategy.*; /** * 主机额外配置项策略枚举 @@ -58,6 +55,11 @@ public enum HostExtraItemEnum implements GenericsStrategyDefinition { */ RDP(HostRdpExtraStrategy.class, true), + /** + * VNC 额外配置 + */ + VNC(HostVncExtraStrategy.class, true), + /** * 规格信息配置 */ diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/model/HostVncExtraModel.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/model/HostVncExtraModel.java new file mode 100644 index 00000000..5db0346b --- /dev/null +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/model/HostVncExtraModel.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * 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.dromara.visor.module.asset.handler.host.extra.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.dromara.visor.common.handler.data.model.GenericsDataModel; + +/** + * 主机拓展信息 - vnc 模型 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/12/20 21:36 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HostVncExtraModel implements GenericsDataModel { + + /** + * 端口号 + */ + private Integer port; + + /** + * 低带宽模式 + */ + private Boolean lowBandwidthMode; + + /** + * 交换红蓝 + */ + private Boolean swapRedBlue; + +} diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/strategy/HostRdpExtraStrategy.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/strategy/HostRdpExtraStrategy.java index fbda5570..709270a2 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/strategy/HostRdpExtraStrategy.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/handler/host/extra/strategy/HostRdpExtraStrategy.java @@ -59,6 +59,7 @@ public class HostRdpExtraStrategy extends AbstractGenericsDataStrategy { + + public HostVncExtraStrategy() { + super(HostVncExtraModel.class); + } + + @Override + public HostVncExtraModel getDefault() { + return HostVncExtraModel.builder() + .lowBandwidthMode(false) + .swapRedBlue(false) + .build(); + } + +} diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/HostConnectService.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/HostConnectService.java index fee71a48..d9e93c85 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/HostConnectService.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/HostConnectService.java @@ -24,6 +24,7 @@ package org.dromara.visor.module.asset.service; import org.dromara.visor.common.session.config.RdpConnectConfig; import org.dromara.visor.common.session.config.SshConnectConfig; +import org.dromara.visor.common.session.config.VncConnectConfig; import org.dromara.visor.module.asset.entity.domain.HostDO; import org.dromara.visor.module.asset.entity.request.host.HostTestConnectRequest; @@ -95,4 +96,30 @@ public interface HostConnectService { */ RdpConnectConfig getRdpConnectConfig(HostDO host, Long userId); + /** + * 获取 VNC 连接配置 + * + * @param hostId hostId + * @return session + */ + VncConnectConfig getVncConnectConfig(Long hostId); + + /** + * 使用用户配置获取 VNC 连接配置 + * + * @param hostId hostId + * @param userId userId + * @return session + */ + VncConnectConfig getVncConnectConfig(Long hostId, Long userId); + + /** + * 使用用户配置获取 VNC 连接配置 + * + * @param host host + * @param userId userId + * @return session + */ + VncConnectConfig getVncConnectConfig(HostDO host, Long userId); + } diff --git a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostConnectServiceImpl.java b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostConnectServiceImpl.java index 93240932..647a5451 100644 --- a/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostConnectServiceImpl.java +++ b/orion-visor-modules/orion-visor-module-asset/orion-visor-module-asset-service/src/main/java/org/dromara/visor/module/asset/service/impl/HostConnectServiceImpl.java @@ -22,6 +22,7 @@ */ package org.dromara.visor.module.asset.service.impl; +import cn.orionsec.kit.lang.utils.Booleans; import cn.orionsec.kit.lang.utils.Exceptions; import cn.orionsec.kit.lang.utils.io.Streams; import cn.orionsec.kit.net.host.SessionStore; @@ -30,6 +31,7 @@ import org.dromara.visor.common.constant.ErrorMessage; import org.dromara.visor.common.session.config.BaseConnectConfig; import org.dromara.visor.common.session.config.RdpConnectConfig; import org.dromara.visor.common.session.config.SshConnectConfig; +import org.dromara.visor.common.session.config.VncConnectConfig; import org.dromara.visor.common.session.ssh.SessionStores; import org.dromara.visor.common.utils.Valid; import org.dromara.visor.module.asset.dao.HostDAO; @@ -40,6 +42,7 @@ import org.dromara.visor.module.asset.entity.domain.HostIdentityDO; import org.dromara.visor.module.asset.entity.domain.HostKeyDO; import org.dromara.visor.module.asset.entity.dto.host.HostRdpConfigDTO; import org.dromara.visor.module.asset.entity.dto.host.HostSshConfigDTO; +import org.dromara.visor.module.asset.entity.dto.host.HostVncConfigDTO; import org.dromara.visor.module.asset.entity.request.host.HostTestConnectRequest; import org.dromara.visor.module.asset.enums.HostAuthTypeEnum; import org.dromara.visor.module.asset.enums.HostExtraAuthTypeEnum; @@ -48,6 +51,7 @@ import org.dromara.visor.module.asset.enums.HostTypeEnum; import org.dromara.visor.module.asset.handler.host.extra.HostExtraItemEnum; import org.dromara.visor.module.asset.handler.host.extra.model.HostRdpExtraModel; import org.dromara.visor.module.asset.handler.host.extra.model.HostSshExtraModel; +import org.dromara.visor.module.asset.handler.host.extra.model.HostVncExtraModel; import org.dromara.visor.module.asset.service.AssetAuthorizedDataService; import org.dromara.visor.module.asset.service.HostConfigService; import org.dromara.visor.module.asset.service.HostConnectService; @@ -108,7 +112,6 @@ public class HostConnectServiceImpl implements HostConnectService { Streams.close(sessionStore); } } - // TODO: 其他连接方式 } @Override @@ -189,6 +192,41 @@ public class HostConnectServiceImpl implements HostConnectService { return this.getRdpConnectConfig(host, config, extra); } + @Override + public VncConnectConfig getVncConnectConfig(Long hostId) { + log.info("HostConnectService.getVncConnectConfig-withHost hostId: {}", hostId); + // 查询主机 + HostDO host = hostDAO.selectById(hostId); + // 查询主机配置 + HostVncConfigDTO config = hostConfigService.getHostConfig(hostId, HostTypeEnum.VNC.name()); + // 获取配置 + return this.getVncConnectConfig(host, config, null); + } + + @Override + public VncConnectConfig getVncConnectConfig(Long hostId, Long userId) { + // 查询主机 + HostDO host = hostDAO.selectById(hostId); + Valid.notNull(host, ErrorMessage.HOST_ABSENT); + // 获取配置 + return this.getVncConnectConfig(host, userId); + } + + @Override + public VncConnectConfig getVncConnectConfig(HostDO host, Long userId) { + Long hostId = host.getId(); + log.info("HostConnectService.getVncConnectConfig hostId: {}, userId: {}", hostId, userId); + // 验证权限 + this.validHostAuthorized(userId, hostId); + // 获取主机配置 + HostVncConfigDTO config = hostConfigService.getHostConfig(hostId, HostTypeEnum.VNC.name()); + Valid.notNull(config, ErrorMessage.CONFIG_ABSENT); + // 查询主机额外配置 + HostVncExtraModel extra = hostExtraService.getHostExtra(userId, hostId, HostExtraItemEnum.VNC); + // 获取连接配置 + return this.getVncConnectConfig(host, config, extra); + } + /** * 获取主机 SSH 连接配置 * @@ -254,14 +292,7 @@ public class HostConnectServiceImpl implements HostConnectService { connectConfig.setPassword(config.getPassword()); } else if (HostAuthTypeEnum.KEY.equals(authType)) { // 密钥认证 - Long keyId = config.getKeyId(); - Valid.notNull(keyId, ErrorMessage.KEY_ABSENT); - HostKeyDO key = hostKeyDAO.selectById(keyId); - Valid.notNull(key, ErrorMessage.KEY_ABSENT); - connectConfig.setKeyId(keyId); - connectConfig.setPublicKey(key.getPublicKey()); - connectConfig.setPrivateKey(key.getPrivateKey()); - connectConfig.setPrivateKeyPassword(key.getPassword()); + this.setSshKey(config.getKeyId(), connectConfig); } return connectConfig; } @@ -279,6 +310,8 @@ public class HostConnectServiceImpl implements HostConnectService { // 填充认证信息 RdpConnectConfig connectConfig = RdpConnectConfig.builder() .hostPort(config.getPort()) + .username(config.getUsername()) + .password(config.getPassword()) .versionGt81(config.getVersionGt81()) .timezone(config.getTimezone()) .keyboardLayout(config.getKeyboardLayout()) @@ -304,25 +337,54 @@ public class HostConnectServiceImpl implements HostConnectService { config.setIdentityId(extra.getIdentityId()); } } - - // 身份认证 - HostAuthTypeEnum authType = HostAuthTypeEnum.of(config.getAuthType()); - if (HostAuthTypeEnum.IDENTITY.equals(authType)) { - // 身份认证 - 仅密码 - authType = HostAuthTypeEnum.PASSWORD; - Valid.notNull(config.getIdentityId(), ErrorMessage.IDENTITY_ABSENT); - HostIdentityDO identity = hostIdentityDAO.selectById(config.getIdentityId()); - Valid.notNull(identity, ErrorMessage.IDENTITY_ABSENT); - // 设置身份信息 - config.setUsername(identity.getUsername()); - config.setPassword(identity.getPassword()); + // 填充身份认证信息 + if (HostAuthTypeEnum.IDENTITY.name().equals(config.getAuthType())) { + this.setIdentityPasswordAuthorization(config.getIdentityId(), connectConfig); } + return connectConfig; + } + /** + * 获取 VNC 连接信息 + * + * @param host host + * @param config config + * @return info + */ + private VncConnectConfig getVncConnectConfig(HostDO host, + HostVncConfigDTO config, + HostVncExtraModel extra) { // 填充认证信息 - connectConfig.setUsername(config.getUsername()); - if (HostAuthTypeEnum.PASSWORD.equals(authType)) { - // 密码认证 - connectConfig.setPassword(config.getPassword()); + VncConnectConfig connectConfig = VncConnectConfig.builder() + .hostPort(config.getPort()) + .username(config.getUsername()) + .password(config.getPassword()) + .timezone(config.getTimezone()) + .clipboardEncoding(config.getClipboardEncoding()) + .build(); + // 填充基础主机信息 + this.setBaseConnectConfig(connectConfig, host); + if (extra != null) { + // 设置额外配置信息 + connectConfig.setLowBandwidthMode(extra.getLowBandwidthMode()); + connectConfig.setSwapRedBlue(extra.getSwapRedBlue()); + // 设置自定义端口 + Integer extraPort = extra.getPort(); + if (extraPort != null) { + connectConfig.setHostPort(extraPort); + } + } + // 填充身份认证信息 + if (HostAuthTypeEnum.IDENTITY.name().equals(config.getAuthType())) { + this.setIdentityPasswordAuthorization(config.getIdentityId(), connectConfig); + } + // 无用户名 + if (Booleans.isTrue(config.getNoUsername())) { + connectConfig.setUsername(null); + } + // 无密码 + if (Booleans.isTrue(config.getNoPassword())) { + connectConfig.setPassword(null); } return connectConfig; } @@ -366,6 +428,41 @@ public class HostConnectServiceImpl implements HostConnectService { } } + /** + * 设置密码认证 + * + * @param identityId identityId + * @param connectConfig connectConfig + */ + private void setIdentityPasswordAuthorization(Long identityId, BaseConnectConfig connectConfig) { + if (identityId == null) { + return; + } + // 查询身份信息 + HostIdentityDO identity = hostIdentityDAO.selectById(identityId); + Valid.notNull(identity, ErrorMessage.IDENTITY_ABSENT); + // 设置身份信息 + connectConfig.setUsername(identity.getUsername()); + connectConfig.setPassword(identity.getPassword()); + } + + /** + * 设置 SSH 密钥信息 + * + * @param keyId keyId + * @param connectConfig connectConfig + */ + private void setSshKey(Long keyId, SshConnectConfig connectConfig) { + Valid.notNull(keyId, ErrorMessage.KEY_ABSENT); + // 查询密钥信息 + HostKeyDO key = hostKeyDAO.selectById(keyId); + Valid.notNull(key, ErrorMessage.KEY_ABSENT); + connectConfig.setKeyId(keyId); + connectConfig.setPublicKey(key.getPublicKey()); + connectConfig.setPrivateKey(key.getPrivateKey()); + connectConfig.setPrivateKeyPassword(key.getPassword()); + } + /** * 设置基础主机信息 * diff --git a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/controller/UserAggregateController.java b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/controller/UserAggregateController.java index c43678d2..71dc5b99 100644 --- a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/controller/UserAggregateController.java +++ b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/controller/UserAggregateController.java @@ -22,11 +22,12 @@ */ package org.dromara.visor.module.infra.controller; +import cn.orionsec.kit.web.servlet.web.Servlets; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import org.dromara.visor.common.constant.AppConst; -import org.dromara.visor.common.constant.HttpHeaderConst; +import org.dromara.visor.common.constant.CustomHeaderConst; import org.dromara.visor.framework.log.core.annotation.IgnoreLog; import org.dromara.visor.framework.log.core.enums.IgnoreLogMode; import org.dromara.visor.framework.web.core.annotation.RestWrapper; @@ -71,10 +72,8 @@ public class UserAggregateController { @GetMapping("/user") @Operation(summary = "获取用户权限聚合信息") public UserAggregateVO getUserAggregateInfo(HttpServletResponse response) { - // FIXME KIT // 设置版本号请求头 - response.setHeader(HttpHeaderConst.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaderConst.APP_VERSION); - response.setHeader(HttpHeaderConst.APP_VERSION, AppConst.VERSION); + Servlets.addCustomHeader(response, CustomHeaderConst.APP_VERSION, AppConst.VERSION); // 获取用户信息 return userAggregateService.getUserAggregateInfo(); } diff --git a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/define/cache/PreferenceCacheKeyDefine.java b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/define/cache/PreferenceCacheKeyDefine.java index 2075a57d..5f6a1d89 100644 --- a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/define/cache/PreferenceCacheKeyDefine.java +++ b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/define/cache/PreferenceCacheKeyDefine.java @@ -39,7 +39,7 @@ import java.util.concurrent.TimeUnit; public interface PreferenceCacheKeyDefine { CacheKeyDefine PREFERENCE = new CacheKeyBuilder() - .key("v1:user:prefer:{}:{}") + .key("user:prefer:{}:{}") .desc("用户偏好 ${userId} ${type}") .type(JSONObject.class) .struct(RedisCacheStruct.STRING) diff --git a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/handler/preference/model/TerminalPreferenceModel.java b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/handler/preference/model/TerminalPreferenceModel.java index b57614ca..0b4c7104 100644 --- a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/handler/preference/model/TerminalPreferenceModel.java +++ b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/handler/preference/model/TerminalPreferenceModel.java @@ -95,6 +95,16 @@ public class TerminalPreferenceModel implements GenericsDataModel { */ private JSONObject rdpActionBarSetting; + /** + * vnc 图形化设置 + */ + private JSONObject vncGraphSetting; + + /** + * vnc 操作栏设置 + */ + private JSONObject vncActionBarSetting; + /** * 快捷键设置 */ @@ -297,6 +307,11 @@ public class TerminalPreferenceModel implements GenericsDataModel { */ private Integer scrollBackLine; + /** + * 替换退格符 + */ + private Boolean replaceBackspace; + } @Data @@ -421,6 +436,11 @@ public class TerminalPreferenceModel implements GenericsDataModel { */ private String position; + /** + * 会话信息 + */ + private Boolean info; + /** * 显示设置 */ @@ -431,18 +451,33 @@ public class TerminalPreferenceModel implements GenericsDataModel { */ private Boolean combinationKey; + /** + * 长按键 + */ + private Boolean triggerKey; + /** * 剪切板 */ private Boolean clipboard; /** - * 上传 + * RDP 上传 */ - private Boolean upload; + private Boolean rdpUpload; /** - * 保存为 rdp 文件 + * SFTP 上传 + */ + private Boolean sftpUpload; + + /** + * 打开 SFTP + */ + private Boolean openSftp; + + /** + * 保存为 RDP 文件 */ private Boolean saveRdp; @@ -451,6 +486,11 @@ public class TerminalPreferenceModel implements GenericsDataModel { */ private Boolean disconnect; + /** + * 重新连接 + */ + private Boolean reconnect; + /** * 关闭 */ @@ -481,6 +521,118 @@ public class TerminalPreferenceModel implements GenericsDataModel { } + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class VncGraphSettingModel implements IJsonObject { + + /** + * 显示大小 + */ + private String displaySize; + + /** + * 显示宽度 + */ + private Integer displayWidth; + + /** + * 显示高度 + */ + private Integer displayHeight; + + /** + * 颜色深度 + */ + private Integer colorDepth; + + /** + * 无损压缩 + */ + private Boolean forceLossless; + + /** + * 光标 + */ + private String cursor; + + /** + * 质量等级 + */ + private Integer compressLevel; + + /** + * 压缩等级 + */ + private Integer qualityLevel; + + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class VncActionBarSettingModel implements IJsonObject { + + /** + * 位置 + */ + private String position; + + /** + * 会话信息 + */ + private Boolean info; + + /** + * 显示设置 + */ + private Boolean display; + + /** + * 组合键 + */ + private Boolean combinationKey; + + /** + * 长按键 + */ + private Boolean triggerKey; + + /** + * 剪切板 + */ + private Boolean clipboard; + + /** + * SFTP 上传 + */ + private Boolean sftpUpload; + + /** + * 打开 SFTP + */ + private Boolean openSftp; + + /** + * 断开连接 + */ + private Boolean disconnect; + + /** + * 重新连接 + */ + private Boolean reconnect; + + + /** + * 关闭 + */ + private Boolean close; + + } + @Data @Builder @NoArgsConstructor diff --git a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/handler/preference/strategy/TerminalPreferenceStrategy.java b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/handler/preference/strategy/TerminalPreferenceStrategy.java index 68458b26..d09856f3 100644 --- a/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/handler/preference/strategy/TerminalPreferenceStrategy.java +++ b/orion-visor-modules/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/handler/preference/strategy/TerminalPreferenceStrategy.java @@ -68,6 +68,10 @@ public class TerminalPreferenceStrategy extends AbstractGenericsDataStrategy~!@#$%^&*|+=[]{}~?│") .terminalEmulationType(TerminalType.XTERM.getType()) .scrollBackLine(1000) + .replaceBackspace(false) .build() .toJsonString(); } @@ -201,12 +206,17 @@ public class TerminalPreferenceStrategy extends AbstractGenericsDataStrategy JSON_CONTAINS(value, JSON_OBJECT('identityId', #{item})) diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/configuration/TerminalWebSocketConfiguration.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/configuration/TerminalWebSocketConfiguration.java index 95120ff5..ebc01ec6 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/configuration/TerminalWebSocketConfiguration.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/configuration/TerminalWebSocketConfiguration.java @@ -25,6 +25,7 @@ package org.dromara.visor.module.terminal.configuration; import org.dromara.visor.module.terminal.handler.terminal.TerminalAccessRdpHandler; import org.dromara.visor.module.terminal.handler.terminal.TerminalAccessSftpHandler; import org.dromara.visor.module.terminal.handler.terminal.TerminalAccessSshHandler; +import org.dromara.visor.module.terminal.handler.terminal.TerminalAccessVncHandler; import org.dromara.visor.module.terminal.handler.transfer.TransferMessageDispatcher; import org.dromara.visor.module.terminal.interceptor.TerminalAccessInterceptor; import org.dromara.visor.module.terminal.interceptor.TerminalTransferInterceptor; @@ -63,6 +64,9 @@ public class TerminalWebSocketConfiguration implements WebSocketConfigurer { @Resource private TerminalAccessRdpHandler terminalAccessRdpHandler; + @Resource + private TerminalAccessVncHandler terminalAccessVncHandler; + @Resource private TransferMessageDispatcher transferMessageDispatcher; @@ -80,6 +84,10 @@ public class TerminalWebSocketConfiguration implements WebSocketConfigurer { registry.addHandler(terminalAccessRdpHandler, prefix + "/terminal/access/rdp/{accessToken}") .addInterceptors(terminalAccessInterceptor) .setAllowedOrigins("*"); + // VNC 终端会话 + registry.addHandler(terminalAccessVncHandler, prefix + "/terminal/access/vnc/{accessToken}") + .addInterceptors(terminalAccessInterceptor) + .setAllowedOrigins("*"); // 文件传输 registry.addHandler(transferMessageDispatcher, prefix + "/terminal/transfer/{transferToken}") .addInterceptors(terminalTransferInterceptor) diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/convert/TerminalSessionConvert.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/convert/TerminalSessionConvert.java index e9738a42..cd601995 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/convert/TerminalSessionConvert.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/convert/TerminalSessionConvert.java @@ -24,9 +24,11 @@ package org.dromara.visor.module.terminal.convert; import org.dromara.visor.common.session.config.RdpConnectConfig; import org.dromara.visor.common.session.config.SshConnectConfig; +import org.dromara.visor.common.session.config.VncConnectConfig; import org.dromara.visor.module.terminal.handler.terminal.model.config.TerminalSessionRdpConfig; import org.dromara.visor.module.terminal.handler.terminal.model.config.TerminalSessionSftpConfig; import org.dromara.visor.module.terminal.handler.terminal.model.config.TerminalSessionSshConfig; +import org.dromara.visor.module.terminal.handler.terminal.model.config.TerminalSessionVncConfig; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; @@ -48,4 +50,6 @@ public interface TerminalSessionConvert { TerminalSessionRdpConfig toRdp(RdpConnectConfig domain); + TerminalSessionVncConfig toVnc(VncConnectConfig domain); + } diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/enums/TerminalConnectTypeEnum.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/enums/TerminalConnectTypeEnum.java index cb57c60c..b6b58989 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/enums/TerminalConnectTypeEnum.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/enums/TerminalConnectTypeEnum.java @@ -49,7 +49,7 @@ public enum TerminalConnectTypeEnum { /** * vnc */ - // VNC, + VNC, ; diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/guacd/GuacdTunnel.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/guacd/GuacdTunnel.java index 2c08d300..0821bad4 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/guacd/GuacdTunnel.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/guacd/GuacdTunnel.java @@ -89,7 +89,6 @@ public class GuacdTunnel implements IGuacdTunnel { @Override public void connect() throws GuacdException { try { - // TODO 端口转发 this.socket = new ConfiguredGuacamoleSocket(new InetGuacamoleSocket(serverAddress, serverPort), serverConfig, clientConfig); this.tunnel = new CustomGuacamoleTunnel(uuid, socket); } catch (GuacamoleException e) { @@ -192,12 +191,16 @@ public class GuacdTunnel implements IGuacdTunnel { } @Override - public void size(int width, int height, int dpi) { + public void size(int width, int height) { clientConfig.setOptimalScreenWidth(width); clientConfig.setOptimalScreenHeight(height); - clientConfig.setOptimalResolution(dpi); this.setParameter(GuacdConst.WIDTH, width); this.setParameter(GuacdConst.HEIGHT, height); + } + + @Override + public void dpi(int dpi) { + clientConfig.setOptimalResolution(dpi); this.setParameter(GuacdConst.DPI, dpi); } diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/guacd/IGuacdTunnel.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/guacd/IGuacdTunnel.java index be72804e..8eff3c20 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/guacd/IGuacdTunnel.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/guacd/IGuacdTunnel.java @@ -64,9 +64,15 @@ public interface IGuacdTunnel extends Runnable, Executable, SafeCloseable { * * @param width width * @param height height - * @param dpi dpi */ - void size(int width, int height, int dpi); + void size(int width, int height); + + /** + * dpi + * + * @param dpi dpi + */ + void dpi(int dpi); /** * 设置时区 diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/guacd/constant/GuacdConst.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/guacd/constant/GuacdConst.java index c0edfb9e..99d5cdb5 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/guacd/constant/GuacdConst.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/guacd/constant/GuacdConst.java @@ -392,6 +392,16 @@ public interface GuacdConst { */ String CLIPBOARD_ENCODING = "clipboard-encoding"; + /** + * 压缩等级 + */ + String COMPRESS_LEVEL = "compress-level"; + + /** + * 质量等级 + */ + String QUALITY_LEVEL = "quality-level"; + // -------------------- const -------------------- String RESIZE_METHOD_DISPLAY_UPDATE = "display-update"; diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/TerminalAccessVncHandler.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/TerminalAccessVncHandler.java new file mode 100644 index 00000000..92a4ac4e --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/TerminalAccessVncHandler.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * 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.dromara.visor.module.terminal.handler.terminal; + +import lombok.extern.slf4j.Slf4j; +import org.dromara.visor.module.terminal.handler.terminal.enums.InputProtocolEnum; +import org.dromara.visor.module.terminal.handler.terminal.model.TerminalChannelProps; +import org.dromara.visor.module.terminal.handler.terminal.sender.IGuacdTerminalSender; +import org.dromara.visor.module.terminal.handler.terminal.sender.WebsocketGuacdTerminalSender; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; + +/** + * VNC 终端处理器 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2023/12/28 14:33 + */ +@Slf4j +@Component +public class TerminalAccessVncHandler extends AbstractTerminalAccessHandler { + + @Override + protected IGuacdTerminalSender createSender(WebSocketSession channel) { + return new WebsocketGuacdTerminalSender(channel); + } + + @Override + protected void handleMessage(WebSocketSession channel, TextMessage message, TerminalChannelProps props, IGuacdTerminalSender sender) { + String payload = message.getPayload(); + try { + // 解析类型 + InputProtocolEnum type = InputProtocolEnum.of(payload); + if (type == null) { + return; + } + // 解析并处理消息 + type.getHandler().handle(props, sender, type.parse(payload)); + } catch (Exception e) { + log.error("TerminalAccessVncHandler-handleMessage-error id: {}, msg: {}", channel.getId(), payload, e); + } + } + +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/GuacdInstructionHandler.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/GuacdInstructionHandler.java index 854bc582..0787e012 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/GuacdInstructionHandler.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/handler/GuacdInstructionHandler.java @@ -26,7 +26,7 @@ import lombok.extern.slf4j.Slf4j; import org.dromara.visor.module.terminal.handler.terminal.model.TerminalChannelProps; import org.dromara.visor.module.terminal.handler.terminal.model.request.GuacdInstructionRequest; import org.dromara.visor.module.terminal.handler.terminal.sender.IGuacdTerminalSender; -import org.dromara.visor.module.terminal.handler.terminal.session.IRdpSession; +import org.dromara.visor.module.terminal.handler.terminal.session.IGuacdSession; import org.dromara.visor.module.terminal.handler.terminal.session.ITerminalSession; import org.springframework.stereotype.Component; @@ -45,20 +45,10 @@ public class GuacdInstructionHandler extends AbstractTerminalHandler */ protected abstract IGuacdTunnel createTunnel(); + /** + * 是否为低带宽模式 + * + * @return is + */ + protected abstract boolean isLowBandwidthMode(); + /** * 设置 tunnel 参数 */ - protected abstract void setTunnelParams(); + protected void setTunnelParams() { + // 设置低带宽模式 + if (this.isLowBandwidthMode()) { + this.setLowBandwidthMode(); + } + // 主机信息 + tunnel.remote(config.getHostAddress(), config.getHostPort()); + // 身份信息 + tunnel.auth(config.getUsername(), AesEncryptUtils.decryptAsString(config.getPassword())); + // 大小 + tunnel.size(config.getWidth(), config.getHeight()); + } + + /** + * 设置低带宽模式 + */ + protected void setLowBandwidthMode() { + TerminalChannelExtra extra = props.getExtra(); + extra.setColorDepth(8); + extra.setForceLossless(false); + extra.setEnableWallpaper(false); + extra.setEnableTheming(false); + extra.setEnableFontSmoothing(false); + extra.setEnableFullWindowDrag(false); + extra.setEnableDesktopComposition(false); + extra.setEnableMenuAnimations(false); + extra.setDisableBitmapCaching(false); + extra.setDisableOffscreenCaching(false); + extra.setDisableGlyphCaching(false); + extra.setDisableGfx(false); + extra.setEnableAudioInput(false); + extra.setEnableAudioOutput(false); + extra.setCompressLevel(9); + extra.setQualityLevel(1); + } /** * 执行连接 @@ -92,6 +135,13 @@ public abstract class AbstractGuacdSession tunnel.write(data); } + @Override + public void resize(int width, int height) { + config.setWidth(width); + config.setHeight(height); + tunnel.writeInstruction("size", String.valueOf(width), String.valueOf(height)); + } + @Override public void keepAlive() { // guacd 有内部实现 diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IGuacdSession.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IGuacdSession.java index a9f79372..56c8249c 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IGuacdSession.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IGuacdSession.java @@ -29,7 +29,7 @@ package org.dromara.visor.module.terminal.handler.terminal.session; * @version 1.0.0 * @since 2025/3/30 17:42 */ -public interface IGuacdSession extends ITerminalSession { +public interface IGuacdSession extends ITerminalSession, IResizeableSession { /** * 写入数据 diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IRdpSession.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IRdpSession.java index de2d5860..225316b9 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IRdpSession.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IRdpSession.java @@ -30,13 +30,4 @@ package org.dromara.visor.module.terminal.handler.terminal.session; * @since 2025/3/30 17:42 */ public interface IRdpSession extends IGuacdSession { - - /** - * 重置大小 - * - * @param width width - * @param height height - */ - void resize(int width, int height); - } diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IResizeableSession.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IResizeableSession.java new file mode 100644 index 00000000..cdc41edb --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IResizeableSession.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * 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.dromara.visor.module.terminal.handler.terminal.session; + +/** + * 可修改大小的会话 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2025/7/3 2:16 + */ +public interface IResizeableSession { + + /** + * 修改大小 + * + * @param width width + * @param height height + */ + void resize(int width, int height); + +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/ISshSession.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/ISshSession.java index 1f621fcc..d978900a 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/ISshSession.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/ISshSession.java @@ -32,15 +32,7 @@ import org.dromara.visor.module.terminal.handler.terminal.sender.ISshTerminalSen * @version 1.0.0 * @since 2024/2/4 16:47 */ -public interface ISshSession extends ITerminalSession { - - /** - * 重置大小 - * - * @param width width - * @param height height - */ - void resize(int width, int height); +public interface ISshSession extends ITerminalSession, IResizeableSession { /** * 写入内容 diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IVncSession.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IVncSession.java new file mode 100644 index 00000000..e17bb388 --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/IVncSession.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * 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.dromara.visor.module.terminal.handler.terminal.session; + +/** + * vnc 会话 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2025/7/3 2:04 + */ +public interface IVncSession extends IGuacdSession { +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/RdpSession.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/RdpSession.java index 165e8af8..c0e463bd 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/RdpSession.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/RdpSession.java @@ -27,7 +27,6 @@ import cn.orionsec.kit.lang.utils.Strings; import cn.orionsec.kit.lang.utils.io.Files1; import lombok.extern.slf4j.Slf4j; import org.dromara.visor.common.constant.AppConst; -import org.dromara.visor.common.utils.AesEncryptUtils; import org.dromara.visor.module.common.config.GuacdConfig; import org.dromara.visor.module.terminal.enums.DriveMountModeEnum; import org.dromara.visor.module.terminal.handler.guacd.GuacdTunnel; @@ -66,19 +65,13 @@ public class RdpSession extends AbstractGuacdSession i @Override protected void setTunnelParams() { + super.setTunnelParams(); + // 设置额外参数 TerminalChannelExtra extra = props.getExtra(); // 音频输入会导致无法连接先写死 extra.setEnableAudioInput(false); - // 设置低带宽模式 - if (Booleans.isTrue(config.getLowBandwidthMode())) { - this.setLowBandwidthMode(extra); - } - // 主机信息 - tunnel.remote(config.getHostAddress(), config.getHostPort()); - // 身份信息 - tunnel.auth(config.getUsername(), AesEncryptUtils.decryptAsString(config.getPassword())); - // 大小 - tunnel.size(config.getWidth(), config.getHeight(), config.getDpi()); + // dpi + tunnel.dpi(config.getDpi()); // 时区 tunnel.timezone(config.getTimezone()); // 忽略证书 @@ -140,32 +133,8 @@ public class RdpSession extends AbstractGuacdSession i } @Override - public void resize(int width, int height) { - config.setWidth(width); - config.setHeight(height); - tunnel.writeInstruction("size", String.valueOf(width), String.valueOf(height)); - } - - /** - * 低带宽模式 - * - * @param extra extra - */ - private void setLowBandwidthMode(TerminalChannelExtra extra) { - extra.setColorDepth(8); - extra.setForceLossless(false); - extra.setEnableWallpaper(false); - extra.setEnableTheming(false); - extra.setEnableFontSmoothing(false); - extra.setEnableFullWindowDrag(false); - extra.setEnableDesktopComposition(false); - extra.setEnableMenuAnimations(false); - extra.setDisableBitmapCaching(false); - extra.setDisableOffscreenCaching(false); - extra.setDisableGlyphCaching(false); - extra.setDisableGfx(false); - extra.setEnableAudioInput(false); - extra.setEnableAudioOutput(false); + protected boolean isLowBandwidthMode() { + return Booleans.isTrue(config.getLowBandwidthMode()); } } diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/VncSession.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/VncSession.java new file mode 100644 index 00000000..9de39f17 --- /dev/null +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/handler/terminal/session/VncSession.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023 - present Dromara, All rights reserved. + * + * https://visor.dromara.org + * https://visor.dromara.org.cn + * https://visor.orionsec.cn + * + * Members: + * Jiahang Li - ljh1553488six@139.com - author + * + * 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.dromara.visor.module.terminal.handler.terminal.session; + +import cn.orionsec.kit.lang.utils.Booleans; +import lombok.extern.slf4j.Slf4j; +import org.dromara.visor.module.common.config.GuacdConfig; +import org.dromara.visor.module.terminal.handler.guacd.GuacdTunnel; +import org.dromara.visor.module.terminal.handler.guacd.IGuacdTunnel; +import org.dromara.visor.module.terminal.handler.guacd.constant.GuacdConst; +import org.dromara.visor.module.terminal.handler.guacd.constant.GuacdProtocol; +import org.dromara.visor.module.terminal.handler.terminal.model.TerminalChannelExtra; +import org.dromara.visor.module.terminal.handler.terminal.model.TerminalChannelProps; +import org.dromara.visor.module.terminal.handler.terminal.model.config.TerminalSessionVncConfig; +import org.dromara.visor.module.terminal.handler.terminal.sender.IGuacdTerminalSender; + +/** + * vnc 会话 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2025/3/30 17:44 + */ +@Slf4j +public class VncSession extends AbstractGuacdSession implements IVncSession { + + private final GuacdConfig guacdConfig; + + public VncSession(TerminalChannelProps props, + IGuacdTerminalSender sender, + TerminalSessionVncConfig config, + GuacdConfig guacdConfig) { + super(props, sender, config); + this.guacdConfig = guacdConfig; + } + + @Override + protected IGuacdTunnel createTunnel() { + return new GuacdTunnel(GuacdProtocol.VNC, sessionId, guacdConfig.getHost(), guacdConfig.getPort()); + } + + @Override + protected void setTunnelParams() { + super.setTunnelParams(); + // 设置额外参数 + TerminalChannelExtra extra = props.getExtra(); + // 时区 + tunnel.timezone(config.getTimezone()); + // 显示设置 + tunnel.setParameter(GuacdConst.COLOR_DEPTH, extra.getColorDepth()); + tunnel.setParameter(GuacdConst.FORCE_LOSSLESS, extra.getForceLossless()); + tunnel.setParameter(GuacdConst.COMPRESS_LEVEL, extra.getCompressLevel()); + tunnel.setParameter(GuacdConst.QUALITY_LEVEL, extra.getQualityLevel()); + // 交换红蓝 + tunnel.setParameter(GuacdConst.SWAP_RED_BLUE, config.getSwapRedBlue()); + // 光标设置 + tunnel.setParameter(GuacdConst.CURSOR, extra.getCursor()); + // 编码设置 + tunnel.setParameter(GuacdConst.CLIPBOARD_ENCODING, config.getClipboardEncoding()); + } + + @Override + protected boolean isLowBandwidthMode() { + return Booleans.isTrue(config.getLowBandwidthMode()); + } + +} diff --git a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/utils/SftpFileUtils.java b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/utils/SftpFileUtils.java index 8c97c447..2ffe54cb 100644 --- a/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/utils/SftpFileUtils.java +++ b/orion-visor-modules/orion-visor-module-terminal/orion-visor-module-terminal-service/src/main/java/org/dromara/visor/module/terminal/utils/SftpFileUtils.java @@ -79,7 +79,7 @@ public class SftpFileUtils { SftpFileVO vo = new SftpFileVO(); vo.setName(file.getName()); vo.setPath(file.getPath()); - vo.setSuffix(Files1.getSuffix(file.getName())); + vo.setSuffix(Files1.getFileNameSuffix(file.getName())); vo.setSize(file.getSize()); vo.setPermission(file.getPermission()); vo.setUid(file.getUid()); diff --git a/orion-visor-ui/.env.development b/orion-visor-ui/.env.development index 18b59f51..a9980fe5 100644 --- a/orion-visor-ui/.env.development +++ b/orion-visor-ui/.env.development @@ -3,4 +3,4 @@ VITE_API_BASE_URL=http://127.0.0.1:9200/orion-visor/api # websocket 路径 VITE_WS_BASE_URL=ws://127.0.0.1:9200/orion-visor/keep-alive # 版本号 -VITE_APP_VERSION=2.4.1 +VITE_APP_VERSION=2.4.2 diff --git a/orion-visor-ui/.env.production b/orion-visor-ui/.env.production index 25daa624..9ef22fae 100644 --- a/orion-visor-ui/.env.production +++ b/orion-visor-ui/.env.production @@ -3,4 +3,4 @@ VITE_API_BASE_URL=/orion-visor/api # websocket 路径 VITE_WS_BASE_URL=/orion-visor/keep-alive # 版本号 -VITE_APP_VERSION=2.4.1 +VITE_APP_VERSION=2.4.2 diff --git a/orion-visor-ui/package.json b/orion-visor-ui/package.json index 25609244..d057bd54 100644 --- a/orion-visor-ui/package.json +++ b/orion-visor-ui/package.json @@ -1,7 +1,7 @@ { "name": "orion-visor-ui", "description": "Orion Visor UI", - "version": "2.4.1", + "version": "2.4.2", "private": true, "author": "Jiahang Li", "license": "Apache 2.0", diff --git a/orion-visor-ui/src/api/asset/host-config.ts b/orion-visor-ui/src/api/asset/host-config.ts index 7a089263..281d34a0 100644 --- a/orion-visor-ui/src/api/asset/host-config.ts +++ b/orion-visor-ui/src/api/asset/host-config.ts @@ -51,6 +51,16 @@ export interface HostRdpConfig extends HostBaseConfig { remoteAppArgs?: string; } +// 主机 VNC 配置 +export interface HostVncConfig extends HostBaseConfig { + identityId?: number; + noUsername?: boolean; + noPassword?: boolean; + portForwardId?: number; + timezone?: string; + clipboardEncoding?: string; +} + /** * 更新主机配置 */ diff --git a/orion-visor-ui/src/api/asset/host-extra.ts b/orion-visor-ui/src/api/asset/host-extra.ts index 9fb2b142..62419d8d 100644 --- a/orion-visor-ui/src/api/asset/host-extra.ts +++ b/orion-visor-ui/src/api/asset/host-extra.ts @@ -33,6 +33,13 @@ export interface HostRdpExtraSettingModel { initialProgram: string; } +// VNC 额外配置 +export interface HostVncExtraSettingModel { + port: number; + lowBandwidthMode: boolean; + swapRedBlue: boolean; +} + // 标签额外配置 export interface HostLabelExtraSettingModel { alias: string; diff --git a/orion-visor-ui/src/api/asset/host.ts b/orion-visor-ui/src/api/asset/host.ts index 08320dd9..a01dd48e 100644 --- a/orion-visor-ui/src/api/asset/host.ts +++ b/orion-visor-ui/src/api/asset/host.ts @@ -5,7 +5,7 @@ import axios from 'axios'; import qs from 'query-string'; // 主机类型 -export type HostType = 'SSH' | string | undefined; +export type HostType = 'SSH' | 'RDP' | 'VNC' | string | undefined; /** * 主机创建请求 diff --git a/orion-visor-ui/src/assets/style/host-terminal-layout.less b/orion-visor-ui/src/assets/style/host-terminal-layout.less index 090d5bf3..926c7f37 100644 --- a/orion-visor-ui/src/assets/style/host-terminal-layout.less +++ b/orion-visor-ui/src/assets/style/host-terminal-layout.less @@ -619,5 +619,189 @@ body[terminal-theme='dark'] .arco-modal-container { font-size: 16px; margin: 0 8px 0 4px; } - +} + +// guacd 容器 +.guacd-container { + + // guacd 视口 + .guacd-viewport { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + + :deep(> div) { + position: relative; + z-index: 8; + } + } + + // guacd 状态遮罩 + .guacd-status-mask { + width: 100%; + height: 100%; + position: absolute; + backdrop-filter: blur(10px); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + } + + // guacd 工具栏 + .guacd-action-bar { + position: absolute; + cursor: pointer; + z-index: 9998; + + &.top { + top: 4px; + left: 50%; + transform: translateX(-50%); + } + + &.right { + right: 4px; + top: 50%; + transform: translateY(-50%); + } + + // 工具栏触发器 + .action-bar-trigger { + display: flex; + border-radius: 8px; + transition: .3s all; + background: var(--color-bg-rdp-toolbar); + filter: contrast(50%) brightness(50%); + + &.top { + width: 240px; + height: 8px; + + &:hover { + transform: translateY(2px); + } + } + + &.right { + width: 8px; + height: 228px; + + &:hover { + transform: translateX(-2px); + } + } + + &:hover { + background: var(--color-bg-rdp-toolbar-hover); + } + } + + } +} + +// guacd 工具栏 +@guacd-action-size: 42px; + +.guacd-action-bar-popover { + --actions-width: calc(var(--action-count) * @guacd-action-size + (var(--action-count) - 1) * 16px); + background: var(--color-bg-2); + + .arco-popover-content { + margin-top: 0; + display: flex; + } + + .action-bar-button { + width: @guacd-action-size !important; + height: @guacd-action-size !important; + font-size: 20px; + } + + .action-bar-content { + display: flex; + flex-direction: column; + } + + .action-bar-content-footer { + margin-top: 12px; + display: flex; + justify-content: flex-end; + } + + .display-size-label { + padding-right: 6px; + user-select: none; + text-align: end; + } + + .display-size-input { + width: 198px; + } + + .action-bar-upload, .action-bar-clipboard { + display: flex; + } + + .combination-key-item { + span { + display: block; + padding: 6px 12px; + cursor: pointer; + background: var(--color-fill-1); + border-radius: 2px; + user-select: none; + transition: 0.2s ALL; + + &:hover { + background: var(--color-fill-2); + } + } + } +} + +.guacd-action-bar-popover.top { + .arco-popover-content { + flex-direction: column; + width: var(--actions-width); + } + + .action-bar-content { + margin-top: 16px; + max-height: 224px; + overflow-x: hidden; + overflow-y: auto; + + &::-webkit-scrollbar { + display: none; + } + } + + .action-bar-upload, .action-bar-clipboard { + height: 186px; + } +} + +.guacd-action-bar-popover.right { + .arco-popover-content { + flex-direction: row-reverse; + height: var(--actions-width); + } + + .action-bar-content { + margin-right: 16px; + width: 344px; + overflow-x: hidden; + overflow-y: auto; + + &::-webkit-scrollbar { + display: none; + } + } + + .action-bar-upload, .action-bar-clipboard { + height: calc(var(--actions-width) - 40px); + } } diff --git a/orion-visor-ui/src/components/view/card-list/components/card-item.vue b/orion-visor-ui/src/components/view/card-list/components/card-item.vue index 1c134d14..70891885 100644 --- a/orion-visor-ui/src/components/view/card-list/components/card-item.vue +++ b/orion-visor-ui/src/components/view/card-list/components/card-item.vue @@ -9,7 +9,6 @@ :bordered="false" :hoverable="true" :body-style="cardBodyStyle as Record" - @contextmenu.prevent="() => false" @click="bubblesEmitter(CardEmitter.CLICK, item, index)" @dblclick="bubblesEmitter(CardEmitter.DBL_CLICK, item, index)"> diff --git a/orion-visor-ui/src/components/view/card-list/index.vue b/orion-visor-ui/src/components/view/card-list/index.vue index d1d2454a..097fe684 100644 --- a/orion-visor-ui/src/components/view/card-list/index.vue +++ b/orion-visor-ui/src/components/view/card-list/index.vue @@ -33,7 +33,9 @@ v-bind="cardLayoutCols" :class="{ 'disabled-col': item.disabled === true }"> - + -