Files
Logan.Li 04d1342b27 feat: FlowStack 发布系统首个可运行版本 (gin + React)
- Go gin 后端:流水线/节点/连线/运行/日志/GitOps/插件 REST API,
  SQLite(GORM) 自动建表 + seed,模拟执行引擎 + SSE 实时日志推送,
  前端产物 go:embed 嵌入单二进制,一个端口同时服务 API 与页面
- React 19 前端:画布式流水线编辑器(React Flow + Tekton 任务库)、
  GitOps 部署、批量发布、发布记录等 14 个模块页面,
  tRPC 全部替换为 REST(TanStack Query)
- 首次启动生成随机密码管理员账号(日志 + data/initial-admin-password.txt)
- run.sh 一键构建启动(默认端口 9527,支持 start/stop/restart/build/dev)
2026-10-27 02:44:10 +08:00

269 lines
8.7 KiB
Bash
Executable File
Raw Permalink 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
# FlowStack 发布系统启动脚本
# 一键构建并启动前后端(前端产物嵌入 Go 单二进制,一个端口同时服务 API 与页面)
# 用法: ./run.sh [start|stop|restart|build|dev|backend|status|help]
set -e
# ============================================================
# 配置区
# ============================================================
PROJECT_NAME="FlowStack"
BACKEND_DIR="backend"
FRONTEND_DIR="frontend"
STANDALONE_BIN="flowstack"
STANDALONE_PORT="${PORT:-9527}"
FRONTEND_DEV_PORT=5173
STANDALONE_PID_FILE="$PWD/.flowstack.pid"
STANDALONE_LOG_FILE="$PWD/logs/flowstack.log"
BACKEND_DEV_PID_FILE="$PWD/.backend-dev.pid"
# ============================================================
# 通用函数
# ============================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
check_port() { lsof -ti:$1 2>/dev/null | head -1; }
kill_port() {
local pids=$(lsof -ti:$1 2>/dev/null || echo "")
if [ -n "$pids" ]; then
info "释放端口 $1..."
echo "$pids" | xargs kill -9 2>/dev/null || true
sleep 1
fi
}
wait_health() {
local retries=0
while ! curl -s "http://localhost:$STANDALONE_PORT/health" > /dev/null 2>&1; do
retries=$((retries + 1))
if [ $retries -ge 60 ]; then
error "服务启动超时,请检查日志: $STANDALONE_LOG_FILE"
return 1
fi
sleep 1
done
return 0
}
# ============================================================
# 构建单二进制(前端 dist → backend/static → go build)
# ============================================================
build_frontend() {
info "构建前端资源..."
cd "$SCRIPT_DIR/$FRONTEND_DIR"
if [ ! -d "node_modules" ]; then
warning "node_modules 不存在,正在安装依赖..."
npm install
# 允许 esbuild 安装其二进制(npm allow-scripts 策略)
npm approve-scripts esbuild 2>/dev/null || true
npm rebuild esbuild 2>/dev/null || true
fi
npm run build
success "前端构建完成: $FRONTEND_DIR/dist"
cd "$SCRIPT_DIR"
}
build_binary() {
build_frontend
info "拷贝前端产物到 backend/static 并编译 $STANDALONE_BIN..."
rm -rf "$SCRIPT_DIR/$BACKEND_DIR/static"
mkdir -p "$SCRIPT_DIR/$BACKEND_DIR/static"
cp -R "$SCRIPT_DIR/$FRONTEND_DIR/dist/." "$SCRIPT_DIR/$BACKEND_DIR/static/"
cd "$SCRIPT_DIR/$BACKEND_DIR"
go build -trimpath -o "$SCRIPT_DIR/$STANDALONE_BIN" .
cd "$SCRIPT_DIR"
success "单二进制已生成: $SCRIPT_DIR/$STANDALONE_BIN"
}
# ============================================================
# 单二进制运行
# ============================================================
start_binary() {
mkdir -p "$SCRIPT_DIR/logs"
kill_port "$STANDALONE_PORT"
info "启动 FlowStack (端口 $STANDALONE_PORT)..."
PORT="$STANDALONE_PORT" GIN_MODE="${GIN_MODE:-release}" \
nohup "$SCRIPT_DIR/$STANDALONE_BIN" >> "$STANDALONE_LOG_FILE" 2>&1 &
echo $! > "$STANDALONE_PID_FILE"
if wait_health; then
success "FlowStack 已启动: http://localhost:$STANDALONE_PORT (PID: $(cat "$STANDALONE_PID_FILE"))"
else
tail -20 "$STANDALONE_LOG_FILE"
return 1
fi
}
stop_all() {
info "正在停止服务..."
if [ -f "$STANDALONE_PID_FILE" ]; then
local pid=$(cat "$STANDALONE_PID_FILE" 2>/dev/null || echo "")
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
kill "$pid" 2>/dev/null || true
fi
rm -f "$STANDALONE_PID_FILE"
fi
if [ -f "$BACKEND_DEV_PID_FILE" ]; then
local pid=$(cat "$BACKEND_DEV_PID_FILE" 2>/dev/null || echo "")
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
kill "$pid" 2>/dev/null || true
fi
rm -f "$BACKEND_DEV_PID_FILE"
fi
kill_port "$STANDALONE_PORT"
kill_port "$FRONTEND_DEV_PORT"
success "服务已停止"
}
show_status() {
info "服务状态:"
if [ -n "$(check_port $STANDALONE_PORT)" ]; then
success "FlowStack: 运行中 (http://localhost:$STANDALONE_PORT)"
else
warning "FlowStack: 未运行"
fi
if [ -n "$(check_port $FRONTEND_DEV_PORT)" ]; then
success "Vite dev: 运行中 (http://localhost:$FRONTEND_DEV_PORT)"
fi
[ -x "$SCRIPT_DIR/$STANDALONE_BIN" ] && success "二进制: $SCRIPT_DIR/$STANDALONE_BIN"
}
# ============================================================
# 开发模式:gin(9527) + vite(5173 代理 /api)
# ============================================================
dev_mode() {
info "开发模式: 后端 gin ($STANDALONE_PORT) + 前端 Vite ($FRONTEND_DEV_PORT)"
kill_port "$STANDALONE_PORT"
mkdir -p "$SCRIPT_DIR/logs"
cd "$SCRIPT_DIR/$BACKEND_DIR"
PORT="$STANDALONE_PORT" GIN_MODE=debug go run . >> "$SCRIPT_DIR/logs/backend-dev.log" 2>&1 &
echo $! > "$BACKEND_DEV_PID_FILE"
cd "$SCRIPT_DIR"
if ! wait_health; then
error "后端启动失败,查看 logs/backend-dev.log"
return 1
fi
success "后端已启动: http://localhost:$STANDALONE_PORT"
cd "$SCRIPT_DIR/$FRONTEND_DIR"
[ ! -d "node_modules" ] && { npm install; npm approve-scripts esbuild 2>/dev/null || true; npm rebuild esbuild 2>/dev/null || true; }
info "启动 Vite(Ctrl+C 退出,退出后自动停止后端)..."
trap 'kill $(cat "$BACKEND_DEV_PID_FILE" 2>/dev/null) 2>/dev/null; rm -f "$BACKEND_DEV_PID_FILE"; exit 0' INT TERM EXIT
npm run dev
}
# ============================================================
# 命令入口
# ============================================================
show_help() {
echo "$PROJECT_NAME 发布系统启动脚本"
echo ""
echo "用法: ./run.sh [命令]"
echo ""
echo "命令:"
echo " start 构建并启动单二进制服务(默认,含前端资源)"
echo " stop 停止所有服务"
echo " restart 重启服务"
echo " build 仅构建单二进制 $STANDALONE_BIN"
echo " dev 开发模式:gin + Vite 热更新"
echo " backend 仅启动后端 API(开发用)"
echo " status 查看服务状态"
echo " help 显示帮助"
echo ""
echo "环境变量:"
echo " PORT 服务端口(默认 9527)"
echo ""
echo "示例:"
echo " ./run.sh # 一键启动,访问 http://localhost:9527"
echo " PORT=9000 ./run.sh # 指定端口"
echo " ./run.sh dev # 前后端热更新开发"
}
main() {
local command=${1:-start}
# 依赖检查
if [[ "$command" =~ ^(start|build|dev|backend)$ ]]; then
command -v go &> /dev/null || { error "Go 未安装"; exit 1; }
fi
if [[ "$command" =~ ^(start|build|dev)$ ]]; then
command -v npm &> /dev/null || { error "npm 未安装,请先安装 Node.js"; exit 1; }
fi
case "$command" in
start)
info "========== $PROJECT_NAME =========="
[ ! -x "$SCRIPT_DIR/$STANDALONE_BIN" ] && {
warning "未找到 $STANDALONE_BIN,开始构建(首次约 1-3 分钟)..."
build_binary
}
start_binary && {
echo ""
success "========== FlowStack 已启动 =========="
info "访问地址: http://localhost:$STANDALONE_PORT"
info "健康检查: http://localhost:$STANDALONE_PORT/health"
info "停止服务: ./run.sh stop"
echo ""
}
;;
stop)
stop_all
;;
restart)
stop_all
sleep 1
build_binary
start_binary
;;
build)
build_binary
;;
dev)
dev_mode
;;
backend)
kill_port "$STANDALONE_PORT"
mkdir -p "$SCRIPT_DIR/logs"
cd "$SCRIPT_DIR/$BACKEND_DIR"
PORT="$STANDALONE_PORT" GIN_MODE=debug go run . >> "$SCRIPT_DIR/logs/backend-dev.log" 2>&1 &
echo $! > "$BACKEND_DEV_PID_FILE"
cd "$SCRIPT_DIR"
wait_health && success "后端 API: http://localhost:$STANDALONE_PORT (日志: logs/backend-dev.log)"
;;
status)
show_status
;;
help|--help|-h)
show_help
;;
*)
error "未知命令: $command"
show_help
exit 1
;;
esac
}
main "$@"