Merge branch 'master' of http://82.157.175.59:3000/GenarrativeAI/Genarrative
This commit is contained in:
@@ -7,6 +7,7 @@ usage() {
|
||||
用法:
|
||||
npm run dev:rust
|
||||
./scripts/dev-rust-stack.sh --api-port 8090 --spacetime-port 3110
|
||||
./scripts/dev-rust-stack.sh --spacetime-data-dir server-rs/.spacetimedb/local/data
|
||||
./scripts/dev-rust-stack.sh --admin-web-port 3102
|
||||
./scripts/dev-rust-stack.sh --api-timeout-seconds 600
|
||||
./scripts/dev-rust-stack.sh --skip-spacetime --skip-publish
|
||||
@@ -18,7 +19,7 @@ usage() {
|
||||
1. 默认同时启动 SpacetimeDB standalone、Rust api-server、主站 Vite 与后台 Vite。
|
||||
2. 当前开发阶段默认 publish server-rs/crates/spacetime-module 时追加 -c=on-conflict 在结构冲突时清理旧模块数据。
|
||||
3. 只有显式传入 --preserve-database 时,才会跳过 -c=on-conflict。
|
||||
4. SpacetimeDB 默认使用 server-rs/.spacetimedb/local 作为本地数据与日志目录。
|
||||
4. SpacetimeDB 默认使用 server-rs/.spacetimedb/local/data 作为本地数据目录;端口被占用时自动接受 SpacetimeDB 建议的最近可用端口。
|
||||
5. 默认在发布模块前随机生成迁移引导密钥,注入 GENARRATIVE_SPACETIME_MIGRATION_BOOTSTRAP_SECRET 并显示在控制台。
|
||||
EOF
|
||||
}
|
||||
@@ -126,18 +127,18 @@ cleanup() {
|
||||
wait_for_spacetime() {
|
||||
local server="$1"
|
||||
local timeout_seconds="$2"
|
||||
local root_dir="$3"
|
||||
local data_dir="$3"
|
||||
local process_pid="${4:-}"
|
||||
local deadline=$((SECONDS + timeout_seconds))
|
||||
|
||||
while ((SECONDS < deadline)); do
|
||||
if [[ -n "${process_pid}" ]] && ! kill -0 "${process_pid}" 2>/dev/null; then
|
||||
echo "[dev:rust] SpacetimeDB 进程在就绪前退出。" >&2
|
||||
print_spacetime_start_failure_diagnostics "${root_dir}"
|
||||
print_spacetime_start_failure_diagnostics "${data_dir}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if is_spacetime_ready "${server}" "${root_dir}"; then
|
||||
if is_spacetime_ready "${server}"; then
|
||||
return
|
||||
fi
|
||||
|
||||
@@ -145,16 +146,56 @@ wait_for_spacetime() {
|
||||
done
|
||||
|
||||
echo "[dev:rust] 等待 SpacetimeDB 就绪超时: ${server}" >&2
|
||||
print_spacetime_start_failure_diagnostics "${root_dir}"
|
||||
print_spacetime_start_failure_diagnostics "${data_dir}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
wait_for_spacetime_listen_addr() {
|
||||
local log_file="$1"
|
||||
local timeout_seconds="$2"
|
||||
local process_pid="${3:-}"
|
||||
local deadline=$((SECONDS + timeout_seconds))
|
||||
local listen_addr=""
|
||||
|
||||
while ((SECONDS < deadline)); do
|
||||
if [[ -f "${log_file}" ]]; then
|
||||
listen_addr="$(sed -n 's/^.*Starting SpacetimeDB listening on \([^[:space:]]\+\).*$/\1/p' "${log_file}" | tail -n 1)"
|
||||
if [[ -n "${listen_addr}" ]]; then
|
||||
echo "${listen_addr}"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${process_pid}" ]] && ! kill -0 "${process_pid}" 2>/dev/null; then
|
||||
echo "[dev:rust] SpacetimeDB 进程在输出监听地址前退出。" >&2
|
||||
if [[ -f "${log_file}" ]]; then
|
||||
echo "[dev:rust] 最近 SpacetimeDB 启动日志: ${log_file}" >&2
|
||||
tail -n 80 "${log_file}" >&2 || true
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sleep 0.2
|
||||
done
|
||||
|
||||
echo "[dev:rust] 等待 SpacetimeDB 输出监听地址超时。" >&2
|
||||
if [[ -f "${log_file}" ]]; then
|
||||
echo "[dev:rust] 最近 SpacetimeDB 启动日志: ${log_file}" >&2
|
||||
tail -n 80 "${log_file}" >&2 || true
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
port_from_listen_addr() {
|
||||
local listen_addr="$1"
|
||||
echo "${listen_addr##*:}"
|
||||
}
|
||||
|
||||
is_spacetime_ready() {
|
||||
local server="$1"
|
||||
local root_dir="$2"
|
||||
local output
|
||||
|
||||
if output="$(spacetime --root-dir="${root_dir}" server ping "${server}" 2>&1)" &&
|
||||
if output="$(spacetime server ping "${server}" 2>&1)" &&
|
||||
[[ "${output}" == *"Server is online:"* ]]; then
|
||||
return 0
|
||||
fi
|
||||
@@ -174,10 +215,10 @@ request.on("error", () => process.exit(1));
|
||||
}
|
||||
|
||||
print_spacetime_start_failure_diagnostics() {
|
||||
local root_dir="$1"
|
||||
local log_file="${root_dir}/data/logs/spacetime-standalone.log"
|
||||
local data_dir="$1"
|
||||
local log_file="${data_dir}/logs/spacetime-standalone.log"
|
||||
|
||||
echo "[dev:rust] SpacetimeDB root-dir: ${root_dir}" >&2
|
||||
echo "[dev:rust] SpacetimeDB data-dir: ${data_dir}" >&2
|
||||
|
||||
if [[ ! -f "${log_file}" ]]; then
|
||||
echo "[dev:rust] 未找到 SpacetimeDB standalone 日志: ${log_file}" >&2
|
||||
@@ -189,26 +230,28 @@ print_spacetime_start_failure_diagnostics() {
|
||||
|
||||
if grep -q "mismatched database identity" "${log_file}" 2>/dev/null; then
|
||||
echo "[dev:rust] 检测到本地 replica 与当前数据库 identity 不一致。" >&2
|
||||
echo "[dev:rust] 常见原因是同一个 root-dir 保留了旧库 data/replicas/1,但 control-db 已指向新库。" >&2
|
||||
echo "[dev:rust] 若这是可丢弃的本地开发库,请先停止 SpacetimeDB,再备份或移走 ${root_dir}/data 后重新启动。" >&2
|
||||
echo "[dev:rust] 若需要保留数据,不要清理目录;请改回创建旧库的 database/root-dir,或先走迁移导出。" >&2
|
||||
echo "[dev:rust] 常见原因是同一个 data-dir 保留了旧库 replicas/1,但 control-db 已指向新库。" >&2
|
||||
echo "[dev:rust] 若这是可丢弃的本地开发库,请先停止 SpacetimeDB,再备份或移走 ${data_dir} 后重新启动。" >&2
|
||||
echo "[dev:rust] 若需要保留数据,不要清理目录;请改回创建旧库的 database/data-dir,或先走迁移导出。" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
describe_spacetime_root_owner() {
|
||||
local root_dir="$1"
|
||||
local windows_root_dir="${root_dir}"
|
||||
local data_dir="$1"
|
||||
local windows_data_dir="${data_dir}"
|
||||
|
||||
if [[ "${windows_root_dir}" =~ ^/([a-zA-Z])/(.*)$ ]]; then
|
||||
windows_root_dir="${BASH_REMATCH[1]}:/${BASH_REMATCH[2]}"
|
||||
if [[ "${windows_data_dir}" =~ ^/([a-zA-Z])/(.*)$ ]]; then
|
||||
windows_data_dir="${BASH_REMATCH[1]}:/${BASH_REMATCH[2]}"
|
||||
fi
|
||||
|
||||
# Windows 本地开发最常见的失败是同一个 root-dir 下已有 standalone 持有 spacetime.pid;
|
||||
# Windows 本地开发最常见的失败是同一个 data-dir 下已有 standalone 持有 spacetime.pid;
|
||||
# 启动前先打印占用进程,避免用户只看到底层 os error 33 而不知道该停哪个实例。
|
||||
if command -v powershell.exe >/dev/null 2>&1; then
|
||||
ROOT_DIR_FOR_POWERSHELL="${windows_root_dir}" powershell.exe -NoProfile -Command '
|
||||
$rootDir = $env:ROOT_DIR_FOR_POWERSHELL
|
||||
$normalized = $rootDir.Replace("/", "\")
|
||||
# 只有 Windows/Git Bash 风格路径才交给 PowerShell 查 Windows 进程;
|
||||
# WSL/Linux 的 /tmp、/home 路径不能直接拿去匹配 Windows CommandLine,容易误命中无关 spacetime 进程。
|
||||
if command -v powershell.exe >/dev/null 2>&1 && [[ "${data_dir}" =~ ^/([a-zA-Z])/ ]]; then
|
||||
DATA_DIR_FOR_POWERSHELL="${windows_data_dir}" powershell.exe -NoProfile -Command '
|
||||
$dataDir = $env:DATA_DIR_FOR_POWERSHELL
|
||||
$normalized = $dataDir.Replace("/", "\")
|
||||
Get-CimInstance Win32_Process |
|
||||
Where-Object { $_.Name -match "spacetime" -and $_.CommandLine -and $_.CommandLine.Replace("/", "\") -like "*$normalized*" } |
|
||||
ForEach-Object { "pid=$($_.ProcessId) name=$($_.Name) command=$($_.CommandLine)" }
|
||||
@@ -217,7 +260,7 @@ Get-CimInstance Win32_Process |
|
||||
fi
|
||||
|
||||
if command -v ps >/dev/null 2>&1; then
|
||||
ps -eo user=,pid=,ppid=,stat=,comm=,args= 2>/dev/null | awk -v root_dir="${root_dir}" '
|
||||
ps -eo user=,pid=,ppid=,stat=,comm=,args= 2>/dev/null | awk -v data_dir="${data_dir}" '
|
||||
{
|
||||
user = $1
|
||||
pid = $2
|
||||
@@ -230,7 +273,7 @@ Get-CimInstance Win32_Process |
|
||||
sub(/^.*\//, "", name)
|
||||
|
||||
# 只认真实的 SpacetimeDB 启动进程,避免 .spacetimedb 路径让 grep/awk 自身误命中。
|
||||
if ((name == "spacetime" || name == "spacetimedb-cli") && index(args, root_dir) > 0) {
|
||||
if ((name == "spacetime" || name == "spacetimedb-cli") && index(args, data_dir) > 0) {
|
||||
print user " " pid " " ppid " " stat " " name " " args
|
||||
}
|
||||
}
|
||||
@@ -271,50 +314,6 @@ request.on("error", () => process.exit(1));
|
||||
exit 1
|
||||
}
|
||||
|
||||
sync_local_spacetime_install() {
|
||||
local root_dir="$1"
|
||||
|
||||
# SpacetimeDB standalone 会在 --root-dir 下回调 bin/current/spacetimedb-cli.exe;
|
||||
# Windows 本地开发使用工程内 root-dir 时,需要把用户级安装目录同步进来。
|
||||
if [[ "${OSTYPE:-}" != msys* && "${OSTYPE:-}" != cygwin* ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local target_cli="${root_dir}/bin/current/spacetimedb-cli.exe"
|
||||
if [[ -f "${target_cli}" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local spacetime_command
|
||||
spacetime_command="$(command -v spacetime || true)"
|
||||
if [[ -z "${spacetime_command}" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local install_dir
|
||||
install_dir="$(cd -- "$(dirname -- "${spacetime_command}")" && pwd)"
|
||||
if [[ ! -d "${install_dir}/bin" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
echo "[dev:rust] 同步本机 SpacetimeDB 安装到 ${root_dir}"
|
||||
mkdir -p "${root_dir}"
|
||||
cp -a "${install_dir}/bin" "${root_dir}/"
|
||||
if [[ -f "${install_dir}/spacetime.exe" ]]; then
|
||||
cp -f "${install_dir}/spacetime.exe" "${root_dir}/spacetime.exe"
|
||||
fi
|
||||
|
||||
# Git Bash 复制 Windows junction 时可能不会生成可执行的 current 目录;
|
||||
# 若 current 缺失,则用最新版本目录复制出一个真实目录,满足 standalone 回调路径。
|
||||
if [[ ! -f "${target_cli}" ]]; then
|
||||
local version_dir
|
||||
version_dir="$(find "${root_dir}/bin" -mindepth 1 -maxdepth 1 -type d ! -name current | sort -V | tail -n 1)"
|
||||
if [[ -n "${version_dir}" && -f "${version_dir}/spacetimedb-cli.exe" ]]; then
|
||||
rm -rf "${root_dir}/bin/current"
|
||||
cp -a "${version_dir}" "${root_dir}/bin/current"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
generate_migration_bootstrap_secret() {
|
||||
node -e 'const crypto = require("crypto"); process.stdout.write(crypto.randomBytes(32).toString("hex"));'
|
||||
@@ -363,10 +362,11 @@ ADMIN_WEB_PORT="3102"
|
||||
SPACETIME_HOST="127.0.0.1"
|
||||
SPACETIME_PORT="3101"
|
||||
SPACETIME_ROOT_DIR="${SERVER_RS_DIR}/.spacetimedb/local"
|
||||
SPACETIME_DATA_DIR="${SPACETIME_ROOT_DIR}/data"
|
||||
DATABASE=""
|
||||
API_LOG="info,tower_http=info"
|
||||
SPACETIME_TIMEOUT_SECONDS="60"
|
||||
API_SERVER_TIMEOUT_SECONDS="300"
|
||||
API_SERVER_TIMEOUT_SECONDS="600"
|
||||
SKIP_SPACETIME=0
|
||||
SKIP_PUBLISH=0
|
||||
PRESERVE_DATABASE=0
|
||||
@@ -436,6 +436,11 @@ while [[ $# -gt 0 ]]; do
|
||||
;;
|
||||
--spacetime-root-dir)
|
||||
SPACETIME_ROOT_DIR="${2:?缺少 --spacetime-root-dir 的值}"
|
||||
SPACETIME_DATA_DIR="${SPACETIME_ROOT_DIR}/data"
|
||||
shift 2
|
||||
;;
|
||||
--spacetime-data-dir)
|
||||
SPACETIME_DATA_DIR="${2:?缺少 --spacetime-data-dir 的值}"
|
||||
shift 2
|
||||
;;
|
||||
--database)
|
||||
@@ -537,39 +542,44 @@ echo "[dev:rust] rust api: ${RUST_SERVER_TARGET}"
|
||||
echo "[dev:rust] spacetime: ${SPACETIME_SERVER}"
|
||||
echo "[dev:rust] database: ${DATABASE}"
|
||||
echo "[dev:rust] spacetime root: ${SPACETIME_ROOT_DIR}"
|
||||
echo "[dev:rust] spacetime data: ${SPACETIME_DATA_DIR}"
|
||||
echo "[dev:rust] api timeout: ${API_SERVER_TIMEOUT_SECONDS}s"
|
||||
|
||||
if [[ "${SKIP_SPACETIME}" -ne 1 ]]; then
|
||||
mkdir -p "${SPACETIME_ROOT_DIR}"
|
||||
sync_local_spacetime_install "${SPACETIME_ROOT_DIR}"
|
||||
if is_spacetime_ready "${SPACETIME_SERVER}" "${SPACETIME_ROOT_DIR}"; then
|
||||
echo "[dev:rust] 复用已运行的 SpacetimeDB: ${SPACETIME_SERVER}"
|
||||
else
|
||||
SPACETIME_ROOT_OWNER="$(describe_spacetime_root_owner "${SPACETIME_ROOT_DIR}")"
|
||||
if [[ -n "${SPACETIME_ROOT_OWNER}" ]]; then
|
||||
echo "[dev:rust] 当前 root-dir 已被其他 SpacetimeDB 实例占用,无法再次启动。" >&2
|
||||
echo "[dev:rust] 目标地址未就绪: ${SPACETIME_SERVER}" >&2
|
||||
echo "[dev:rust] 如需复用,请传入占用实例实际端口,例如 --spacetime-port 3199;如需重启,请先停止下列进程。" >&2
|
||||
echo "${SPACETIME_ROOT_OWNER}" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "[dev:rust] 启动 spacetimedb"
|
||||
(
|
||||
cd "${SERVER_RS_DIR}"
|
||||
exec spacetime \
|
||||
--root-dir="${SPACETIME_ROOT_DIR}" \
|
||||
start \
|
||||
--edition standalone \
|
||||
--listen-addr "${SPACETIME_HOST}:${SPACETIME_PORT}"
|
||||
) &
|
||||
PIDS+=("$!")
|
||||
NAMES+=("spacetimedb")
|
||||
mkdir -p "${SPACETIME_ROOT_DIR}" "${SPACETIME_DATA_DIR}"
|
||||
SPACETIME_ROOT_OWNER="$(describe_spacetime_root_owner "${SPACETIME_DATA_DIR}")"
|
||||
if [[ -n "${SPACETIME_ROOT_OWNER}" ]]; then
|
||||
echo "[dev:rust] 当前 data-dir 已被其他 SpacetimeDB 实例占用,无法再次启动。" >&2
|
||||
echo "[dev:rust] 如需复用,请传入占用实例实际端口并追加 --skip-spacetime;如需重启,请先停止下列进程。" >&2
|
||||
echo "${SPACETIME_ROOT_OWNER}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SPACETIME_START_LOG="${SPACETIME_DATA_DIR}/logs/dev-rust-spacetime-start.log"
|
||||
mkdir -p "$(dirname -- "${SPACETIME_START_LOG}")"
|
||||
: >"${SPACETIME_START_LOG}"
|
||||
echo "[dev:rust] 启动 spacetimedb"
|
||||
(
|
||||
cd "${SERVER_RS_DIR}"
|
||||
# 当目标端口被占用时,SpacetimeDB 会询问是否使用最近的可用端口;
|
||||
# 这里直接发送回车接受默认建议,再从启动日志解析实际监听端口。
|
||||
printf '\n' | spacetime \
|
||||
start \
|
||||
--data-dir "${SPACETIME_DATA_DIR}" \
|
||||
--listen-addr "${SPACETIME_HOST}:${SPACETIME_PORT}"
|
||||
) 2>&1 | tee "${SPACETIME_START_LOG}" &
|
||||
PIDS+=("$!")
|
||||
NAMES+=("spacetimedb")
|
||||
|
||||
SPACETIME_LISTEN_ADDR="$(wait_for_spacetime_listen_addr "${SPACETIME_START_LOG}" "${SPACETIME_TIMEOUT_SECONDS}" "${PIDS[0]:-}")"
|
||||
SPACETIME_PORT="$(port_from_listen_addr "${SPACETIME_LISTEN_ADDR}")"
|
||||
SPACETIME_SERVER="http://${SPACETIME_HOST}:${SPACETIME_PORT}"
|
||||
echo "[dev:rust] spacetime actual: ${SPACETIME_SERVER}"
|
||||
fi
|
||||
|
||||
if [[ "${SKIP_PUBLISH}" -ne 1 ]]; then
|
||||
echo "[dev:rust] 等待 SpacetimeDB 就绪"
|
||||
wait_for_spacetime "${SPACETIME_SERVER}" "${SPACETIME_TIMEOUT_SECONDS}" "${SPACETIME_ROOT_DIR}" "${PIDS[0]:-}"
|
||||
wait_for_spacetime "${SPACETIME_SERVER}" "${SPACETIME_TIMEOUT_SECONDS}" "${SPACETIME_DATA_DIR}" "${PIDS[0]:-}"
|
||||
prepare_migration_bootstrap_secret
|
||||
|
||||
PUBLISH_ARGS=(
|
||||
@@ -586,7 +596,12 @@ if [[ "${SKIP_PUBLISH}" -ne 1 ]]; then
|
||||
PUBLISH_ARGS+=(--yes)
|
||||
|
||||
echo "[dev:rust] 发布 SpacetimeDB 模块: ${DATABASE}"
|
||||
spacetime --root-dir="${SPACETIME_ROOT_DIR}" "${PUBLISH_ARGS[@]}"
|
||||
(
|
||||
cd "${SERVER_RS_DIR}"
|
||||
# spacetime publish 会在内部调用 Cargo;从 server-rs 目录执行,确保读取
|
||||
# server-rs/.cargo/config.toml 中的 sccache/linker 配置,并复用同一套 target 缓存。
|
||||
spacetime "${PUBLISH_ARGS[@]}"
|
||||
)
|
||||
fi
|
||||
|
||||
echo "[dev:rust] 启动 api-server"
|
||||
|
||||
Reference in New Issue
Block a user