#!/bin/bash # 本地站点管理:启动 / 停止 / 重启 / 状态 # 用法:bash sites-manage-local.sh [start|stop|restart|status] [站点名...] # 不加参数则交互选择 # # 站点名: # cc: www-cc cloud-cc token-cc # ai: www-ai cloud-ai token-ai issue assets ldap # all: 全部站点 set -e BASE_DIR="/Users/niuniu/programs/docker" ISSUE_DIR="$BASE_DIR/issue-ai" ASSETS_DIR="$BASE_DIR/assets-ai" LDAP_DIR="$BASE_DIR/ldap-ai" OA_DIR="$BASE_DIR/oa-ai" OA_PORT=6179 # 颜色 GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; RED='\033[0;31m'; NC='\033[0m' log() { printf "${GREEN}[✓]${NC} %s\n" "$1"; } warn() { printf "${YELLOW}[!]${NC} %s\n" "$1"; } err() { printf "${RED}[✗]${NC} %s\n" "$1"; } # 端口 ISSUE_PORT=6176 ASSETS_PORT=6177 LDAP_WEB_PORT=6178 is_port() { lsof -i :"$1" >/dev/null 2>&1; } is_running() { docker ps --format '{{.Names}}' 2>/dev/null | grep -q "^$1$"; } # ============================================================ # 单个 Docker 站点操作 # ============================================================ docker_start() { local name=$1 dir=$2 if is_running $name; then warn "$name 已在运行" else log "启动 $name..." cd "$dir" && docker compose up -d 2>/dev/null && log "$name 已启动" || err "$name 启动失败" fi } docker_stop() { local name=$1 dir=$2 if is_running $name; then log "停止 $name..." cd "$dir" && docker compose down 2>/dev/null && log "$name 已停止" || err "$name 停止失败" else warn "$name 未在运行" fi } # ============================================================ # 单个 Node.js 服务(npm run dev) # ============================================================ node_start() { local name=$1 dir=$2 port=$3 logfile=$4 if is_port $port; then warn "$name 已在运行 (port $port)" else log "启动 $name (port $port)..." cd "$dir" && npm run dev > "$logfile" 2>&1 & sleep 2 is_port $port && log "$name 已启动" || err "$name 启动失败,请查看 $logfile" fi } node_stop() { local name=$1 port=$2 if is_port $port; then kill $(lsof -ti :$port) 2>/dev/null && log "$name 已停止" || warn "$name 停止失败" else warn "$name 未在运行" fi } # ============================================================ # 批量操作 # ============================================================ start_all() { for name in "$@"; do case $name in www-cc) docker_start www-local "$BASE_DIR/www-cc" ;; cloud-cc) docker_start cloud-cc "$BASE_DIR/cloud-cc" ;; token-cc) docker_start token-cc "$BASE_DIR/token-cc" ;; www-ai) docker_start www-local-ai "$BASE_DIR/www-ai" ;; cloud-ai) docker_start cloud-ai "$BASE_DIR/cloud-ai" ;; token-ai) docker_start token-ai "$BASE_DIR/token-ai" ;; issue) node_start issue-ai "$ISSUE_DIR" $ISSUE_PORT "/tmp/issue-ai-dev.log" if ! is_port $ASSETS_PORT; then node_start assets-ai "$ASSETS_DIR" $ASSETS_PORT "/tmp/assets-ai-dev.log" warn "已自动启动 assets-ai(issue 依赖 assets API)" fi ;; assets) node_start assets-ai "$ASSETS_DIR" $ASSETS_PORT "/tmp/assets-ai-dev.log" ;; ldap) docker_start lldap "$LDAP_DIR" if is_port $LDAP_WEB_PORT; then log "LLDAP Web UI: http://localhost:$LDAP_WEB_PORT" fi ;; oa) node_start oa-ai "$OA_DIR" $OA_PORT "/tmp/oa-ai-dev.log" ;; cc) start_all www-cc cloud-cc token-cc ;; ai) start_all ldap www-ai cloud-ai token-ai issue assets oa ;; all) start_all cc ai ;; *) err "未知站点: $name" ;; esac done } stop_all() { for name in "$@"; do case $name in www-cc) docker_stop www-local "$BASE_DIR/www-cc" ;; cloud-cc) docker_stop cloud-cc "$BASE_DIR/cloud-cc" ;; token-cc) docker_stop token-cc "$BASE_DIR/token-cc" ;; www-ai) docker_stop www-local-ai "$BASE_DIR/www-ai" ;; cloud-ai) docker_stop cloud-ai "$BASE_DIR/cloud-ai" ;; token-ai) docker_stop token-ai "$BASE_DIR/token-ai" ;; issue) node_stop issue-ai $ISSUE_PORT ;; assets) node_stop assets-ai $ASSETS_PORT ;; ldap) docker_stop lldap "$LDAP_DIR" ;; oa) node_stop oa-ai $OA_PORT ;; cc) stop_all www-cc cloud-cc token-cc ;; ai) stop_all ldap www-ai cloud-ai token-ai issue assets oa ;; all) stop_all cc ai ;; *) err "未知站点: $name" ;; esac done } restart_all() { stop_all "$@" echo "" log "正在重启..." start_all "$@" } # ============================================================ # 状态 # ============================================================ show_status() { local targets=("$@") # 合并 all/cc/ai local expanded=() for t in "${targets[@]}"; do case $t in all) eval "expanded=(www-cc cloud-cc token-cc ldap www-ai cloud-ai token-ai issue assets oa)" ;; cc) eval "expanded=(\${expanded[@]} www-cc cloud-cc token-cc)" ;; ai) eval "expanded=(\${expanded[@]} ldap www-ai cloud-ai token-ai issue assets oa)" ;; *) expanded+=($t) ;; esac done echo "" printf "${CYAN}=========================================${NC}\n" printf "${CYAN} 当前状态${NC}\n" printf "${CYAN}=========================================${NC}\n" echo "" local running=0 for name in "${expanded[@]}"; do case $name in www-cc|cloud-cc|token-cc|cloud-ai|token-ai) if is_running $name; then printf " ${GREEN}●${NC} %-12s 运行中\n" "$name" running=$((running + 1)) else printf " ${RED}●${NC} %-12s 未运行\n" "$name" fi ;; www-ai) if is_running www-local-ai; then printf " ${GREEN}●${NC} %-12s 运行中\n" "$name" running=$((running + 1)) else printf " ${RED}●${NC} %-12s 未运行\n" "$name" fi ;; issue) if is_port $ISSUE_PORT; then printf " ${GREEN}●${NC} %-12s 运行中 (port %d)\n" "$name" $ISSUE_PORT running=$((running + 1)) else printf " ${RED}●${NC} %-12s 未运行\n" "$name" fi ;; ldap) if is_running lldap; then printf " ${GREEN}●${NC} %-12s 运行中 (port %d)\n" "$name" $LDAP_WEB_PORT running=$((running + 1)) else printf " ${RED}●${NC} %-12s 未运行\n" "$name" fi ;; assets) if is_port $ASSETS_PORT; then printf " ${GREEN}●${NC} %-12s 运行中 (port %d)\n" "$name" $ASSETS_PORT running=$((running + 1)) else printf " ${RED}●${NC} %-12s 未运行\n" "$name" fi ;; oa) if is_port $OA_PORT; then printf " ${GREEN}●${NC} %-12s 运行中 (port %d)\n" "$name" $OA_PORT running=$((running + 1)) else printf " ${RED}●${NC} %-12s 未运行\n" "$name" fi ;; esac done echo "" printf "${CYAN}=========================================${NC}\n" printf "${CYAN} 访问地址${NC}\n" printf "${CYAN}=========================================${NC}\n" echo "" echo " cc 站点:" echo " www-cc http://localhost:5173" echo " cloud-cc http://localhost:5174" echo " token-cc http://localhost:5175" echo "" echo " ai 站点:" echo " www-ai http://localhost:6173" echo " cloud-ai http://localhost:6174" echo " token-ai http://localhost:6175" echo " ldap http://localhost:$LDAP_WEB_PORT (LLDAP Web UI)" echo " oa http://localhost:$OA_PORT" echo " issue http://localhost:$ISSUE_PORT" echo " assets http://localhost:$ASSETS_PORT" echo "" } # ============================================================ # 帮助 # ============================================================ show_help() { echo "" printf "${CYAN}用法:${NC} bash sites-manage-local.sh <命令> [站点名...]\n" echo "" echo " 命令:" echo " start 启动站点" echo " stop 停止站点" echo " restart 重启站点" echo " status 查看状态" echo "" echo " 站点名(可指定多个,空格分隔):" echo " www-cc cloud-cc token-cc" echo " ldap www-ai cloud-ai token-ai issue assets oa" echo " cc — tlyq.cc 全部 (www-cc cloud-cc token-cc)" echo " ai — tlyq.ai 全部 (ldap www-ai cloud-ai token-ai issue assets oa)" echo " all — 全部站点" echo "" echo "示例:" echo " bash sites-manage-local.sh start all # 启动全部" echo " bash sites-manage-local.sh start ai # 启动 ai 全部" echo " bash sites-manage-local.sh start issue assets # 只启 issue + assets" echo " bash sites-manage-local.sh stop www-ai # 只停 www-ai" echo " bash sites-manage-local.sh status # 查看所有状态" echo "" } # ============================================================ # 入口 # ============================================================ ACTION=${1:-} shift || true if [ -z "$ACTION" ]; then show_help exit 0 fi # 解析站点列表 if [ $# -eq 0 ]; then case $ACTION in start|stop|restart) TARGETS=(all) ;; status) TARGETS=(all) ;; help|--help|-h) show_help; exit 0 ;; *) err "未知命令: $ACTION"; show_help; exit 1 ;; esac else TARGETS=("$@") fi case $ACTION in start) start_all "${TARGETS[@]}" ;; stop) stop_all "${TARGETS[@]}" ;; restart) restart_all "${TARGETS[@]}" ;; status) show_status "${TARGETS[@]}" ;; help|--help|-h) show_help ;; *) err "未知命令: $ACTION"; show_help; exit 1 ;; esac