Kaynağa Gözat

first commit

周玉环 1 gün önce
ebeveyn
işleme
8c16b08144

+ 0 - 298
backend/DEPLOY.md

@@ -1,298 +0,0 @@
-# Cloudpods 后端部署指南
-
-## 一、环境要求
-
-### 1. 系统要求
-- **操作系统**: Linux (CentOS 7/8, Ubuntu 18.04/20.04/22.04)
-- **CPU**: 4核及以上
-- **内存**: 8GB及以上
-- **磁盘**: 50GB及以上可用空间
-
-### 2. 软件依赖
-- **MySQL**: 5.7 或 8.0
-- **Go**: 1.21 或更高版本(如需编译)
-
-## 二、部署前准备
-
-### 1. 安装 MySQL
-```bash
-# CentOS/RHEL
-sudo yum install -y mysql-server
-sudo systemctl start mysqld
-sudo systemctl enable mysqld
-
-# Ubuntu/Debian
-sudo apt-get update
-sudo apt-get install -y mysql-server
-sudo systemctl start mysql
-sudo systemctl enable mysql
-```
-
-### 2. 创建数据库
-```bash
-# 登录 MySQL
-mysql -u root -p
-
-# 创建数据库
-CREATE DATABASE yunioncloud CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-
-# 创建用户(可选,也可以使用 root)
-CREATE USER 'yunion'@'%' IDENTIFIED BY 'your_password';
-GRANT ALL PRIVILEGES ON yunioncloud.* TO 'yunion'@'%';
-FLUSH PRIVILEGES;
-EXIT;
-```
-
-### 3. 配置环境变量
-创建 `.env` 文件:
-```bash
-cd /opt/cloudpods/backend
-cat > .env << 'EOF'
-# 数据库配置
-DB_HOST=127.0.0.1
-DB_PORT=3306
-DB_USER=yunion
-DB_PASSWORD=your_password
-DB_NAME=yunioncloud
-
-# 管理员配置
-ADMIN_USER=sysadmin
-ADMIN_PASSWORD=admin@123
-ADMIN_PROJECT=system
-ADMIN_DOMAIN=Default
-REGION_NAME=region0
-EOF
-```
-
-## 三、部署方式
-
-### 方式一:使用预编译二进制(推荐)
-
-#### 1. 下载二进制包
-```bash
-# 创建部署目录
-mkdir -p /opt/cloudpods
-cd /opt/cloudpods
-
-# 下载二进制包(从 CI/CD 或 Release 页面)
-wget https://your-ci-server/cloudpods-backend-linux-amd64.tar.gz
-tar -xzf cloudpods-backend-linux-amd64.tar.gz
-```
-
-#### 2. 执行部署脚本
-```bash
-cd /opt/cloudpods/backend
-
-# 执行初始化(首次部署)
-./init-cloudpods.sh
-
-# 或跳过确认直接部署
-./init-cloudpods.sh --skip-confirm
-```
-
-### 方式二:源码编译部署
-
-#### 1. 安装 Go
-```bash
-# 下载 Go
-wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz
-sudo tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz
-
-# 配置环境变量
-echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
-source ~/.bashrc
-```
-
-#### 2. 克隆代码并编译
-```bash
-cd /opt
-git clone https://github.com/your-org/cloudpods.git
-cd cloudpods/backend
-
-# 编译所有服务
-make all
-
-# 或只编译特定服务
-make cmd/keystone cmd/region cmd/glance cmd/yunionconf cmd/scheduledtask cmd/apigateway
-```
-
-#### 3. 执行部署
-```bash
-./init-cloudpods.sh
-```
-
-## 四、服务管理
-
-### 查看服务状态
-```bash
-cd /opt/cloudpods/backend
-
-# 查看所有服务进程
-ps aux | grep -E 'keystone|region|glance|yunionconf|scheduledtask|apigateway'
-
-# 查看服务端口
-netstat -tlnp | grep -E '30500|30889|9292|30888|30950|3000'
-```
-
-### 启动/停止服务
-```bash
-# 停止所有服务
-./stop-backend.sh
-
-# 启动服务(开发模式,不重置数据)
-./start-dev.sh
-
-# 完全重置并重新初始化(会删除数据库)
-./init-cloudpods.sh
-```
-
-### 查看日志
-```bash
-# 实时查看所有日志
-tail -f logs/*.log
-
-# 查看特定服务日志
-tail -f logs/keystone.log
-tail -f logs/region.log
-```
-
-## 五、配置文件说明
-
-### init-config.env(可选)
-如需自定义配置,创建此文件:
-```bash
-cat > init-config.env << 'EOF'
-# 数据库配置
-DB_HOST=192.168.1.100
-DB_PORT=3306
-DB_USER=cloudpods
-DB_PASSWORD=SecurePass123!
-DB_NAME=yunioncloud
-
-# 服务端口(如有端口冲突可修改)
-KEYSTONE_PORT=30500
-REGION_PORT=30889
-GLANCE_PORT=9292
-YUNIONCONF_PORT=30888
-SCHEDULEDTASK_PORT=30950
-APIGATEWAY_PORT=3000
-
-# 管理员账号
-ADMIN_USER=admin
-ADMIN_PASSWORD=YourSecurePassword
-ADMIN_PROJECT=system
-ADMIN_DOMAIN=Default
-REGION_NAME=region0
-EOF
-```
-
-然后执行:
-```bash
-./init-cloudpods.sh --config init-config.env
-```
-
-## 六、健康检查
-
-### 1. 检查服务是否正常运行
-```bash
-# Keystone
-curl http://localhost:30500/v3
-
-# APIGateway
-curl http://localhost:3000/api/v1/rpc/parameters/global-settings
-
-# Region
-curl http://localhost:30889/regions
-```
-
-### 2. 使用 climc 检查
-```bash
-export OS_AUTH_URL=http://127.0.0.1:30500/v3
-export OS_USERNAME=sysadmin
-export OS_PASSWORD=admin@123
-export OS_DOMAIN_NAME=Default
-export OS_PROJECT_NAME=system
-
-# 查看 region 列表
-./_output/bin/climc region-list
-
-# 查看服务列表
-./_output/bin/climc service-list
-
-# 查看 endpoint 列表
-./_output/bin/climc endpoint-list
-```
-
-## 七、常见问题
-
-### 1. 端口冲突
-如果提示端口被占用,修改 `init-config.env` 中的端口配置。
-
-### 2. 数据库连接失败
-检查:
-- MySQL 服务是否运行:`systemctl status mysql`
-- 数据库用户权限是否正确
-- 防火墙是否放行 MySQL 端口
-
-### 3. 服务启动失败
-查看对应服务的日志:
-```bash
-tail -f logs/keystone.log
-```
-
-## 八、系统服务化(可选)
-
-如需将服务注册为 systemd 服务,创建 `/etc/systemd/system/cloudpods-backend.service`:
-
-```ini
-[Unit]
-Description=Cloudpods Backend Services
-After=network.target mysql.service
-
-[Service]
-Type=forking
-User=cloudpods
-Group=cloudpods
-WorkingDirectory=/opt/cloudpods/backend
-ExecStart=/opt/cloudpods/backend/start-dev.sh
-ExecStop=/opt/cloudpods/backend/stop-backend.sh
-Restart=on-failure
-RestartSec=5
-
-[Install]
-WantedBy=multi-user.target
-```
-
-启用服务:
-```bash
-sudo systemctl daemon-reload
-sudo systemctl enable cloudpods-backend
-sudo systemctl start cloudpods-backend
-sudo systemctl status cloudpods-backend
-```
-
-## 九、前端配置
-
-前端需要配置 API 地址,修改前端项目的 `.env` 文件:
-```bash
-# 前端项目根目录
-cat > .env << 'EOF'
-VUE_APP_API_URL=http://your-server-ip:3000
-EOF
-```
-
-然后构建并部署前端。
-
-## 十、安全建议
-
-1. **修改默认密码**: 部署后立即修改 sysadmin 密码
-2. **使用 HTTPS**: 生产环境配置 SSL/TLS
-3. **防火墙配置**: 只开放必要的端口(3000, 8080)
-4. **数据库安全**: 使用强密码,限制数据库访问 IP
-5. **定期备份**: 备份数据库和配置文件
-
----
-
-**部署完成后访问**: http://your-server-ip:8080
-
-**默认账号**: sysadmin / admin@123

+ 0 - 551
backend/init-cloudpods.sh

@@ -1,551 +0,0 @@
-#!/bin/bash
-
-# Cloudpods 统一初始化脚本
-# 用于全新部署时的完整初始化
-
-set -e
-
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-BLUE='\033[0;34m'
-NC='\033[0m'
-
-# 初始化配置
-INIT_CONFIG_FILE="${INIT_CONFIG_FILE:-init-config.env}"
-
-# 加载配置文件
-load_config() {
-    if [ -f "$INIT_CONFIG_FILE" ]; then
-        echo -e "${BLUE}加载配置文件: $INIT_CONFIG_FILE${NC}"
-        set -a
-        source "$INIT_CONFIG_FILE"
-        set +a
-    fi
-    
-    # 加载 .env.local
-    if [ -f "../.env.local" ]; then
-        set -a
-        source ../.env.local
-        set +a
-    fi
-}
-
-# 设置默认值
-setup_defaults() {
-    # 数据库配置
-    DB_HOST=${DB_HOST:-127.0.0.1}
-    DB_PORT=${DB_PORT:-3306}
-    DB_USER=${DB_USER:-root}
-    DB_PASSWORD=${DB_PASSWORD:-}
-    DB_NAME=${DB_NAME:-yunioncloud}
-    
-    # Keystone 配置
-    KEYSTONE_HOST=${KEYSTONE_HOST:-127.0.0.1}
-    KEYSTONE_PORT=${KEYSTONE_PORT:-30500}
-    ADMIN_USER=${ADMIN_USER:-sysadmin}
-    ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin@123}
-    ADMIN_PROJECT=${ADMIN_PROJECT:-system}
-    ADMIN_DOMAIN=${ADMIN_DOMAIN:-Default}
-    REGION_NAME=${REGION_NAME:-region0}
-    
-    # 服务端口配置
-    REGION_PORT=${REGION_PORT:-30889}
-    GLANCE_PORT=${GLANCE_PORT:-9292}
-    YUNIONCONF_PORT=${YUNIONCONF_PORT:-30888}
-    MONITOR_PORT=${MONITOR_PORT:-30900}
-    SCHEDULEDTASK_PORT=${SCHEDULEDTASK_PORT:-30950}
-    APIGATEWAY_PORT=${APIGATEWAY_PORT:-3000}
-}
-
-# 打印配置信息
-print_config() {
-    echo -e "${GREEN}=== Cloudpods 初始化配置 ===${NC}"
-    echo ""
-    echo -e "${BLUE}数据库配置:${NC}"
-    echo "  主机: ${DB_HOST}:${DB_PORT}"
-    echo "  数据库: ${DB_NAME}"
-    echo "  用户: ${DB_USER}"
-    echo ""
-    echo -e "${BLUE}Keystone 配置:${NC}"
-    echo "  地址: http://${KEYSTONE_HOST}:${KEYSTONE_PORT}/v3"
-    echo "  管理员: ${ADMIN_USER}@${ADMIN_DOMAIN}"
-    echo "  项目: ${ADMIN_PROJECT}"
-    echo "  Region: ${REGION_NAME}"
-    echo ""
-    echo -e "${BLUE}服务端口配置:${NC}"
-    echo "  Region: ${REGION_PORT}"
-    echo "  Glance: ${GLANCE_PORT}"
-    echo "  YunionConf: ${YUNIONCONF_PORT}"
-    echo "  Monitor: ${MONITOR_PORT}"
-    echo "  ScheduledTask: ${SCHEDULEDTASK_PORT}"
-    echo "  APIGateway: ${APIGATEWAY_PORT}"
-    echo ""
-}
-
-# 初始化阶段 1: 数据库
-init_database() {
-    echo -e "${GREEN}=== 阶段 1: 初始化数据库 ===${NC}"
-    
-    # 构建 mysql 命令
-    if [ -z "$DB_PASSWORD" ]; then
-        MYSQL_CMD="mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER}"
-    else
-        MYSQL_CMD="mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASSWORD}"
-    fi
-    
-    # 检查 MySQL 连接
-    echo -e "${YELLOW}检查 MySQL 连接...${NC}"
-    if ! $MYSQL_CMD -e "SELECT 1" > /dev/null 2>&1; then
-        echo -e "${RED}错误: 无法连接到 MySQL${NC}"
-        exit 1
-    fi
-    echo -e "${GREEN}✓ MySQL 连接正常${NC}"
-    
-    # 删除已存在的数据库
-    echo -e "${YELLOW}删除已存在的数据库 ${DB_NAME}...${NC}"
-    $MYSQL_CMD -e "DROP DATABASE IF EXISTS ${DB_NAME};" 2>/dev/null || true
-    
-    # 创建新数据库
-    echo -e "${YELLOW}创建数据库 ${DB_NAME}...${NC}"
-    $MYSQL_CMD -e "CREATE DATABASE ${DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
-    echo -e "${GREEN}✓ 数据库创建成功${NC}"
-    
-    # 创建用户(如果不存在)
-    if [ -n "$DB_PASSWORD" ]; then
-        echo -e "${YELLOW}创建数据库用户...${NC}"
-        $MYSQL_CMD -e "CREATE USER IF NOT EXISTS '${DB_USER}'@'%' IDENTIFIED BY '${DB_PASSWORD}';" 2>/dev/null || true
-        $MYSQL_CMD -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%';" 2>/dev/null || true
-        $MYSQL_CMD -e "FLUSH PRIVILEGES;" 2>/dev/null || true
-        echo -e "${GREEN}✓ 数据库用户权限设置完成${NC}"
-    fi
-}
-
-# 初始化阶段 2: 启动基础服务
-init_services() {
-    echo -e "${GREEN}=== 阶段 2: 启动基础服务 ===${NC}"
-    
-    # 构建数据库连接字符串
-    if [ -z "$DB_PASSWORD" ]; then
-        SQL_CONNECTION="mysql+pymysql://${DB_USER}@${DB_HOST}:${DB_PORT}/${DB_NAME}?charset=utf8mb4"
-    else
-        SQL_CONNECTION="mysql+pymysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?charset=utf8mb4"
-    fi
-    
-    # 创建日志目录
-    mkdir -p logs
-    
-    # 启动 Keystone
-    echo -e "${YELLOW}启动 Keystone...${NC}"
-    if [ ! -f "_output/bin/keystone" ]; then
-        echo -e "${YELLOW}编译 Keystone...${NC}"
-        GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/keystone yunion.io/x/onecloud/cmd/keystone
-    fi
-    
-    ./_output/bin/keystone \
-        --config /dev/null \
-        --sql-connection "$SQL_CONNECTION" \
-        --auto-sync-table \
-        --port ${KEYSTONE_PORT} \
-        --admin-port 35357 \
-        --bootstrap-admin-user-password ${ADMIN_PASSWORD} \
-        > logs/keystone.log 2>&1 &
-    echo $! > logs/keystone.pid
-    echo -e "${GREEN}✓ Keystone 已启动${NC}"
-    
-    # 等待 Keystone 就绪
-    echo -e "${YELLOW}等待 Keystone 就绪...${NC}"
-    for i in {1..30}; do
-        if curl -s http://127.0.0.1:${KEYSTONE_PORT}/v3 > /dev/null 2>&1; then
-            echo -e "${GREEN}✓ Keystone 已就绪${NC}"
-            break
-        fi
-        sleep 1
-        if [ $i -eq 30 ]; then
-            echo -e "${RED}错误: Keystone 启动超时${NC}"
-            exit 1
-        fi
-    done
-    
-    # 设置环境变量
-    export OS_AUTH_URL=http://127.0.0.1:${KEYSTONE_PORT}/v3
-    export OS_USERNAME=${ADMIN_USER}
-    export OS_PASSWORD=${ADMIN_PASSWORD}
-    export OS_DOMAIN_NAME=${ADMIN_DOMAIN}
-    export OS_PROJECT_NAME=${ADMIN_PROJECT}
-    export OS_REGION_NAME=${REGION_NAME}
-}
-
-# 初始化阶段 3: 注册服务和 Endpoint
-init_keystone() {
-    echo -e "${GREEN}=== 阶段 3: 注册服务和 Endpoint ===${NC}"
-    
-    # 编译 climc
-    if [ ! -f "_output/bin/climc" ]; then
-        echo -e "${YELLOW}编译 climc...${NC}"
-        GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/climc yunion.io/x/onecloud/cmd/climc
-    fi
-    
-    # 创建 Region
-    echo -e "${YELLOW}创建 Region...${NC}"
-    if ! ./_output/bin/climc region-list 2>/dev/null | grep -q "| ${REGION_NAME} "; then
-        ./_output/bin/climc region-create ${REGION_NAME} 2>/dev/null
-        echo -e "${GREEN}✓ Region ${REGION_NAME} 创建成功${NC}"
-    else
-        echo -e "${GREEN}✓ Region ${REGION_NAME} 已存在${NC}"
-    fi
-    
-    # 创建 Zone
-    echo -e "${YELLOW}创建 Zone...${NC}"
-    if ! ./_output/bin/climc zone-list 2>/dev/null | grep -q "| zone0 "; then
-        ./_output/bin/climc zone-create zone0 2>/dev/null
-        echo -e "${GREEN}✓ Zone zone0 创建成功${NC}"
-    else
-        echo -e "${GREEN}✓ Zone zone0 已存在${NC}"
-    fi
-    
-    # 定义服务和端口
-    declare -A SERVICES=(
-        ["region"]=${REGION_PORT}
-        ["glance"]=${GLANCE_PORT}
-        ["yunionconf"]=${YUNIONCONF_PORT}
-        ["monitor"]=${MONITOR_PORT}
-        ["scheduledtask"]=${SCHEDULEDTASK_PORT}
-        ["compute_v2"]=${REGION_PORT}
-        ["common"]=${REGION_PORT}
-        ["yunionapi"]=${REGION_PORT}
-        ["meter"]=${REGION_PORT}
-        ["yunionagent"]=${REGION_PORT}
-    )
-    
-    # 注册服务和 Endpoint
-    for service_name in "${!SERVICES[@]}"; do
-        port=${SERVICES[$service_name]}
-        service_type=$service_name
-        
-        echo -e "${YELLOW}注册服务: ${service_name}${NC}"
-        
-        # 检查服务是否存在
-        if ! ./_output/bin/climc service-list 2>/dev/null | grep -q "| ${service_name} "; then
-            ./_output/bin/climc service-create ${service_type} ${service_name} 2>/dev/null
-            echo -e "${GREEN}  ✓ 服务 ${service_name} 创建成功${NC}"
-        else
-            echo -e "${GREEN}  ✓ 服务 ${service_name} 已存在${NC}"
-        fi
-        
-        # 获取服务 ID
-        service_id=$(./_output/bin/climc service-show ${service_name} 2>/dev/null | grep "^| id" | awk '{print $4}')
-        
-        # 创建 Endpoints
-        for interface in public internal admin; do
-            endpoint_name="${service_name}-${interface}"
-            if ! ./_output/bin/climc endpoint-list --service ${service_id} 2>/dev/null | grep -q "| ${endpoint_name} "; then
-                ./_output/bin/climc endpoint-create ${service_id} ${REGION_NAME} ${interface} "http://127.0.0.1:${port}" 2>/dev/null
-                echo -e "${GREEN}  ✓ Endpoint ${interface} 创建成功${NC}"
-            fi
-        done
-    done
-    
-    # 为 Keystone 创建 slave endpoint
-    echo -e "${YELLOW}注册 Keystone slave endpoint...${NC}"
-    keystone_id=$(./_output/bin/climc service-show keystone 2>/dev/null | grep "^| id" | awk '{print $4}')
-    if [ -n "$keystone_id" ]; then
-        if ! ./_output/bin/climc endpoint-list --service ${keystone_id} 2>/dev/null | grep -q "slave"; then
-            ./_output/bin/climc endpoint-create ${keystone_id} ${REGION_NAME} slave "http://127.0.0.1:35357" 2>/dev/null
-            echo -e "${GREEN}✓ Keystone slave endpoint 创建成功${NC}"
-        fi
-    fi
-}
-
-# 初始化阶段 4: 启动其他服务
-start_other_services() {
-    echo -e "${GREEN}=== 阶段 4: 启动其他服务 ===${NC}"
-    
-    # 构建数据库连接字符串
-    if [ -z "$DB_PASSWORD" ]; then
-        SQL_CONNECTION="mysql+pymysql://${DB_USER}@${DB_HOST}:${DB_PORT}/${DB_NAME}?charset=utf8mb4"
-    else
-        SQL_CONNECTION="mysql+pymysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?charset=utf8mb4"
-    fi
-    
-    # 通用服务启动参数
-    COMMON_ARGS="--auth-url http://127.0.0.1:${KEYSTONE_PORT}/v3 --admin-user ${ADMIN_USER} --admin-password ${ADMIN_PASSWORD} --admin-project ${ADMIN_PROJECT} --admin-domain ${ADMIN_DOMAIN}"
-    
-    # 启动 Region
-    echo -e "${YELLOW}启动 Region...${NC}"
-    if [ ! -f "_output/bin/region" ]; then
-        GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/region yunion.io/x/onecloud/cmd/region
-    fi
-    ./_output/bin/region \
-        --config /dev/null \
-        --sql-connection "$SQL_CONNECTION" \
-        --auto-sync-table \
-        --port ${REGION_PORT} \
-        ${COMMON_ARGS} \
-        > logs/region.log 2>&1 &
-    echo $! > logs/region.pid
-    echo -e "${GREEN}✓ Region 已启动${NC}"
-    
-    # 创建 glance 数据目录
-    mkdir -p /tmp/glance_data
-    
-    # 启动 Glance
-    echo -e "${YELLOW}启动 Glance...${NC}"
-    if [ ! -f "_output/bin/glance" ]; then
-        GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/glance yunion.io/x/onecloud/cmd/glance
-    fi
-    ./_output/bin/glance \
-        --config /dev/null \
-        --sql-connection "$SQL_CONNECTION" \
-        --auto-sync-table \
-        --port ${GLANCE_PORT} \
-        --filesystem-store-datadir /tmp/glance_data \
-        ${COMMON_ARGS} \
-        > logs/glance.log 2>&1 &
-    echo $! > logs/glance.pid
-    echo -e "${GREEN}✓ Glance 已启动${NC}"
-    
-    # 启动 YunionConf
-    echo -e "${YELLOW}启动 YunionConf...${NC}"
-    if [ ! -f "_output/bin/yunionconf" ]; then
-        GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/yunionconf yunion.io/x/onecloud/cmd/yunionconf
-    fi
-    ./_output/bin/yunionconf \
-        --config /dev/null \
-        --sql-connection "$SQL_CONNECTION" \
-        --auto-sync-table \
-        --port ${YUNIONCONF_PORT} \
-        ${COMMON_ARGS} \
-        > logs/yunionconf.log 2>&1 &
-    echo $! > logs/yunionconf.pid
-    echo -e "${GREEN}✓ YunionConf 已启动${NC}"
-    
-    # 启动 ScheduledTask
-    echo -e "${YELLOW}启动 ScheduledTask...${NC}"
-    if [ ! -f "_output/bin/scheduledtask" ]; then
-        GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/scheduledtask yunion.io/x/onecloud/cmd/scheduledtask
-    fi
-    ./_output/bin/scheduledtask \
-        --config /dev/null \
-        --sql-connection "$SQL_CONNECTION" \
-        --auto-sync-table \
-        --port ${SCHEDULEDTASK_PORT} \
-        ${COMMON_ARGS} \
-        > logs/scheduledtask.log 2>&1 &
-    echo $! > logs/scheduledtask.pid
-    echo -e "${GREEN}✓ ScheduledTask 已启动${NC}"
-    
-    # 启动 APIGateway
-    echo -e "${YELLOW}启动 APIGateway...${NC}"
-    if [ ! -f "_output/bin/apigateway" ]; then
-        GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o _output/bin/apigateway yunion.io/x/onecloud/cmd/apigateway
-    fi
-    ./_output/bin/apigateway \
-        --config /dev/null \
-        --port ${APIGATEWAY_PORT} \
-        ${COMMON_ARGS} \
-        --default-region ${REGION_NAME} \
-        > logs/apigateway.log 2>&1 &
-    echo $! > logs/apigateway.pid
-    echo -e "${GREEN}✓ APIGateway 已启动${NC}"
-    
-    # 等待所有服务就绪
-    echo -e "${YELLOW}等待所有服务就绪...${NC}"
-    sleep 5
-}
-
-# 初始化阶段 5: 初始化数据
-init_data() {
-    echo -e "${GREEN}=== 阶段 5: 初始化基础数据 ===${NC}"
-    
-    # 等待服务完全就绪
-    echo -e "${YELLOW}等待服务就绪...${NC}"
-    for i in {1..30}; do
-        if ./_output/bin/climc region-list > /dev/null 2>&1; then
-            echo -e "${GREEN}✓ 服务已就绪${NC}"
-            break
-        fi
-        sleep 2
-        if [ $i -eq 30 ]; then
-            echo -e "${RED}警告: 服务未完全就绪,继续初始化...${NC}"
-        fi
-    done
-    
-    # 创建参数
-    echo -e "${YELLOW}创建系统参数...${NC}"
-    
-    # dashboard-system-default
-    if ! ./_output/bin/climc parameter-show dashboard-system-default > /dev/null 2>&1; then
-        ./_output/bin/climc parameter-create dashboard-system-default '[{"layout":{"component":"NumberCard","h":6,"w":20,"x":0,"y":0},"params":{"name":"虚拟机数量","regionAccountType":"region","usage_key":"all.servers"}},{"layout":{"component":"NumberCard","h":6,"w":20,"x":20,"y":0},"params":{"name":"宿主机数量","regionAccountType":"region","usage_key":"hosts"}},{"layout":{"component":"NumberCard","h":6,"w":20,"x":40,"y":0},"params":{"name":"物理机数量","regionAccountType":"region","usage_key":"baremetals"}},{"layout":{"component":"NumberCard","h":6,"w":20,"x":60,"y":0},"params":{"name":"存储桶数量","regionAccountType":"region","usage_key":"buckets"}}]' 2>/dev/null
-        echo -e "${GREEN}✓ dashboard-system-default 创建成功${NC}"
-    fi
-    
-    # global-settings
-    if ! ./_output/bin/climc parameter-show global-settings > /dev/null 2>&1; then
-        ./_output/bin/climc parameter-create global-settings '{"setupKeys":["zh-CN","en"]}' 2>/dev/null
-        echo -e "${GREEN}✓ global-settings 创建成功${NC}"
-    fi
-    
-    # widget-settings
-    if ! ./_output/bin/climc parameter-show widget-settings > /dev/null 2>&1; then
-        ./_output/bin/climc parameter-create widget-settings '{}' 2>/dev/null
-        echo -e "${GREEN}✓ widget-settings 创建成功${NC}"
-    fi
-    
-    # profile
-    if ! ./_output/bin/climc parameter-show profile > /dev/null 2>&1; then
-        ./_output/bin/climc parameter-create profile '{}' 2>/dev/null
-        echo -e "${GREEN}✓ profile 创建成功${NC}"
-    fi
-    
-    # 更新 sysadmin 用户
-    echo -e "${YELLOW}更新 sysadmin 用户配置...${NC}"
-    user_id=$(./_output/bin/climc user-show ${ADMIN_USER} 2>/dev/null | grep "^| id" | awk '{print $4}')
-    if [ -n "$user_id" ]; then
-        # 允许 Web 控制台登录
-        ./_output/bin/climc user-update --allow-web-console ${ADMIN_USER} 2>/dev/null || true
-        
-        # 分配 admin 角色
-        project_id=$(./_output/bin/climc project-show ${ADMIN_PROJECT} 2>/dev/null | grep "^| id" | awk '{print $4}')
-        if [ -n "$project_id" ]; then
-            ./_output/bin/climc project-add-user ${ADMIN_PROJECT} ${ADMIN_USER} admin 2>/dev/null || true
-        fi
-        echo -e "${GREEN}✓ sysadmin 用户配置更新成功${NC}"
-    fi
-}
-
-# 初始化阶段 6: 初始化 RBAC 策略
-init_policies() {
-    echo -e "${GREEN}=== 阶段 6: 初始化 RBAC 策略 ===${NC}"
-    
-    # 检查 policy 表是否为空
-    policy_count=$(./_output/bin/climc policy-list 2>/dev/null | grep -c "^| " || echo "0")
-    if [ "$policy_count" -gt "2" ]; then
-        echo -e "${GREEN}✓ 策略已存在,跳过初始化${NC}"
-        return
-    fi
-    
-    echo -e "${YELLOW}创建默认策略...${NC}"
-    
-    # 创建系统管理员策略
-    sysadmin_policy='{"policy":{"compute":{"servers":"allow","hosts":"allow","zones":"allow","cloudregions":"allow"},"identity":{"users":"allow","projects":"allow","roles":"allow","policies":"allow","domains":"allow"},"image":{"images":"allow"},"yunionconf":{"parameters":"allow"}}}'
-    
-    if ! ./_output/bin/climc policy-show sysadmin-system-policy > /dev/null 2>&1; then
-        ./_output/bin/climc policy-create --type admin --scope system sysadmin-system-policy "${sysadmin_policy}" 2>/dev/null
-        echo -e "${GREEN}✓ sysadmin-system-policy 创建成功${NC}"
-    fi
-    
-    # 绑定策略到角色
-    admin_role_id=$(./_output/bin/climc role-show admin 2>/dev/null | grep "^| id" | awk '{print $4}')
-    if [ -n "$admin_role_id" ]; then
-        # 检查是否已绑定
-        if ! ./_output/bin/climc policy-binding-list --role ${admin_role_id} 2>/dev/null | grep -q "sysadmin-system-policy"; then
-            ./_output/bin/climc policy-binding-create --role ${admin_role_id} sysadmin-system-policy 2>/dev/null || true
-            echo -e "${GREEN}✓ 策略绑定成功${NC}"
-        fi
-    fi
-}
-
-# 打印完成信息
-print_completion() {
-    echo ""
-    echo -e "${GREEN}========================================${NC}"
-    echo -e "${GREEN}  Cloudpods 初始化完成!${NC}"
-    echo -e "${GREEN}========================================${NC}"
-    echo ""
-    echo -e "${BLUE}服务地址:${NC}"
-    echo "  Keystone:     http://127.0.0.1:${KEYSTONE_PORT}"
-    echo "  Region:       http://127.0.0.1:${REGION_PORT}"
-    echo "  Glance:       http://127.0.0.1:${GLANCE_PORT}"
-    echo "  YunionConf:   http://127.0.0.1:${YUNIONCONF_PORT}"
-    echo "  ScheduledTask:http://127.0.0.1:${SCHEDULEDTASK_PORT}"
-    echo "  APIGateway:   http://127.0.0.1:${APIGATEWAY_PORT}"
-    echo ""
-    echo -e "${BLUE}管理信息:${NC}"
-    echo "  管理员账号: ${ADMIN_USER}"
-    echo "  管理员密码: ${ADMIN_PASSWORD}"
-    echo "  项目: ${ADMIN_PROJECT}"
-    echo "  Domain: ${ADMIN_DOMAIN}"
-    echo ""
-    echo -e "${BLUE}前端访问:${NC}"
-    echo "  http://localhost:8080"
-    echo ""
-    echo -e "${BLUE}常用命令:${NC}"
-    echo "  查看日志: tail -f backend/logs/*.log"
-    echo "  停止服务: ./backend/stop-backend.sh"
-    echo "  服务状态: ps aux | grep -E 'keystone|region|glance|yunionconf|scheduledtask|apigateway'"
-    echo ""
-}
-
-# 主流程
-main() {
-    echo -e "${GREEN}========================================${NC}"
-    echo -e "${GREEN}  Cloudpods 统一初始化脚本${NC}"
-    echo -e "${GREEN}========================================${NC}"
-    echo ""
-    
-    # 加载配置
-    load_config
-    setup_defaults
-    print_config
-    
-    # 确认初始化
-    if [ "$SKIP_CONFIRM" != "true" ]; then
-        echo -e "${YELLOW}警告: 这将删除现有的数据库并重新初始化所有数据!${NC}"
-        read -p "是否继续? (y/N): " confirm
-        if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
-            echo -e "${YELLOW}已取消初始化${NC}"
-            exit 0
-        fi
-    fi
-    
-    # 执行初始化阶段
-    init_database
-    init_services
-    init_keystone
-    start_other_services
-    init_data
-    init_policies
-    
-    # 打印完成信息
-    print_completion
-}
-
-# 处理命令行参数
-while [[ $# -gt 0 ]]; do
-    case $1 in
-        --config)
-            INIT_CONFIG_FILE="$2"
-            shift 2
-            ;;
-        --skip-confirm)
-            SKIP_CONFIRM="true"
-            shift
-            ;;
-        --help)
-            echo "Cloudpods 统一初始化脚本"
-            echo ""
-            echo "用法: $0 [选项]"
-            echo ""
-            echo "选项:"
-            echo "  --config <文件>     指定配置文件 (默认: init-config.env)"
-            echo "  --skip-confirm      跳过确认提示"
-            echo "  --help              显示帮助信息"
-            echo ""
-            echo "环境变量:"
-            echo "  DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME"
-            echo "  ADMIN_USER, ADMIN_PASSWORD, ADMIN_PROJECT, ADMIN_DOMAIN"
-            echo "  REGION_NAME, KEYSTONE_PORT, etc."
-            echo ""
-            exit 0
-            ;;
-        *)
-            echo "未知选项: $1"
-            echo "使用 --help 查看帮助"
-            exit 1
-            ;;
-    esac
-done
-
-# 运行主流程
-main

+ 0 - 38
backend/init-config.env

@@ -1,38 +0,0 @@
-# Cloudpods 初始化配置文件
-# 复制此文件并根据需要修改
-
-# ============================================
-# 数据库配置
-# ============================================
-DB_HOST=127.0.0.1
-DB_PORT=3306
-DB_USER=root
-DB_PASSWORD=
-DB_NAME=yunioncloud
-
-# ============================================
-# Keystone 认证配置
-# ============================================
-KEYSTONE_HOST=127.0.0.1
-KEYSTONE_PORT=30500
-ADMIN_USER=sysadmin
-ADMIN_PASSWORD=admin@123
-ADMIN_PROJECT=system
-ADMIN_DOMAIN=Default
-REGION_NAME=region0
-
-# ============================================
-# 服务端口配置
-# ============================================
-REGION_PORT=30889
-GLANCE_PORT=9292
-YUNIONCONF_PORT=30888
-MONITOR_PORT=30900
-SCHEDULEDTASK_PORT=30950
-APIGATEWAY_PORT=3000
-
-# ============================================
-# 初始化选项
-# ============================================
-# 设置为 true 可跳过确认提示
-# SKIP_CONFIRM=false

+ 0 - 50
backend/init-policies.sql

@@ -1,50 +0,0 @@
--- 初始化 Cloudpods 基本策略数据
--- 这些策略允许 sysadmin 用户执行管理操作
-
--- 检查 policy 表是否为空
-SET @policy_count = (SELECT COUNT(*) FROM policy);
-
--- 如果 policy 表为空,插入默认策略
--- 注意:blob 是 MySQL 保留关键字,需要用反引号转义
--- 策略格式需要包含 "policy" 键,并且规则是嵌套字典格式
--- 格式: {"policy": {"service": {"resource": {"action": "result"}}}}
-INSERT INTO policy (id, name, type, description, `blob`, scope, domain_id, is_public, created_at, updated_at, deleted)
-SELECT 
-    LOWER(REPLACE(UUID(), '-', '')),
-    'sysadmin-system-policy',
-    'sysadmin-system-policy',
-    'System admin policy with full permissions',
-    '{"policy":{"*":{"*":{"*":"allow"}}}}',
-    'system',
-    'default',
-    1,
-    NOW(),
-    NOW(),
-    0
-WHERE @policy_count = 0;
-
--- 获取刚插入的策略 ID
-SET @policy_id = (SELECT id FROM policy WHERE name = 'sysadmin-system-policy' LIMIT 1);
-
--- 获取 admin 角色 ID
-SET @admin_role_id = (SELECT id FROM role WHERE name = 'admin' LIMIT 1);
-
--- 检查 rolepolicy_tbl 是否已有数据
-SET @rp_count = (SELECT COUNT(*) FROM rolepolicy_tbl);
-
--- 如果 rolepolicy_tbl 为空且策略和角色都存在,插入关联
--- rolepolicy_tbl 主键是 (role_id, project_id, policy_id),没有 id 列
-INSERT INTO rolepolicy_tbl (role_id, project_id, policy_id, created_at, updated_at, deleted, auth)
-SELECT 
-    @admin_role_id,
-    '',
-    @policy_id,
-    NOW(),
-    NOW(),
-    0,
-    1
-WHERE @rp_count = 0 AND @policy_id IS NOT NULL AND @admin_role_id IS NOT NULL;
-
--- 验证插入结果
-SELECT 'Policy count after init:' AS info, COUNT(*) AS count FROM policy;
-SELECT 'RolePolicy count after init:' AS info, COUNT(*) AS count FROM rolepolicy_tbl;

+ 0 - 229
backend/start-dev.sh

@@ -1,229 +0,0 @@
-#!/bin/bash
-
-# Cloudpods 开发环境启动脚本
-# 用于日常开发,不重置数据库,只启动服务
-
-set -e
-
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-BLUE='\033[0;34m'
-NC='\033[0m'
-
-echo -e "${GREEN}========================================${NC}"
-echo -e "${GREEN}  Cloudpods 开发环境启动脚本${NC}"
-echo -e "${GREEN}========================================${NC}"
-echo ""
-
-# 加载环境变量
-if [ -f "../.env.local" ]; then
-    echo -e "${BLUE}加载 .env.local 配置...${NC}"
-    set -a
-    source ../.env.local
-    set +a
-fi
-
-# 设置默认值
-DB_HOST=${DB_HOST:-127.0.0.1}
-DB_PORT=${DB_PORT:-3306}
-DB_USER=${DB_USER:-root}
-DB_PASSWORD=${DB_PASSWORD:-}
-DB_NAME=${DB_NAME:-yunioncloud}
-
-KEYSTONE_PORT=${KEYSTONE_PORT:-30500}
-ADMIN_USER=${ADMIN_USER:-sysadmin}
-ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin@123}
-ADMIN_PROJECT=${ADMIN_PROJECT:-system}
-ADMIN_DOMAIN=${ADMIN_DOMAIN:-Default}
-REGION_NAME=${REGION_NAME:-region0}
-
-REGION_PORT=${REGION_PORT:-30889}
-GLANCE_PORT=${GLANCE_PORT:-9292}
-YUNIONCONF_PORT=${YUNIONCONF_PORT:-30888}
-SCHEDULEDTASK_PORT=${SCHEDULEDTASK_PORT:-30950}
-APIGATEWAY_PORT=${APIGATEWAY_PORT:-3000}
-
-# 构建数据库连接字符串
-if [ -z "$DB_PASSWORD" ]; then
-    SQL_CONNECTION="mysql+pymysql://${DB_USER}@${DB_HOST}:${DB_PORT}/${DB_NAME}?charset=utf8mb4"
-else
-    SQL_CONNECTION="mysql+pymysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?charset=utf8mb4"
-fi
-
-# 通用服务启动参数
-COMMON_ARGS="--auth-url http://127.0.0.1:${KEYSTONE_PORT}/v3 --admin-user ${ADMIN_USER} --admin-password ${ADMIN_PASSWORD} --admin-project ${ADMIN_PROJECT} --admin-domain ${ADMIN_DOMAIN}"
-
-# 创建日志目录
-mkdir -p logs
-
-# 检查并编译服务
-check_binary() {
-    local service=$1
-    if [ ! -f "_output/bin/${service}" ]; then
-        echo -e "${YELLOW}编译 ${service}...${NC}"
-        GOOS=darwin CGO_ENABLED=0 go build -mod vendor -o "_output/bin/${service}" "yunion.io/x/onecloud/cmd/${service}"
-    fi
-}
-
-# 停止已运行的服务
-stop_existing() {
-    echo -e "${YELLOW}检查并停止已运行的服务...${NC}"
-    for service in apigateway scheduledtask yunionconf glance region keystone; do
-        if [ -f "logs/${service}.pid" ]; then
-            pid=$(cat logs/${service}.pid)
-            if ps -p $pid > /dev/null 2>&1; then
-                echo "  停止 ${service} (PID: ${pid})"
-                kill $pid 2>/dev/null || true
-            fi
-            rm -f logs/${service}.pid
-        fi
-    done
-    sleep 1
-}
-
-# 启动服务
-start_service() {
-    local service=$1
-    local port=$2
-    local extra_args=$3
-    
-    echo -e "${YELLOW}启动 ${service} (端口 ${port})...${NC}"
-    
-    local port_args=""
-    if [ "${service}" = "keystone" ]; then
-        port_args="--port ${KEYSTONE_PORT} --admin-port 35357 --bootstrap-admin-user-password ${ADMIN_PASSWORD}"
-    else
-        port_args="--port ${port}"
-    fi
-    
-    local sql_args=""
-    if [ "${service}" != "apigateway" ]; then
-        sql_args="--sql-connection \"${SQL_CONNECTION}\" --auto-sync-table"
-    fi
-    
-    eval "_output/bin/${service} \
-        --config /dev/null \
-        ${sql_args} \
-        ${port_args} \
-        ${extra_args} \
-        > logs/${service}.log 2>&1 &"
-    
-    echo $! > logs/${service}.pid
-    echo -e "${GREEN}  ✓ ${service} 已启动 (PID: $!)${NC}"
-}
-
-# 等待服务就绪
-wait_for_service() {
-    local service=$1
-    local url=$2
-    local max_wait=${3:-30}
-    
-    echo -e "${YELLOW}等待 ${service} 就绪...${NC}"
-    for i in $(seq 1 $max_wait); do
-        if curl -s "$url" > /dev/null 2>&1; then
-            echo -e "${GREEN}  ✓ ${service} 已就绪${NC}"
-            return 0
-        fi
-        sleep 1
-    done
-    echo -e "${RED}  ✗ ${service} 启动超时${NC}"
-    return 1
-}
-
-# 主流程
-main() {
-    # 停止已运行的服务
-    stop_existing
-    
-    # 检查并编译所有服务
-    echo -e "${BLUE}检查服务二进制文件...${NC}"
-    check_binary keystone
-    check_binary region
-    check_binary glance
-    check_binary yunionconf
-    check_binary scheduledtask
-    check_binary apigateway
-    
-    # 启动 Keystone
-    start_service keystone ${KEYSTONE_PORT} ""
-    wait_for_service "Keystone" "http://127.0.0.1:${KEYSTONE_PORT}/v3" 30
-    
-    # 创建 glance 数据目录
-    mkdir -p /tmp/glance_data
-    
-    # 启动其他服务
-    start_service region ${REGION_PORT} "${COMMON_ARGS}"
-    start_service glance ${GLANCE_PORT} "${COMMON_ARGS} --filesystem-store-datadir /tmp/glance_data"
-    start_service yunionconf ${YUNIONCONF_PORT} "${COMMON_ARGS}"
-    start_service scheduledtask ${SCHEDULEDTASK_PORT} "${COMMON_ARGS}"
-    
-    # APIGateway 特殊处理
-    echo -e "${YELLOW}启动 APIGateway (端口 ${APIGATEWAY_PORT})...${NC}"
-    _output/bin/apigateway \
-        --config /dev/null \
-        --port ${APIGATEWAY_PORT} \
-        ${COMMON_ARGS} \
-        --default-region ${REGION_NAME} \
-        > logs/apigateway.log 2>&1 &
-    echo $! > logs/apigateway.pid
-    echo -e "${GREEN}  ✓ APIGateway 已启动 (PID: $!)${NC}"
-    
-    # 等待所有服务就绪
-    echo ""
-    echo -e "${YELLOW}等待所有服务就绪...${NC}"
-    sleep 3
-    
-    # 打印状态
-    echo ""
-    echo -e "${GREEN}========================================${NC}"
-    echo -e "${GREEN}  服务启动完成!${NC}"
-    echo -e "${GREEN}========================================${NC}"
-    echo ""
-    echo -e "${BLUE}服务状态:${NC}"
-    for service in keystone region glance yunionconf scheduledtask apigateway; do
-        if [ -f "logs/${service}.pid" ]; then
-            pid=$(cat logs/${service}.pid)
-            if ps -p $pid > /dev/null 2>&1; then
-                echo -e "  ${GREEN}✓${NC} ${service} (PID: ${pid})"
-            else
-                echo -e "  ${RED}✗${NC} ${service} (已停止)"
-            fi
-        fi
-    done
-    echo ""
-    echo -e "${BLUE}访问地址:${NC}"
-    echo "  APIGateway: http://127.0.0.1:${APIGATEWAY_PORT}"
-    echo "  Frontend:   http://localhost:8080"
-    echo ""
-    echo -e "${BLUE}常用命令:${NC}"
-    echo "  查看日志: tail -f logs/*.log"
-    echo "  停止服务: ./stop-backend.sh"
-    echo ""
-}
-
-# 处理命令行参数
-while [[ $# -gt 0 ]]; do
-    case $1 in
-        --help)
-            echo "Cloudpods 开发环境启动脚本"
-            echo ""
-            echo "用法: $0 [选项]"
-            echo ""
-            echo "选项:"
-            echo "  --help    显示帮助信息"
-            echo ""
-            echo "说明:"
-            echo "  此脚本用于日常开发,不重置数据库,只启动服务。"
-            echo "  如果是全新部署,请使用 init-cloudpods.sh"
-            echo ""
-            exit 0
-            ;;
-        *)
-            echo "未知选项: $1"
-            exit 1
-            ;;
-    esac
-done
-
-main

+ 0 - 39
backend/stop-backend.sh

@@ -1,39 +0,0 @@
-#!/bin/bash
-
-# 后端服务停止脚本
-
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-NC='\033[0m'
-
-echo -e "${YELLOW}停止后端服务...${NC}"
-
-stop_service() {
-    local service=$1
-    local pidfile="logs/${service}.pid"
-    
-    if [ -f "$pidfile" ]; then
-        pid=$(cat "$pidfile")
-        if ps -p $pid > /dev/null 2>&1; then
-            echo "停止 ${service} (PID: ${pid})"
-            kill $pid
-            rm "$pidfile"
-        else
-            echo "${service} 未运行"
-            rm "$pidfile"
-        fi
-    else
-        echo "${service} PID 文件不存在"
-    fi
-}
-
-# 停止所有服务
-stop_service apigateway
-stop_service scheduledtask
-stop_service yunionconf
-stop_service glance
-stop_service region
-stop_service keystone
-
-echo -e "${GREEN}✓ 后端服务已停止${NC}"

+ 291 - 15
frontend/pnpm-lock.yaml

@@ -11,6 +11,12 @@ importers:
 
   .:
     dependencies:
+      '@interactjs/interactjs':
+        specifier: ^1.10.27
+        version: 1.10.27
+      '@vue/babel-helper-vue-jsx-merge-props':
+        specifier: ^1.4.0
+        version: 1.4.0
       '@wangeditor/editor':
         specifier: ^5.1.23
         version: 5.1.23
@@ -35,6 +41,9 @@ importers:
       chalk:
         specifier: ^4.1.0
         version: 4.1.2
+      classnames:
+        specifier: ^2.5.1
+        version: 2.5.1
       clipboard:
         specifier: ^2.0.11
         version: 2.0.11
@@ -47,6 +56,9 @@ importers:
       core-js:
         specifier: ^3.6.5
         version: 3.49.0
+      cropperjs:
+        specifier: 1.5.13
+        version: 1.5.13
       crypto-js:
         specifier: ^4.0.0
         version: 4.2.0
@@ -128,6 +140,9 @@ importers:
       socket.io-client:
         specifier: ^2.3.0
         version: 2.5.0
+      sortablejs:
+        specifier: ^1.15.7
+        version: 1.15.7
       uplot:
         specifier: 1.5.2
         version: 1.5.2
@@ -142,7 +157,7 @@ importers:
         version: 4.2.0
       vue-grid-layout:
         specifier: ^2.3.7
-        version: 2.4.0(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+        version: 2.4.0(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
       vue-i18n:
         specifier: ^8.14.0
         version: 8.28.2(vue@2.7.16)
@@ -860,6 +875,12 @@ packages:
       '@interactjs/core': 1.10.2
       '@interactjs/utils': 1.10.2
 
+  '@interactjs/actions@1.10.27':
+    resolution: {integrity: sha512-FCRg5KwB+stkPcAMx/Cn0fgGP6p4LyMX9S/Upcn/W+hpYme31bPi54PCqmOebzz6myTthN6zFf9jMyLOqtI/gg==}
+    peerDependencies:
+      '@interactjs/core': 1.10.27
+      '@interactjs/utils': 1.10.27
+
   '@interactjs/arrange@1.10.2':
     resolution: {integrity: sha512-pPLA9o4RWMFN0VfalklOFSRLL4WqqXcD9no4XEuqV00goZPCxLBbMTztaWwnutlRy7evtOhUjUH+pZVsS+dZ4Q==}
 
@@ -868,12 +889,23 @@ packages:
     peerDependencies:
       '@interactjs/utils': 1.10.2
 
+  '@interactjs/auto-scroll@1.10.27':
+    resolution: {integrity: sha512-zPg5TnVsZv+9Hnt4qnbxLvBMf+rIWHkoJVoSETEbLNaj90C8hIyr0pVwukSUySSgDhCgQ7np0f3pg4INLq9beQ==}
+    peerDependencies:
+      '@interactjs/utils': 1.10.27
+
   '@interactjs/auto-start@1.10.2':
     resolution: {integrity: sha512-nZudj8VzJzz+uEyDHqXwtKpvUYr+Oj1+xBrJEu21CywroHQWM2J4fCIiCgeCo3d5/p/TrzFk5b+YfAWePKiLxA==}
     peerDependencies:
       '@interactjs/core': 1.10.2
       '@interactjs/utils': 1.10.2
 
+  '@interactjs/auto-start@1.10.27':
+    resolution: {integrity: sha512-ECLBO/nxmaF1knncJKIE5F7la3KKRgEkn0Cu2JTPOYj9xy/LpfYElo3wkRHsodgOqF651nR70GK2/IzPR2lO9A==}
+    peerDependencies:
+      '@interactjs/core': 1.10.27
+      '@interactjs/utils': 1.10.27
+
   '@interactjs/clone@1.10.2':
     resolution: {integrity: sha512-XzA8BRHSCwvysOegZ1kopg+IJF3erh4qzY6DRoZsIJovKAXawoa176E58IAzDbgYPJ2yoaSGT+XyzT2C0wa3pQ==}
 
@@ -882,9 +914,20 @@ packages:
     peerDependencies:
       '@interactjs/utils': 1.10.2
 
+  '@interactjs/core@1.10.27':
+    resolution: {integrity: sha512-SliUr/3ZbLAdED8LokzYzWHWMdCB5Cq+UnpXuRy+BIod1j97m4IUFf/D1iIKUBBjBcucgXbz28z96WnenVCB7Q==}
+    peerDependencies:
+      '@interactjs/utils': 1.10.27
+
   '@interactjs/dev-tools@1.10.2':
     resolution: {integrity: sha512-aAd9NgTAGA3yVdFCYcAAYrM4TYQFuVqEvsF+xj+g5SlGyrJ7+GTjPZ2rScOyAsABY4Tz64L2pXvWmXMG87dncA==}
 
+  '@interactjs/dev-tools@1.10.27':
+    resolution: {integrity: sha512-YolmBwRaKH1gWbvyLeV3m5QSwtD38lOZnCBA87PCAlcd9PQAC2gb03fEPeEyD336bE20oLB8f0WZt4Wre+afiw==}
+    peerDependencies:
+      '@interactjs/modifiers': 1.10.27
+      '@interactjs/utils': 1.10.27
+
   '@interactjs/feedback@1.10.2':
     resolution: {integrity: sha512-XlcoICGrFeUwwRtDgOpstOOvlU42WZoEg7gJHK3LwF7j0IctPd1+3blXofFlBeVvodle8MvUMepm5CRXz741fA==}
 
@@ -895,18 +938,37 @@ packages:
       '@interactjs/modifiers': 1.10.2
       '@interactjs/utils': 1.10.2
 
+  '@interactjs/inertia@1.10.27':
+    resolution: {integrity: sha512-S/SVj/M0D+wWWPVXHcXN/YUWOK51LFJsEA+CTgVnFhlSU04+1FUvNLwilCZcHgECu1RJxZNKDwZysDATg+r8jQ==}
+    peerDependencies:
+      '@interactjs/core': 1.10.27
+      '@interactjs/modifiers': 1.10.27
+      '@interactjs/utils': 1.10.27
+
   '@interactjs/interact@1.10.2':
     resolution: {integrity: sha512-Ms5uVCY9IobVYpQyBnBdkP6Bk6iDY7TkC7GupsdUPUxzAvYSQCTEAGr/1PwxSrSS6dN/8O8TuyUWPbCaylr/JA==}
 
+  '@interactjs/interact@1.10.27':
+    resolution: {integrity: sha512-XdH3A2UUzjEFGGJgFuJlhiz99tE8jB8xNh/DmnoMuL6uOQPxNA+sWRnzEVjG0+zY2P3/dbhEpi4Cn3FLPzydwA==}
+
   '@interactjs/interactjs@1.10.2':
     resolution: {integrity: sha512-OwLl70af6lfZOOg/bvWKSNm1DS1nDI72QnzDYljSKfc2D8stqLIGDO1wPY2rhZudUG5q3t50EhmMUQF76yll/g==}
 
+  '@interactjs/interactjs@1.10.27':
+    resolution: {integrity: sha512-UwhfUZMZVXUY72efPABuKSBz1sUY+r+49v8t6Ku9o5Jq76AKg9mwmdGszIlOn3ppnFDDjvtzK/8TL+Sbd0EQEA==}
+
   '@interactjs/modifiers@1.10.2':
     resolution: {integrity: sha512-3wYEucvZF2NTIslnVIKw5MWhkn9LM42cGCQaC19o3LZeWnbps7NnHJCyQp6zylJrCbwt7f+CSt4Oj2/s0f6XEA==}
     peerDependencies:
       '@interactjs/core': 1.10.2
       '@interactjs/utils': 1.10.2
 
+  '@interactjs/modifiers@1.10.27':
+    resolution: {integrity: sha512-ei/qfoQ+9/8k6WzNzdNqHI6cWkIV576N4Ap16r5CoqOWwhA6Xzj3OMHf1g0t1O4eSq2HdJsVJn3eLNfw9HsbeQ==}
+    peerDependencies:
+      '@interactjs/core': 1.10.27
+      '@interactjs/utils': 1.10.27
+
   '@interactjs/multi-target@1.10.2':
     resolution: {integrity: sha512-O2GiIqgZBzjAVTOpL8doTnAcM9AtM3+H/Bb+An12wWKtNutVK7JbqUAO2nYueOk55/PP3yDLY9Qdr15RJns3lQ==}
 
@@ -916,12 +978,24 @@ packages:
       '@interactjs/core': 1.10.2
       '@interactjs/utils': 1.10.2
 
+  '@interactjs/offset@1.10.27':
+    resolution: {integrity: sha512-AezsLiuK+Qv4jXdYuRa65HJ2pMFMZPlqiAep6ZRLwhP9HE7O75c0EAm+gfx+dpPrHNHs6J9LaiKSZl+B+A2qAw==}
+    peerDependencies:
+      '@interactjs/core': 1.10.27
+      '@interactjs/utils': 1.10.27
+
   '@interactjs/pointer-events@1.10.2':
     resolution: {integrity: sha512-O8s3N399hkGIzWGlcJVy0LJyDn5YWDh6XKjyowh/QivtlZSWPY8eglmlN2uZX0lmiqUYghbKI4CpQYP/cE0ZDA==}
     peerDependencies:
       '@interactjs/core': 1.10.2
       '@interactjs/utils': 1.10.2
 
+  '@interactjs/pointer-events@1.10.27':
+    resolution: {integrity: sha512-Yo5SS6PhWfC93gHNxnwwW0wvebo5hSYJKGaSnAHO4f9Lh25yibecMnmPBmiEfWVcdMboK/kXrme43mHQaRegVg==}
+    peerDependencies:
+      '@interactjs/core': 1.10.27
+      '@interactjs/utils': 1.10.27
+
   '@interactjs/react@1.10.2':
     resolution: {integrity: sha512-JXzPdANft+W2vq3SCSzprCwom5UuC8TaiAAhVdt8R+/P6xHbOeAX93XLS5YmDwT8e0Zh9J9jYvz55tkTdwjFZQ==}
 
@@ -931,17 +1005,31 @@ packages:
       '@interactjs/core': 1.10.2
       '@interactjs/utils': 1.10.2
 
+  '@interactjs/reflow@1.10.27':
+    resolution: {integrity: sha512-Msm0QdYFr40oSsPFxyCR3dHN/pQx34k7QSkdN1uIsUn/drrm+YSFvrvVOu99DFOwr7gTThr5vNe06Sz4vubTSA==}
+    peerDependencies:
+      '@interactjs/core': 1.10.27
+      '@interactjs/utils': 1.10.27
+
   '@interactjs/snappers@1.10.2':
     resolution: {integrity: sha512-wQ41Vn5GRn6VavjIEUtTkd9d5QgdKgC4+CPW0fjKkiQclLBmaic7VibNETO8twN0Jx5e73EoPj9K2nAVHIA0hA==}
     peerDependencies:
       '@interactjs/utils': 1.10.2
 
+  '@interactjs/snappers@1.10.27':
+    resolution: {integrity: sha512-HZLZ0XSi6HI08OmTv/HKG6AltQoaKAALLQ+KDW92utj3XSgw7oren0KsWUKPhaPg3Av7R1jFQd08s+uafqIlLw==}
+    peerDependencies:
+      '@interactjs/utils': 1.10.27
+
   '@interactjs/types@1.10.2':
     resolution: {integrity: sha512-l0T1bU8OHRv716ztQOYwP+K7b/lA76C0T3r/cdabbUv6CKeAFdFRRFlmNxYOM36SxMGWAiq5VWrN3SeXlB7Fow==}
 
   '@interactjs/utils@1.10.2':
     resolution: {integrity: sha512-sOr+pu7XGAN4qv+ikajMo3RJygbkbMLegmx0Tv5Qf6e80sF42FjkmHeMGuV7fL98nwat0VmDiWerOFBnKctXow==}
 
+  '@interactjs/utils@1.10.27':
+    resolution: {integrity: sha512-+qfLOio2OxQqg1cXSnRaCl+N8MQDQLDS9w+aOGxH8YLAhIMyt7Asxx/46//sT8orgsi16pmlBPtngPHT9s8zKw==}
+
   '@interactjs/vue@1.10.2':
     resolution: {integrity: sha512-msLdc42DFsCPQZt6YBGZrw8Ro23kQcNnj+iLz2OUQcOrp/lma7WjorUuAwwfyFna2DevLtiYlMLbT0dpO/cVhg==}
 
@@ -1386,6 +1474,20 @@ packages:
       html-webpack-plugin: '>=2.26.0'
       webpack: '>=4.0.0'
 
+  '@vue/reactivity@3.5.32':
+    resolution: {integrity: sha512-/ORasxSGvZ6MN5gc+uE364SxFdJ0+WqVG0CENXaGW58TOCdrAW76WWaplDtECeS1qphvtBZtR+3/o1g1zL4xPQ==}
+
+  '@vue/runtime-core@3.5.32':
+    resolution: {integrity: sha512-pDrXCejn4UpFDFmMd27AcJEbHaLemaE5o4pbb7sLk79SRIhc6/t34BQA7SGNgYtbMnvbF/HHOftYBgFJtUoJUQ==}
+
+  '@vue/runtime-dom@3.5.32':
+    resolution: {integrity: sha512-1CDVv7tv/IV13V8Nip1k/aaObVbWqRlVCVezTwx3K07p7Vxossp5JU1dcPNhJk3w347gonIUT9jQOGutyJrSVQ==}
+
+  '@vue/server-renderer@3.5.32':
+    resolution: {integrity: sha512-IOjm2+JQwRFS7W28HNuJeXQle9KdZbODFY7hFGVtnnghF51ta20EWAZJHX+zLGtsHhaU6uC9BGPV52KVpYryMQ==}
+    peerDependencies:
+      vue: 3.5.32
+
   '@vue/shared@3.5.32':
     resolution: {integrity: sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==}
 
@@ -2703,8 +2805,8 @@ packages:
   create-hmac@1.1.7:
     resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==}
 
-  cropperjs@1.6.2:
-    resolution: {integrity: sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==}
+  cropperjs@1.5.13:
+    resolution: {integrity: sha512-by7jKAo73y5/Do0K6sxdTKHgndY0NMjG2bEdgeJxycbcmHuCiMXqw8sxy5C5Y5WTOTcDGmbT7Sr5CgKOXR06OA==}
 
   cross-spawn@5.1.0:
     resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
@@ -6538,6 +6640,9 @@ packages:
   sortablejs@1.10.2:
     resolution: {integrity: sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==}
 
+  sortablejs@1.15.7:
+    resolution: {integrity: sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==}
+
   source-list-map@2.0.1:
     resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==}
 
@@ -7319,6 +7424,14 @@ packages:
     resolution: {integrity: sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==}
     deprecated: Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.
 
+  vue@3.5.32:
+    resolution: {integrity: sha512-vM4z4Q9tTafVfMAK7IVzmxg34rSzTFMyIe0UUEijUCkn9+23lj0WRfA83dg7eQZIUlgOSGrkViIaCfqSAUXsMw==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
   vuedraggable@2.24.3:
     resolution: {integrity: sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==}
 
@@ -8394,6 +8507,20 @@ snapshots:
     optionalDependencies:
       '@interactjs/interact': 1.10.2
 
+  '@interactjs/actions@1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+      '@interactjs/utils': 1.10.27
+    optionalDependencies:
+      '@interactjs/interact': 1.10.2
+
+  '@interactjs/actions@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+      '@interactjs/utils': 1.10.27
+    optionalDependencies:
+      '@interactjs/interact': 1.10.27
+
   '@interactjs/arrange@1.10.2': {}
 
   '@interactjs/auto-scroll@1.10.2(@interactjs/utils@1.10.2)':
@@ -8402,6 +8529,18 @@ snapshots:
     optionalDependencies:
       '@interactjs/interact': 1.10.2
 
+  '@interactjs/auto-scroll@1.10.2(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/utils': 1.10.27
+    optionalDependencies:
+      '@interactjs/interact': 1.10.2
+
+  '@interactjs/auto-scroll@1.10.27(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/utils': 1.10.27
+    optionalDependencies:
+      '@interactjs/interact': 1.10.27
+
   '@interactjs/auto-start@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)':
     dependencies:
       '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
@@ -8409,35 +8548,77 @@ snapshots:
     optionalDependencies:
       '@interactjs/interact': 1.10.2
 
+  '@interactjs/auto-start@1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+      '@interactjs/utils': 1.10.27
+    optionalDependencies:
+      '@interactjs/interact': 1.10.2
+
+  '@interactjs/auto-start@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+      '@interactjs/utils': 1.10.27
+    optionalDependencies:
+      '@interactjs/interact': 1.10.27
+
   '@interactjs/clone@1.10.2': {}
 
   '@interactjs/core@1.10.2(@interactjs/utils@1.10.2)':
     dependencies:
       '@interactjs/utils': 1.10.2
 
+  '@interactjs/core@1.10.27(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/utils': 1.10.27
+
   '@interactjs/dev-tools@1.10.2':
     dependencies:
       '@interactjs/utils': 1.10.2
     optionalDependencies:
       '@interactjs/interact': 1.10.2
 
+  '@interactjs/dev-tools@1.10.27(@interactjs/modifiers@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/modifiers': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+      '@interactjs/utils': 1.10.27
+    optionalDependencies:
+      '@interactjs/interact': 1.10.27
+      vue: 3.5.32
+    transitivePeerDependencies:
+      - typescript
+
   '@interactjs/feedback@1.10.2': {}
 
   '@interactjs/inertia@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/modifiers@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)':
     dependencies:
       '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
-      '@interactjs/modifiers': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+      '@interactjs/modifiers': 1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
       '@interactjs/offset': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
       '@interactjs/utils': 1.10.2
     optionalDependencies:
       '@interactjs/interact': 1.10.2
 
+  '@interactjs/inertia@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/modifiers@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+      '@interactjs/modifiers': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+      '@interactjs/offset': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+      '@interactjs/utils': 1.10.27
+    optionalDependencies:
+      '@interactjs/interact': 1.10.27
+
   '@interactjs/interact@1.10.2':
     dependencies:
       '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
       '@interactjs/types': 1.10.2
       '@interactjs/utils': 1.10.2
 
+  '@interactjs/interact@1.10.27':
+    dependencies:
+      '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+      '@interactjs/utils': 1.10.27
+
   '@interactjs/interactjs@1.10.2':
     dependencies:
       '@interactjs/actions': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
@@ -8450,7 +8631,7 @@ snapshots:
       '@interactjs/feedback': 1.10.2
       '@interactjs/inertia': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/modifiers@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
       '@interactjs/interact': 1.10.2
-      '@interactjs/modifiers': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+      '@interactjs/modifiers': 1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
       '@interactjs/multi-target': 1.10.2
       '@interactjs/offset': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
       '@interactjs/pointer-events': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
@@ -8459,14 +8640,39 @@ snapshots:
       '@interactjs/utils': 1.10.2
       '@interactjs/vue': 1.10.2
 
-  '@interactjs/modifiers@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)':
+  '@interactjs/interactjs@1.10.27':
+    dependencies:
+      '@interactjs/actions': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+      '@interactjs/auto-scroll': 1.10.27(@interactjs/utils@1.10.27)
+      '@interactjs/auto-start': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+      '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+      '@interactjs/dev-tools': 1.10.27(@interactjs/modifiers@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+      '@interactjs/inertia': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/modifiers@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+      '@interactjs/interact': 1.10.27
+      '@interactjs/modifiers': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+      '@interactjs/offset': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+      '@interactjs/pointer-events': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+      '@interactjs/reflow': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+      '@interactjs/utils': 1.10.27
+    transitivePeerDependencies:
+      - typescript
+
+  '@interactjs/modifiers@1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
     dependencies:
-      '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
+      '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
       '@interactjs/snappers': 1.10.2(@interactjs/utils@1.10.2)
-      '@interactjs/utils': 1.10.2
+      '@interactjs/utils': 1.10.27
     optionalDependencies:
       '@interactjs/interact': 1.10.2
 
+  '@interactjs/modifiers@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+      '@interactjs/snappers': 1.10.27(@interactjs/utils@1.10.27)
+      '@interactjs/utils': 1.10.27
+    optionalDependencies:
+      '@interactjs/interact': 1.10.27
+
   '@interactjs/multi-target@1.10.2': {}
 
   '@interactjs/offset@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)':
@@ -8476,6 +8682,13 @@ snapshots:
     optionalDependencies:
       '@interactjs/interact': 1.10.2
 
+  '@interactjs/offset@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+      '@interactjs/utils': 1.10.27
+    optionalDependencies:
+      '@interactjs/interact': 1.10.27
+
   '@interactjs/pointer-events@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)':
     dependencies:
       '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
@@ -8483,6 +8696,13 @@ snapshots:
     optionalDependencies:
       '@interactjs/interact': 1.10.2
 
+  '@interactjs/pointer-events@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+      '@interactjs/utils': 1.10.27
+    optionalDependencies:
+      '@interactjs/interact': 1.10.27
+
   '@interactjs/react@1.10.2': {}
 
   '@interactjs/reflow@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)':
@@ -8492,14 +8712,29 @@ snapshots:
     optionalDependencies:
       '@interactjs/interact': 1.10.2
 
+  '@interactjs/reflow@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+      '@interactjs/utils': 1.10.27
+    optionalDependencies:
+      '@interactjs/interact': 1.10.27
+
   '@interactjs/snappers@1.10.2(@interactjs/utils@1.10.2)':
     dependencies:
       '@interactjs/utils': 1.10.2
 
+  '@interactjs/snappers@1.10.27(@interactjs/utils@1.10.27)':
+    dependencies:
+      '@interactjs/utils': 1.10.27
+    optionalDependencies:
+      '@interactjs/interact': 1.10.27
+
   '@interactjs/types@1.10.2': {}
 
   '@interactjs/utils@1.10.2': {}
 
+  '@interactjs/utils@1.10.27': {}
+
   '@interactjs/vue@1.10.2': {}
 
   '@intervolga/optimize-cssnano-plugin@1.0.6(webpack@4.47.0)':
@@ -8600,7 +8835,9 @@ snapshots:
       source-map: 0.6.1
       string-length: 2.0.0
     transitivePeerDependencies:
+      - bufferutil
       - supports-color
+      - utf-8-validate
 
   '@jest/source-map@24.9.0':
     dependencies:
@@ -9372,6 +9609,32 @@ snapshots:
       html-webpack-plugin: 3.2.0(webpack@4.47.0)
       webpack: 4.47.0
 
+  '@vue/reactivity@3.5.32':
+    dependencies:
+      '@vue/shared': 3.5.32
+    optional: true
+
+  '@vue/runtime-core@3.5.32':
+    dependencies:
+      '@vue/reactivity': 3.5.32
+      '@vue/shared': 3.5.32
+    optional: true
+
+  '@vue/runtime-dom@3.5.32':
+    dependencies:
+      '@vue/reactivity': 3.5.32
+      '@vue/runtime-core': 3.5.32
+      '@vue/shared': 3.5.32
+      csstype: 3.2.3
+    optional: true
+
+  '@vue/server-renderer@3.5.32(vue@3.5.32)':
+    dependencies:
+      '@vue/compiler-ssr': 3.5.32
+      '@vue/shared': 3.5.32
+      vue: 3.5.32
+    optional: true
+
   '@vue/shared@3.5.32': {}
 
   '@vue/test-utils@1.3.6(vue-template-compiler@2.7.16)(vue@2.7.16)':
@@ -10785,7 +11048,7 @@ snapshots:
       safe-buffer: 5.2.1
       sha.js: 2.4.12
 
-  cropperjs@1.6.2: {}
+  cropperjs@1.5.13: {}
 
   cross-spawn@5.1.0:
     dependencies:
@@ -13091,7 +13354,9 @@ snapshots:
       pretty-format: 24.9.0
       throat: 4.1.0
     transitivePeerDependencies:
+      - bufferutil
       - supports-color
+      - utf-8-validate
 
   jest-leak-detector@24.9.0:
     dependencies:
@@ -15435,6 +15700,8 @@ snapshots:
 
   sortablejs@1.10.2: {}
 
+  sortablejs@1.15.7: {}
+
   source-list-map@2.0.1: {}
 
   source-map-js@1.2.1: {}
@@ -16236,7 +16503,7 @@ snapshots:
 
   vue-cropperjs@4.2.0:
     dependencies:
-      cropperjs: 1.6.2
+      cropperjs: 1.5.13
 
   vue-eslint-parser@7.11.0(eslint@6.8.0):
     dependencies:
@@ -16251,14 +16518,14 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  vue-grid-layout@2.4.0(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2):
+  vue-grid-layout@2.4.0(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27):
     dependencies:
-      '@interactjs/actions': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
-      '@interactjs/auto-scroll': 1.10.2(@interactjs/utils@1.10.2)
-      '@interactjs/auto-start': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+      '@interactjs/actions': 1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+      '@interactjs/auto-scroll': 1.10.2(@interactjs/utils@1.10.27)
+      '@interactjs/auto-start': 1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
       '@interactjs/dev-tools': 1.10.2
       '@interactjs/interactjs': 1.10.2
-      '@interactjs/modifiers': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+      '@interactjs/modifiers': 1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
       element-resize-detector: 1.2.4
     transitivePeerDependencies:
       - '@interactjs/core'
@@ -16395,6 +16662,15 @@ snapshots:
       '@vue/compiler-sfc': 2.7.16
       csstype: 3.2.3
 
+  vue@3.5.32:
+    dependencies:
+      '@vue/compiler-dom': 3.5.32
+      '@vue/compiler-sfc': 3.5.32
+      '@vue/runtime-dom': 3.5.32
+      '@vue/server-renderer': 3.5.32(vue@3.5.32)
+      '@vue/shared': 3.5.32
+    optional: true
+
   vuedraggable@2.24.3:
     dependencies:
       sortablejs: 1.10.2