Files
WeKnora/scripts/dev.sh
wizardchen ef1047bf67 feat(parser): add OpenDataLoader, PaddleOCR-VL engines, and parser improvements
Introduce opendataloader and PaddleOCR-VL parser engines with tenant-level
settings UI, replace liteparse, and harden Excel/PPT/Markdown parsing.
Optional odl-hybrid sidecar stays local-build only and is excluded from
default dev-start and full profiles.
2026-06-03 12:29:13 +08:00

445 lines
14 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# 开发环境启动脚本 - 只启动基础设施app 和 frontend 需要手动在本地运行
# 设置颜色
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # 无颜色
# 获取项目根目录
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROJECT_ROOT="$( cd "$SCRIPT_DIR/.." && pwd )"
# 日志函数
log_info() {
printf "%b\n" "${BLUE}[INFO]${NC} $1"
}
log_success() {
printf "%b\n" "${GREEN}[SUCCESS]${NC} $1"
}
log_error() {
printf "%b\n" "${RED}[ERROR]${NC} $1"
}
log_warning() {
printf "%b\n" "${YELLOW}[WARNING]${NC} $1"
}
# 选择可用的 Docker Compose 命令
DOCKER_COMPOSE_BIN=""
DOCKER_COMPOSE_SUBCMD=""
detect_compose_cmd() {
if docker compose version &> /dev/null; then
DOCKER_COMPOSE_BIN="docker"
DOCKER_COMPOSE_SUBCMD="compose"
return 0
fi
if command -v docker-compose &> /dev/null; then
if docker-compose version &> /dev/null; then
DOCKER_COMPOSE_BIN="docker-compose"
DOCKER_COMPOSE_SUBCMD=""
return 0
fi
fi
return 1
}
# 显示帮助信息
show_help() {
printf "%b\n" "${GREEN}WeKnora 开发环境脚本${NC}"
echo "用法: $0 [命令] [选项]"
echo ""
echo "命令:"
echo " start 启动基础设施服务postgres, redis, docreader, langfuse"
echo " stop 停止所有服务"
echo " restart 重启所有服务"
echo " logs 查看服务日志"
echo " status 查看服务状态"
echo " app 启动后端应用(本地运行)"
echo " frontend 启动前端开发服务器(本地运行)"
echo " help 显示此帮助信息"
echo ""
echo "可选 Profile用于 start 命令):"
echo " --minio 启动 MinIO 对象存储"
echo " --qdrant 启动 Qdrant 向量数据库"
echo " --neo4j 启动 Neo4j 图数据库"
echo " --jaeger 启动 Jaeger 链路追踪"
echo " --dex 启动 DexOIDC 身份认证)"
echo " --langfuse 启动 Langfuse默认已开启"
echo " --no-langfuse 不启动 Langfuse"
echo " --odl-hybrid 启动 OpenDataLoader hybridDocling镜像较大按需启用"
echo " --full 启动所有可选服务(不含 odl-hybrid需另加 --odl-hybrid"
echo ""
echo "示例:"
echo " $0 start # 启动基础服务"
echo " $0 start --qdrant # 启动基础服务 + Qdrant"
echo " $0 start --qdrant --jaeger # 启动基础服务 + Qdrant + Jaeger"
echo " $0 start --dex # 启动基础服务 + Dex"
echo " $0 start --odl-hybrid # 启动基础服务 + OpenDataLoader hybrid"
echo " $0 start --full # 启动所有服务"
echo " make dev-start DEV_ARGS=--odl-hybrid # 同上Makefile 传参)"
echo " $0 app # 在另一个终端启动后端"
echo " $0 frontend # 在另一个终端启动前端"
}
# 检查 Docker
check_docker() {
if ! command -v docker &> /dev/null; then
log_error "未安装Docker请先安装Docker"
return 1
fi
if ! detect_compose_cmd; then
log_error "未检测到 Docker Compose"
return 1
fi
if ! docker info &> /dev/null; then
log_error "Docker服务未运行"
return 1
fi
return 0
}
# 检查 .env 是否启用了 hybrid 模式(用于 --odl-hybrid 启动后重建 docreader
_should_enable_odl_hybrid_from_env() {
local hybrid="${DOCREADER_ODL_HYBRID:-off}"
hybrid=$(printf '%s' "$hybrid" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')
case "$hybrid" in
off|"") return 1 ;;
*) return 0 ;;
esac
}
_enable_odl_hybrid_profile() {
PROFILES="$PROFILES --profile odl-hybrid"
ENABLED_SERVICES="$ENABLED_SERVICES odl-hybrid"
}
# 等待 odl-hybrid HTTP 健康检查通过compose 启动后服务可能仍在拉依赖)
_wait_odl_hybrid_ready() {
local port="${ODL_HYBRID_PORT:-5002}"
local max_wait="${ODL_HYBRID_STARTUP_WAIT_SEC:-180}"
local waited=0
local interval=5
if ! command -v curl &> /dev/null; then
log_warning "未安装 curl跳过 odl-hybrid 就绪等待;请手动检查 http://localhost:${port}/health"
return 0
fi
log_info "等待 odl-hybrid 就绪(最多 ${max_wait}s首次需构建镜像: docker compose ... build odl-hybrid..."
while [ "$waited" -lt "$max_wait" ]; do
if curl -sf "http://127.0.0.1:${port}/health" >/dev/null 2>&1; then
log_success "odl-hybrid 已就绪 (http://localhost:${port}/health)"
return 0
fi
sleep "$interval"
waited=$((waited + interval))
done
log_warning "odl-hybrid 在 ${max_wait}s 内未就绪,请查看: docker logs WeKnora-odl-hybrid"
return 1
}
# 启动基础设施服务
start_services() {
log_info "启动开发环境基础设施服务..."
check_docker
if [ $? -ne 0 ]; then
return 1
fi
cd "$PROJECT_ROOT"
# 检查 .env 文件
if [ ! -f ".env" ]; then
log_error ".env 文件不存在,请先创建"
return 1
fi
set -a
# shellcheck source=/dev/null
source .env
set +a
# 解析 profile 参数
shift # 移除 "start" 命令本身
# 默认启动基础设施postgres / redis / docreader+ langfuse
# 其余可选服务通过 --minio / --qdrant / --neo4j / --jaeger / --dex / --full 按需开启。
PROFILES="--profile langfuse"
ENABLED_SERVICES="langfuse"
while [ $# -gt 0 ]; do
case "$1" in
--minio)
PROFILES="$PROFILES --profile minio"
ENABLED_SERVICES="$ENABLED_SERVICES minio"
;;
--qdrant)
PROFILES="$PROFILES --profile qdrant"
ENABLED_SERVICES="$ENABLED_SERVICES qdrant"
;;
--neo4j)
PROFILES="$PROFILES --profile neo4j"
ENABLED_SERVICES="$ENABLED_SERVICES neo4j"
;;
--jaeger)
PROFILES="$PROFILES --profile jaeger"
ENABLED_SERVICES="$ENABLED_SERVICES jaeger"
;;
--dex)
PROFILES="$PROFILES --profile dex"
ENABLED_SERVICES="$ENABLED_SERVICES dex"
;;
--langfuse)
PROFILES="$PROFILES --profile langfuse"
ENABLED_SERVICES="$ENABLED_SERVICES langfuse"
;;
--no-langfuse)
PROFILES="${PROFILES//--profile langfuse/}"
ENABLED_SERVICES="${ENABLED_SERVICES//langfuse/}"
;;
--odl-hybrid)
if [[ "$ENABLED_SERVICES" != *"odl-hybrid"* ]]; then
_enable_odl_hybrid_profile
fi
;;
--full)
PROFILES="--profile full"
ENABLED_SERVICES="minio qdrant neo4j jaeger dex"
break
;;
*)
log_warning "未知参数: $1"
;;
esac
shift
done
# 启动服务odl-hybrid 单独 --build避免每次重建 docreader
"$DOCKER_COMPOSE_BIN" $DOCKER_COMPOSE_SUBCMD -f docker-compose.dev.yml $PROFILES up -d
local compose_rc=$?
if [ "$compose_rc" -eq 0 ] && [[ "$ENABLED_SERVICES" == *"odl-hybrid"* ]]; then
log_info "构建/更新 odl-hybrid 镜像..."
"$DOCKER_COMPOSE_BIN" $DOCKER_COMPOSE_SUBCMD -f docker-compose.dev.yml $PROFILES up -d --build odl-hybrid
_wait_odl_hybrid_ready || true
# docreader 需读取 DOCREADER_ODL_HYBRID若刚改 .env强制重建以注入环境变量
if _should_enable_odl_hybrid_from_env; then
log_info "重建 docreader 以应用 DOCREADER_ODL_HYBRID=${DOCREADER_ODL_HYBRID} ..."
"$DOCKER_COMPOSE_BIN" $DOCKER_COMPOSE_SUBCMD -f docker-compose.dev.yml up -d --force-recreate docreader
fi
fi
if [ "$compose_rc" -eq 0 ]; then
log_success "基础设施服务已启动"
echo ""
log_info "服务访问地址:"
echo " - PostgreSQL: localhost:5432"
echo " - Redis: localhost:6379"
echo " - DocReader: localhost:50051"
# 根据启用的 profile 显示额外服务
if [[ "$ENABLED_SERVICES" == *"minio"* ]]; then
echo " - MinIO: localhost:9000 (Console: localhost:9001)"
fi
if [[ "$ENABLED_SERVICES" == *"qdrant"* ]]; then
echo " - Qdrant: localhost:6333 (gRPC: localhost:6334)"
fi
if [[ "$ENABLED_SERVICES" == *"neo4j"* ]]; then
echo " - Neo4j: localhost:7474 (Bolt: localhost:7687)"
fi
if [[ "$ENABLED_SERVICES" == *"jaeger"* ]]; then
echo " - Jaeger: localhost:16686"
fi
if [[ "$ENABLED_SERVICES" == *"dex"* ]]; then
echo " - Dex: localhost:5556"
fi
if [[ "$ENABLED_SERVICES" == *"langfuse"* ]]; then
echo " - Langfuse: http://localhost:${LANGFUSE_WEB_PORT:-3000}"
fi
if [[ "$ENABLED_SERVICES" == *"odl-hybrid"* ]]; then
echo " - ODL Hybrid: http://localhost:${ODL_HYBRID_PORT:-5002} (health: /health)"
echo " docreader 需 DOCREADER_ODL_HYBRID=docling-fast"
fi
echo ""
log_info "接下来的步骤:"
printf "%b\n" "${YELLOW}1. 在新终端运行后端:${NC} make dev-app"
printf "%b\n" "${YELLOW}2. 在新终端运行前端:${NC} make dev-frontend"
return 0
else
log_error "服务启动失败"
return 1
fi
}
# 停止服务
stop_services() {
log_info "停止开发环境服务..."
check_docker
if [ $? -ne 0 ]; then
return 1
fi
cd "$PROJECT_ROOT"
"$DOCKER_COMPOSE_BIN" $DOCKER_COMPOSE_SUBCMD -f docker-compose.dev.yml down
if [ $? -eq 0 ]; then
log_success "所有服务已停止"
return 0
else
log_error "服务停止失败"
return 1
fi
}
# 重启服务
restart_services() {
stop_services
sleep 2
start_services
}
# 查看日志
show_logs() {
cd "$PROJECT_ROOT"
"$DOCKER_COMPOSE_BIN" $DOCKER_COMPOSE_SUBCMD -f docker-compose.dev.yml logs -f
}
# 查看状态
show_status() {
cd "$PROJECT_ROOT"
"$DOCKER_COMPOSE_BIN" $DOCKER_COMPOSE_SUBCMD -f docker-compose.dev.yml ps
}
# 启动后端应用(本地)
start_app() {
log_info "启动后端应用(本地开发模式)..."
cd "$PROJECT_ROOT"
# 检查 Go 是否安装
if ! command -v go &> /dev/null; then
log_error "Go 未安装"
return 1
fi
# 加载环境变量(使用 set -a 确保所有变量都被导出)
if [ -f ".env" ]; then
log_info "加载 .env 文件..."
set -a
source .env
set +a
else
log_error ".env 文件不存在,请先创建配置文件"
return 1
fi
# 设置本地开发环境变量(覆盖 Docker 容器地址)
export DB_HOST=localhost
export DOCREADER_ADDR=localhost:50051
export DOCREADER_TRANSPORT=grpc
export MINIO_ENDPOINT=localhost:9000
export REDIS_ADDR=localhost:6379
export MILVUS_ADDRESS=localhost:19530
export OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317
export NEO4J_URI=bolt://localhost:7687
export QDRANT_HOST=localhost
# 确保必要的环境变量已设置
if [ -z "$DB_DRIVER" ]; then
log_error "DB_DRIVER 环境变量未设置,请检查 .env 文件"
return 1
fi
log_info "环境变量已设置,启动应用..."
log_info "数据库地址: $DB_HOST:${DB_PORT:-5432}"
export CGO_CFLAGS="-Wno-deprecated-declarations -Wno-gnu-folding-constant"
if [[ "$(uname)" == "Darwin" ]]; then
export CGO_LDFLAGS="-Wl,-no_warn_duplicate_libraries"
fi
# 检查是否安装了 Air热重载工具
if command -v air &> /dev/null; then
log_success "检测到 Air使用热重载模式启动..."
log_info "修改 Go 代码后将自动重新编译和重启"
air
else
log_info "未检测到 Air使用普通模式启动"
log_warning "提示: 安装 Air 可以实现代码修改后自动重启"
log_info "安装命令: go install github.com/air-verse/air@latest"
LDFLAGS="$(./scripts/get_version.sh ldflags) -X 'google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=warn'"
go run -ldflags="$LDFLAGS" ./cmd/server
fi
}
# 启动前端(本地)
start_frontend() {
log_info "启动前端开发服务器..."
cd "$PROJECT_ROOT/frontend"
# 检查 npm 是否安装
if ! command -v npm &> /dev/null; then
log_error "npm 未安装"
return 1
fi
# 检查依赖是否已安装
if [ ! -d "node_modules" ]; then
log_warning "node_modules 不存在,正在安装依赖..."
npm install
fi
log_info "启动 Vite 开发服务器..."
log_info "前端将运行在 http://localhost:5173"
log_info "前端 API 代理目标: ${VITE_DEV_PROXY_TARGET:-${FRONTEND_BACKEND_URL:-http://localhost:8080}}"
# 运行开发服务器
npm run dev
}
# 解析命令
CMD="${1:-help}"
case "$CMD" in
start)
start_services "$@"
;;
stop)
stop_services
;;
restart)
restart_services
;;
logs)
show_logs
;;
status)
show_status
;;
app)
start_app
;;
frontend)
start_frontend
;;
help|--help|-h)
show_help
;;
*)
log_error "未知命令: $CMD"
show_help
exit 1
;;
esac
exit 0