diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..3d96771 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,209 @@ +#!/usr/bin/env bash +# filepath: deploy.sh + +set -euo pipefail + +# 固定镜像名(兼容你原 stop.sh 的做法) +IMAGE_NAME="teres_web_frontend:latest" +FLASK_IMAGE_NAME="teres_web_frontend_flask:latest" +SERVICE_NAME="teres_web_frontend" +NETWORK_NAME="proxy-net" + +usage() { + cat <<'EOF' +用法: ./deploy.sh <命令> [选项] + +常用命令: + start 拉最新代码并构建启动(docker compose up -d --build) + stop 按固定镜像名停止并删除容器,同时删除镜像 + restart 先 stop 再 start + clean 同 stop(停止容器并删除镜像) + +增强命令: + status 查看服务状态(compose ps + 过滤容器) + logs [--follow] 查看日志(加 --follow 跟随) + health 健康检查(HTTP 200) + set [opts] 更新 .env 中 PORT/BUILD_MODE/RAGFLOW_BASE + pull 仅拉取最新代码 + prune 清理悬空镜像与缓存 + +set 支持的选项: + --port 设置端口(映射和构建参数) + --mode 设置构建模式,选择 .env.production 或 .env.flask + --base 设置 ragflow 子路径(默认 /ragflow/) + +示例: + ./deploy.sh start + ./deploy.sh set --port 5173 --mode production --base /ragflow/ + ./deploy.sh logs --follow + ./deploy.sh health +EOF +} + +# 检查命令存在 +require_cmd() { + local cmd="$1"; command -v "$cmd" >/dev/null 2>&1 || { + echo "错误: 未找到命令 '$cmd'"; exit 1; } +} + +# 选择 compose 命令 +compose_cmd() { + if command -v docker-compose >/dev/null 2>&1; then + echo docker-compose + else + echo docker compose + fi +} + +ensure_network() { + if ! docker network inspect "$NETWORK_NAME" >/dev/null 2>&1; then + echo "--- 创建网络: $NETWORK_NAME ---" + docker network create "$NETWORK_NAME" + fi +} + +git_pull() { + echo "--- 拉取最新的代码 ---" + git fetch --all --prune || true + git pull +} + +compose_up() { + local cmd; cmd=$(compose_cmd) + ensure_network + echo "--- docker compose 构建并启动 ---" + $cmd up -d --build +} + +compose_down() { + local cmd; cmd=$(compose_cmd) + echo "--- docker compose down ---" + $cmd down || true +} + +stop_by_image() { + local image="$1" + echo "--- 停止并删除镜像对应的容器: $image ---" + local ids + ids=$(docker ps -a --filter "ancestor=$image" --format "{{.ID}}" || true) + if [[ -n "$ids" ]]; then + for id in $ids; do + docker stop "$id" || true + docker rm "$id" || true + done + fi + echo "--- 删除镜像: $image ---" + docker rmi "$image" || true +} + +status() { + local cmd; cmd=$(compose_cmd) + echo "--- compose ps ---" + $cmd ps || true + echo "--- docker ps (过滤 $SERVICE_NAME) ---" + docker ps --format '{{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Status}}' | grep -E "$SERVICE_NAME|$IMAGE_NAME" || true +} + +logs() { + local follow="false" + if [[ "${1:-}" == "--follow" ]]; then follow="true"; fi + local cmd; cmd=$(compose_cmd) + if [[ "$follow" == "true" ]]; then + $cmd logs -f "$SERVICE_NAME" + else + $cmd logs --tail=300 "$SERVICE_NAME" + fi +} + +# 更新 .env 中某个键值(若不存在则追加) +update_env_var() { + local key="$1"; shift + local val="$1"; shift + touch .env + if grep -qE "^\s*${key}\s*=\s*" .env; then + # 使用正则替换整行,保留键名 + sed -i.bak -E "s|^\s*(${key})\s*=\s*.*|\1=${val}|" .env + else + echo "${key}=${val}" >> .env + fi +} + +health() { + # 从 .env 读取,带默认值 + local PORT=${PORT:-$(grep -E '^\s*PORT\s*=' .env | sed -E 's/.*=\s*//g' || echo 5173)} + local BASE=${RAGFLOW_BASE:-$(grep -E '^\s*RAGFLOW_BASE\s*=' .env | sed -E 's/.*=\s*//g' || echo /ragflow/)} + local host="http://127.0.0.1:${PORT}" + echo "--- 健康检查: ${host} 与 ${host}${BASE} ---" + if command -v curl >/dev/null 2>&1; then + curl -fsS -o /dev/null "${host}" && echo "根应用 OK" || echo "根应用失败" + curl -fsS -o /dev/null "${host}${BASE}" && echo "ragflow 子路径 OK" || echo "ragflow 子路径失败" + else + wget -q --spider "${host}" && echo "根应用 OK" || echo "根应用失败" + wget -q --spider "${host}${BASE}" && echo "ragflow 子路径 OK" || echo "ragflow 子路径失败" + fi +} + +prune() { + echo "--- 清理悬空镜像与构建缓存 ---" + docker image prune -f || true + docker builder prune -f || true +} + +cmd_start() { + git_pull + compose_up +} + +cmd_stop() { + compose_down + stop_by_image "$IMAGE_NAME" + stop_by_image "$FLASK_IMAGE_NAME" +} + +cmd_restart() { + cmd_stop + cmd_start +} + +cmd_clean() { cmd_stop; } + +cmd_pull() { git_pull; } + +cmd_set() { + # 解析选项 + local port="" mode="" base="" + while [[ $# -gt 0 ]]; do + case "$1" in + --port) port="$2"; shift 2;; + --mode) mode="$2"; shift 2;; + --base) base="$2"; shift 2;; + *) echo "未知选项: $1"; usage; exit 1;; + esac + done + [[ -n "$port" ]] && update_env_var PORT "$port" + [[ -n "$mode" ]] && update_env_var BUILD_MODE "$mode" + [[ -n "$base" ]] && update_env_var RAGFLOW_BASE "$base" + echo "--- .env 已更新 ---" +} + +main() { + require_cmd docker + local subcmd="${1:-}" + case "$subcmd" in + start) shift; cmd_start "$@" ;; + stop) shift; cmd_stop "$@" ;; + restart) shift; cmd_restart "$@" ;; + clean) shift; cmd_clean "$@" ;; + status) shift; status "$@" ;; + logs) shift; logs "${1:-}" ;; + health) shift; health "$@" ;; + set) shift; cmd_set "$@" ;; + pull) shift; cmd_pull "$@" ;; + prune) shift; prune "$@" ;; + ""|help|-h|--help) usage ;; + *) echo "未知命令: $subcmd"; usage; exit 1 ;; + esac + echo "--- 完成 ---" +} + +main "$@" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ef3f7a0 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +networks: + proxy-net: + external: true # 使用已存在的 proxy-net 网络 + +services: + teres_web_frontend: + build: + context: . + dockerfile: Dockerfile + args: + BUILD_MODE: ${BUILD_MODE:-production} + RAGFLOW_BASE: ${RAGFLOW_BASE:-/ragflow/} + PORT: ${PORT:-5173} + image: teres_web_frontend:latest + container_name: teres_web_frontend + networks: + - proxy-net # 加入 NPM 的网络 + ports: + - "${PORT:-5173}:${PORT:-5173}" + restart: unless-stopped \ No newline at end of file