MySQL 就绪探测改为走 TCP(127.0.0.1:3306),避免初始化阶段只监听 socket 的临时服务器让探测提前通过,导致后端连入时遇到 unexpected EOF。 后端监听端口改为读取 APP_PORT(默认 8082),与脚本探测的空闲端口一致, 并在启动时和最终提示中打印完整访问地址;npm 默认使用 npmmirror 镜像, 同时将根目录编译产物 /server 加入忽略。
152 lines
5.1 KiB
Bash
Executable File
152 lines
5.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 一次性试用脚本:本机编译前后端,Docker 仅跑一个一次性 MySQL,数据全部隔离。
|
||
#
|
||
# 特性:
|
||
# - 前端本机 vite build -> frontend/dist(由后端直接托管)
|
||
# - 后端本机 go build -> .dev-run/ 下的二进制,本机直接运行
|
||
# - MySQL 用独立容器 wmms-dev-mysql + 匿名卷,只绑本机,不影响其他环境
|
||
# - 自动探测空闲端口:后端默认从 8080 起,MySQL 从 13306 起
|
||
# - 每次都是全新数据库(执行 scripts/init.sql)
|
||
# - 按 Ctrl+C 停止并删除二进制目录与 MySQL 容器/数据卷
|
||
#
|
||
# 可选环境变量:
|
||
# APP_PORT 期望的网页端口;被占用时自动顺延(默认 8080)
|
||
# DB_PORT 期望映射到本机的 MySQL 端口;被占用时顺延(默认 13306)
|
||
# KEEP_DATA=1 退出时保留 .dev-run/ 和 MySQL 容器(默认全部删除)
|
||
# NPM_REGISTRY 重装依赖时使用的 npm 镜像源(默认 https://registry.npmmirror.com)
|
||
|
||
set -euo pipefail
|
||
|
||
cd "$(dirname "$0")"
|
||
|
||
RUNDIR="$PWD/.dev-run"
|
||
DB_CONTAINER="wmms-dev-mysql"
|
||
DB_PASSWORD="wmms1234"
|
||
DB_NAME="wmms"
|
||
# 默认走国内镜像,避免官方源 ECONNRESET;可用 NPM_REGISTRY 覆盖。
|
||
NPM_REGISTRY="${NPM_REGISTRY:-https://registry.npmmirror.com}"
|
||
|
||
find_free_port() {
|
||
local start="$1" p="$1"
|
||
for ((i = 0; i < 100; i++)); do
|
||
if ! ss -ltn 2>/dev/null | awk '{print $4}' | grep -Eq "[:.]$p$"; then
|
||
echo "$p"
|
||
return
|
||
fi
|
||
p=$((p + 1))
|
||
done
|
||
echo "$start"
|
||
}
|
||
|
||
cleaned=0
|
||
PID=""
|
||
cleanup() {
|
||
[ "$cleaned" = "1" ] && return
|
||
cleaned=1
|
||
echo
|
||
echo "==> 停止服务"
|
||
[ -n "$PID" ] && kill "$PID" 2>/dev/null || true
|
||
if [ "${KEEP_DATA:-0}" = "1" ]; then
|
||
echo "==> KEEP_DATA=1,保留 $RUNDIR 与容器 $DB_CONTAINER"
|
||
else
|
||
docker rm -f "$DB_CONTAINER" >/dev/null 2>&1 || true
|
||
rm -rf "$RUNDIR"
|
||
echo "==> 已删除 MySQL 容器/数据卷与 $RUNDIR"
|
||
fi
|
||
}
|
||
trap cleanup EXIT INT TERM
|
||
|
||
APP_PORT="$(find_free_port "${APP_PORT:-8080}")"
|
||
DB_PORT="$(find_free_port "${DB_PORT:-13306}")"
|
||
|
||
echo "==> 1/4 构建前端(vite build)"
|
||
(
|
||
cd frontend
|
||
# 随仓库带来的 node_modules 可能是在 Windows 上安装的:缺 Linux 平台的
|
||
# rollup 原生包,且 .bin 丢失可执行位。这种情况下用 npm ci 按 lock 重装。
|
||
need_install=0
|
||
if [ ! -d node_modules ]; then
|
||
need_install=1
|
||
elif [ ! -d node_modules/@rollup/rollup-linux-x64-gnu ] && [ ! -d node_modules/@rollup/rollup-linux-x64-musl ]; then
|
||
echo " 检测到 node_modules 非 Linux 平台安装,重新安装依赖..."
|
||
need_install=1
|
||
fi
|
||
if [ "$need_install" = "1" ]; then
|
||
npm ci --registry="$NPM_REGISTRY"
|
||
else
|
||
chmod +x node_modules/.bin/* 2>/dev/null || true
|
||
fi
|
||
npm run build
|
||
)
|
||
|
||
echo "==> 2/4 构建后端(go build)"
|
||
mkdir -p "$RUNDIR"
|
||
BIN="$RUNDIR/server"
|
||
go build -o "$BIN" ./cmd/server
|
||
|
||
# 若已有同名残留容器(上次异常退出),先清掉。
|
||
docker rm -f "$DB_CONTAINER" >/dev/null 2>&1 || true
|
||
|
||
echo "==> 3/4 启动一次性 MySQL(容器 $DB_CONTAINER,本机端口 $DB_PORT)"
|
||
docker run -d --name "$DB_CONTAINER" \
|
||
-e MYSQL_ROOT_PASSWORD="$DB_PASSWORD" \
|
||
-e MYSQL_DATABASE="$DB_NAME" \
|
||
-p "127.0.0.1:$DB_PORT:3306" \
|
||
-v "$PWD/scripts/init.sql:/docker-entrypoint-initdb.d/001-init.sql:ro" \
|
||
mysql:8.0 \
|
||
--default-authentication-plugin=mysql_native_password \
|
||
--character-set-server=utf8mb4 \
|
||
--collation-server=utf8mb4_unicode_ci >/dev/null
|
||
|
||
echo " 等待 MySQL 就绪..."
|
||
for ((i = 0; i < 90; i++)); do
|
||
# 必须走 TCP(127.0.0.1:3306),不能用 localhost 的 socket:初始化阶段的
|
||
# 临时服务器只监听 socket,socket ping 会提前成功,但 3306 尚未就绪,
|
||
# 后端连入时正逢重启,报 unexpected EOF / bad connection。
|
||
if docker exec "$DB_CONTAINER" mysqladmin ping -h 127.0.0.1 -P 3306 -uroot -p"$DB_PASSWORD" --silent >/dev/null 2>&1; then
|
||
break
|
||
fi
|
||
if ! docker ps --format '{{.Names}}' | grep -qx "$DB_CONTAINER"; then
|
||
echo "MySQL 容器意外退出,日志:"
|
||
docker logs "$DB_CONTAINER" 2>&1 | tail -40 || true
|
||
exit 1
|
||
fi
|
||
sleep 1
|
||
done
|
||
|
||
echo "==> 4/4 启动后端(端口 $APP_PORT)"
|
||
echo " 访问地址: http://localhost:$APP_PORT"
|
||
DSN="root:$DB_PASSWORD@tcp(127.0.0.1:$DB_PORT)/$DB_NAME?charset=utf8mb4&parseTime=true"
|
||
APP_PORT="$APP_PORT" DB_DSN="$DSN" "$BIN" &
|
||
PID=$!
|
||
|
||
READY=0
|
||
for ((i = 0; i < 60; i++)); do
|
||
if curl -fsS "http://localhost:$APP_PORT/api/health" >/dev/null 2>&1; then
|
||
READY=1
|
||
break
|
||
fi
|
||
if ! kill -0 "$PID" 2>/dev/null; then
|
||
echo "后端进程意外退出,请查看上方日志"
|
||
exit 1
|
||
fi
|
||
sleep 0.5
|
||
done
|
||
|
||
if [ "$READY" != "1" ]; then
|
||
echo "警告:未检测到服务就绪,后端日志:"
|
||
wait "$PID" || true
|
||
exit 1
|
||
fi
|
||
|
||
echo
|
||
echo "============================================================"
|
||
echo " 试用环境已启动(数据一次性,关闭即清除,不影响其他环境)"
|
||
echo " 访问地址: http://localhost:$APP_PORT"
|
||
echo " 账号:admin / admin123 user / user123"
|
||
echo " 按 Ctrl+C 停止并删除 MySQL 容器与临时文件"
|
||
echo "============================================================"
|
||
echo
|
||
|
||
wait "$PID"
|