139 lines
3.8 KiB
Bash
Executable File
139 lines
3.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
APP_NAME="infinite-canvas"
|
|
REPO_URL="${REPO_URL:-https://gitea.chickliu.fun/Hermes/infinite-canvas.git}"
|
|
APP_DIR="${APP_DIR:-$HOME/.Hermes/workspace/git/infinite-canvas}"
|
|
HOST="${HOST:-0.0.0.0}"
|
|
WEB_PORT="${WEB_PORT:-3000}"
|
|
API_PORT="${API_PORT:-8080}"
|
|
DATA_DIR="${DATA_DIR:-$APP_DIR/data}"
|
|
DB_PATH="${DATABASE_DSN:-$DATA_DIR/infinite-canvas.db}"
|
|
PROMPT_DATA_DIR="${PROMPT_DATA_DIR:-$DATA_DIR/prompts}"
|
|
LOG_DIR="${LOG_DIR:-$APP_DIR/logs}"
|
|
PID_DIR="${PID_DIR:-$APP_DIR/.pids}"
|
|
MODE="${1:-start}"
|
|
|
|
info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
|
|
warn() { printf '\033[1;33mWARN:\033[0m %s\n' "$*" >&2; }
|
|
fail() { printf '\033[1;31mERROR:\033[0m %s\n' "$*" >&2; exit 1; }
|
|
need_cmd() { command -v "$1" >/dev/null 2>&1 || fail "缺少命令:$1"; }
|
|
|
|
ensure_repo() {
|
|
if [ -d "$APP_DIR/.git" ]; then
|
|
info "使用现有代码目录:$APP_DIR"
|
|
return 0
|
|
fi
|
|
info "克隆代码到:$APP_DIR"
|
|
mkdir -p "$(dirname "$APP_DIR")"
|
|
git clone "$REPO_URL" "$APP_DIR"
|
|
}
|
|
|
|
ensure_deps() {
|
|
need_cmd git
|
|
need_cmd go
|
|
need_cmd npm
|
|
mkdir -p "$DATA_DIR" "$PROMPT_DATA_DIR" "$LOG_DIR" "$PID_DIR"
|
|
if [ ! -d "$APP_DIR/web/node_modules" ]; then
|
|
info "安装前端依赖"
|
|
(cd "$APP_DIR/web" && npm install --legacy-peer-deps)
|
|
fi
|
|
}
|
|
|
|
kill_pid_file() {
|
|
local file="$1"
|
|
if [ -f "$file" ]; then
|
|
local pid
|
|
pid="$(cat "$file" 2>/dev/null || true)"
|
|
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
|
kill "$pid" 2>/dev/null || true
|
|
for _ in $(seq 1 20); do
|
|
kill -0 "$pid" 2>/dev/null || break
|
|
sleep 0.2
|
|
done
|
|
kill -9 "$pid" 2>/dev/null || true
|
|
fi
|
|
rm -f "$file"
|
|
fi
|
|
}
|
|
|
|
stop_app() {
|
|
info "停止 $APP_NAME 源码服务"
|
|
kill_pid_file "$PID_DIR/backend.pid"
|
|
kill_pid_file "$PID_DIR/frontend.pid"
|
|
}
|
|
|
|
start_app() {
|
|
ensure_repo
|
|
ensure_deps
|
|
stop_app
|
|
|
|
info "启动后端:http://127.0.0.1:$API_PORT"
|
|
(
|
|
cd "$APP_DIR"
|
|
PORT="$API_PORT" \
|
|
DATABASE_DSN="$DB_PATH" \
|
|
PROMPT_DATA_DIR="$PROMPT_DATA_DIR" \
|
|
nohup go run . >>"$LOG_DIR/backend.log" 2>&1 &
|
|
echo $! >"$PID_DIR/backend.pid"
|
|
)
|
|
|
|
info "启动前端:http://127.0.0.1:$WEB_PORT"
|
|
(
|
|
cd "$APP_DIR/web"
|
|
API_BASE_URL="http://127.0.0.1:$API_PORT" \
|
|
nohup npm run dev -- -H "$HOST" -p "$WEB_PORT" >>"$LOG_DIR/frontend.log" 2>&1 &
|
|
echo $! >"$PID_DIR/frontend.pid"
|
|
)
|
|
|
|
info "等待服务就绪"
|
|
for _ in $(seq 1 120); do
|
|
if curl -fsS "http://127.0.0.1:$API_PORT/api/health" >/dev/null 2>&1 && \
|
|
curl -fsS "http://127.0.0.1:$WEB_PORT/api/health" >/dev/null 2>&1; then
|
|
info "启动完成"
|
|
printf '前端: http://127.0.0.1:%s\n' "$WEB_PORT"
|
|
printf '后端: http://127.0.0.1:%s\n' "$API_PORT"
|
|
printf '账号: admin\n'
|
|
printf '默认密码: infinite-canvas(如已改过,以数据库中现有用户为准)\n'
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
warn "服务未在预期时间内完成健康检查,请查看日志:"
|
|
printf ' tail -f %q %q\n' "$LOG_DIR/backend.log" "$LOG_DIR/frontend.log"
|
|
return 1
|
|
}
|
|
|
|
status_app() {
|
|
for name in backend frontend; do
|
|
local file="$PID_DIR/$name.pid"
|
|
if [ -f "$file" ] && kill -0 "$(cat "$file")" 2>/dev/null; then
|
|
printf '%s: running pid=%s\n' "$name" "$(cat "$file")"
|
|
else
|
|
printf '%s: stopped\n' "$name"
|
|
fi
|
|
done
|
|
curl -fsS "http://127.0.0.1:$API_PORT/api/health" 2>/dev/null && printf ' backend_http=ok\n' || true
|
|
curl -fsS "http://127.0.0.1:$WEB_PORT/api/health" 2>/dev/null && printf ' frontend_proxy=ok\n' || true
|
|
}
|
|
|
|
case "$MODE" in
|
|
start|install) start_app ;;
|
|
stop) stop_app ;;
|
|
restart) stop_app; start_app ;;
|
|
status) status_app ;;
|
|
*)
|
|
cat <<USAGE
|
|
Usage: $0 [start|install|stop|restart|status]
|
|
|
|
Environment overrides:
|
|
APP_DIR=$APP_DIR
|
|
REPO_URL=$REPO_URL
|
|
WEB_PORT=$WEB_PORT
|
|
API_PORT=$API_PORT
|
|
HOST=$HOST
|
|
USAGE
|
|
exit 2
|
|
;;
|
|
esac
|