Browse Source

first commit

周玉环 1 day ago
parent
commit
2f020d9293

+ 550 - 0
backend/init-cloudpods.sh

@@ -0,0 +1,550 @@
+#!/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}
+    )
+    
+    # 注册服务和 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

+ 38 - 0
backend/init-config.env

@@ -0,0 +1,38 @@
+# 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

+ 50 - 0
backend/init-policies.sql

@@ -0,0 +1,50 @@
+-- 初始化 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 - 122
backend/start-backend.sh

@@ -1,122 +0,0 @@
-#!/bin/bash
-
-# 后端服务启动脚本
-
-set -e
-
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-NC='\033[0m'
-
-echo -e "${GREEN}=== 启动后端服务 ===${NC}"
-
-# 加载环境变量
-if [ -f "../.env.local" ]; then
-    echo -e "${YELLOW}加载配置...${NC}"
-    export $(cat ../.env.local | grep -v '^#' | xargs)
-fi
-
-# 数据库配置
-DB_HOST=${DB_HOST:-127.0.0.1}
-DB_PORT=${DB_PORT:-3306}
-DB_USER=${DB_USER:-root}
-DB_PASSWORD=${DB_PASSWORD:-}
-
-# 构建数据库连接字符串
-if [ -z "$DB_PASSWORD" ]; then
-    SQL_CONNECTION="mysql+pymysql://${DB_USER}@${DB_HOST}:${DB_PORT}/yunioncloud?charset=utf8mb4"
-else
-    SQL_CONNECTION="mysql+pymysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/yunioncloud?charset=utf8mb4"
-fi
-
-echo "数据库连接: ${DB_USER}@${DB_HOST}:${DB_PORT}/yunioncloud"
-
-# 创建日志目录
-mkdir -p logs
-
-# 检查二进制文件是否存在
-check_binary() {
-    local service=$1
-    if [ ! -f "_output/bin/${service}" ]; then
-        echo -e "${YELLOW}${service} 未编译,开始编译...${NC}"
-        # 忽略 git 错误,只关注编译结果
-        make cmd/${service} 2>&1 | grep -v "fatal: " | grep -v "error: malformed" || true
-    fi
-}
-
-# 启动服务
-start_service() {
-    local service=$1
-    local port=$2
-    local extra_args=$3
-    
-    echo -e "${YELLOW}启动 ${service} (端口 ${port})...${NC}"
-    
-    ./_output/bin/${service} \
-        --config /dev/null \
-        --sql-connection "$SQL_CONNECTION" \
-        --port ${port} \
-        --enable-ssl=false \
-        ${extra_args} \
-        > logs/${service}.log 2>&1 &
-    
-    echo $! > logs/${service}.pid
-    echo -e "${GREEN}✓ ${service} 已启动 (PID: $!)${NC}"
-}
-
-# 停止已运行的服务
-stop_existing() {
-    echo -e "${YELLOW}检查并停止已运行的服务...${NC}"
-    for service in keystone region glance apigateway; 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 logs/${service}.pid
-        fi
-    done
-    sleep 1
-}
-
-# 主流程
-main() {
-    stop_existing
-    
-    # 检查并编译服务
-    check_binary keystone
-    check_binary region
-    check_binary glance
-    check_binary apigateway
-    
-    # 启动服务
-    start_service keystone 35357 ""
-    sleep 2
-    
-    start_service region 30889 "--auth-url http://127.0.0.1:35357/v3"
-    sleep 2
-    
-    start_service glance 9292 "--auth-url http://127.0.0.1:35357/v3"
-    sleep 2
-    
-    start_service apigateway 3000 "--auth-url http://127.0.0.1:35357/v3"
-    sleep 1
-    
-    echo ""
-    echo -e "${GREEN}=== 后端服务启动成功 ===${NC}"
-    echo ""
-    echo "服务地址:"
-    echo "  Keystone (认证):  http://localhost:35357"
-    echo "  Region (计算):    http://localhost:30889"
-    echo "  Glance (镜像):    http://localhost:9292"
-    echo "  APIGateway:       http://localhost:3000"
-    echo ""
-    echo "日志目录: backend/logs/"
-    echo "查看日志: tail -f logs/*.log"
-    echo "停止服务: ./stop-backend.sh"
-    echo ""
-}
-
-main

+ 229 - 0
backend/start-dev.sh

@@ -0,0 +1,229 @@
+#!/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

+ 2 - 0
backend/stop-backend.sh

@@ -30,6 +30,8 @@ stop_service() {
 
 # 停止所有服务
 stop_service apigateway
+stop_service scheduledtask
+stop_service yunionconf
 stop_service glance
 stop_service region
 stop_service keystone

+ 5 - 0
frontend/package.json

@@ -13,6 +13,8 @@
     "deploy": "node ./upload/upload.js"
   },
   "dependencies": {
+    "@interactjs/interactjs": "^1.10.27",
+    "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0",
     "@wangeditor/editor": "^5.1.23",
     "@wangeditor/editor-for-vue": "^1.0.2",
     "ajv": "^6.12.3",
@@ -21,10 +23,12 @@
     "ant-design-vue": "^1.6.4",
     "axios": "^0.19.0",
     "chalk": "^4.1.0",
+    "classnames": "^2.5.1",
     "clipboard": "^2.0.11",
     "codemirror": "^5.52.2",
     "compressing": "^1.5.1",
     "core-js": "^3.6.5",
+    "cropperjs": "1.5.13",
     "crypto-js": "^4.0.0",
     "custom-protocol-detection": "^1.0.1",
     "echarts": "^4.4.0",
@@ -52,6 +56,7 @@
     "resize-observer-polyfill": "^1.5.1",
     "shelljs": "^0.8.4",
     "socket.io-client": "^2.3.0",
+    "sortablejs": "^1.15.7",
     "uplot": "1.5.2",
     "v-charts": "^1.19.0",
     "vue": "^2.7.14",

+ 1 - 1
frontend/scope/layouts/Navbar/index.vue

@@ -267,7 +267,7 @@ export default {
     },
     globalSettingSetupKeys () {
       const { globalSetting } = this.$store.state
-      if (globalSetting && globalSetting.value) {
+      if (globalSetting && globalSetting.value && globalSetting.value.setupKeys) {
         return globalSetting.value.setupKeys
       }
       return []

+ 2 - 1
frontend/src/permission.js

@@ -137,7 +137,8 @@ router.beforeEach(async (to, from, next) => {
     // !hasGlobalServices && await store.dispatch('common/fetchGlobalServices')
     !hasMonitorResourceAlerts && await store.dispatch('monitor/loadMonitorResourceAlerts')
   } catch (error) {
-    throw error
+    // 忽略错误,确保页面可以正常渲染
+    console.warn('Initialization error:', error)
   } finally {
     const { canRenderDefaultLayout = true } = to.meta
     if (canRenderDefaultLayout) {

+ 8 - 4
frontend/src/store/modules/auth.js

@@ -394,7 +394,8 @@ export default {
         await commit('SET_CAPABILITY', data)
         return response.data
       } catch (error) {
-        throw error
+        // 忽略错误,避免阻塞页面渲染
+        console.warn('getCapabilities error:', error)
       }
     },
     async getStats ({ commit, state }) {
@@ -408,7 +409,8 @@ export default {
         await commit('SET_STATS', data)
         return response.data.data
       } catch (error) {
-        throw error
+        // 忽略错误,避免阻塞页面渲染
+        console.warn('getStats error:', error)
       }
     },
     async getPermission ({ commit, state }) {
@@ -418,7 +420,8 @@ export default {
         await commit('SET_PERMISSION', response.data)
         return response.data
       } catch (error) {
-        throw error
+        // 忽略错误,避免阻塞页面渲染
+        console.warn('getPermission error:', error)
       }
     },
     async getScopeResource ({ commit }) {
@@ -430,7 +433,8 @@ export default {
         await commit('SET_SCOPERESOURCE', data)
         return response.data
       } catch (error) {
-        throw error
+        // 忽略错误,避免阻塞页面渲染
+        console.warn('getScopeResource error:', error)
       }
     },
     async getRegions ({ commit, state }, params) {

+ 2 - 1
frontend/src/store/modules/common.js

@@ -267,7 +267,8 @@ export default {
           commit('SET_GLOBAL_SERVICE', data)
         }
       } catch (error) {
-        throw error
+        // 忽略错误,避免阻塞页面渲染
+        console.warn('fetchGlobalServices error:', error)
       }
     },
   },

+ 3 - 2
frontend/src/store/modules/monitor.js

@@ -23,12 +23,13 @@ export default {
           limit: 1,
           scope: getScopeFromCookie() || 'project',
           ...payload,
-          ignoreErrorStatusCode: [403],
+          ignoreErrorStatusCode: [403, 404],
         }
         const { data: { data = [] } } = await new Manager('monitorresourcealerts', 'v1').list({ params })
         commit('setMonitorResourceAlerts', data)
       } catch (error) {
-        throw error
+        // 忽略错误,避免阻塞页面渲染
+        commit('setMonitorResourceAlerts', [])
       }
     },
   },

+ 3 - 0
frontend/src/store/modules/scopedPolicy.js

@@ -49,6 +49,9 @@ export default {
         } else {
           return {}
         }
+      } catch (error) {
+        // 忽略错误,避免阻塞页面渲染
+        console.warn('scopedPolicy get error:', error)
       } finally {
         onFinally && onFinally()
       }