#!/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 <