#!/usr/bin/env bash # ARTEX 安装脚本: ① 全部 Docker ② 本地编译运行 set -euo pipefail cd "$(cd "$(dirname "$0")" && pwd)" # 自建私有镜像仓库 REGISTRY="registry.gitea.mygoband.com" IMAGE="${REGISTRY}/carrydela/artex" info(){ printf '\033[36m[*]\033[0m %s\n' "$*"; } ok(){ printf '\033[32m[+]\033[0m %s\n' "$*"; } warn(){ printf '\033[33m[!]\033[0m %s\n' "$*"; } die(){ printf '\033[31m[x]\033[0m %s\n' "$*" >&2; exit 1; } ask(){ local p="$1" d="${2:-}" a; read -rp "$p${d:+ [$d]}: " a; echo "${a:-$d}"; } rand(){ head -c 18 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | head -c 24; } # ── docker 环境检测 / 自动安装 ────────────────── ensure_docker(){ if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then ok "已检测到 docker 和 docker compose"; return fi warn "未找到 docker / docker compose" case "$(uname -s)" in Linux) if [ "$(ask '是否自动安装 Docker? (y/n)' y)" = y ]; then curl -fsSL https://get.docker.com | sh sudo usermod -aG docker "$USER" || true ok "Docker 安装完成 (用户组变更需重新登录后才能免 sudo)" else die "请先手动安装 docker 后再运行本脚本" fi ;; Darwin) die "macOS 请安装 Docker Desktop: https://www.docker.com/products/docker-desktop/" ;; *) die "请先手动安装 docker 后再运行本脚本" ;; esac } # ── 登录私有镜像仓库(仅本地无镜像时需要) ───────── login_registry(){ if docker image inspect "${IMAGE}:${ARTEX_TAG}" >/dev/null 2>&1; then info "本地已有镜像 ${IMAGE}:${ARTEX_TAG}, 跳过登录"; return fi if [ -n "${ARTEX_REGISTRY_USER:-}" ]; then if [ -n "${ARTEX_REGISTRY_PASS:-}" ]; then printf '%s' "$ARTEX_REGISTRY_PASS" | docker login "$REGISTRY" -u "$ARTEX_REGISTRY_USER" --password-stdin else read -r -s -p "请输入 $REGISTRY 的密码: " ARTEX_REGISTRY_PASS; echo printf '%s' "$ARTEX_REGISTRY_PASS" | docker login "$REGISTRY" -u "$ARTEX_REGISTRY_USER" --password-stdin fi ok "已登录私有镜像仓库 $REGISTRY" else info "本地无镜像且未设置仓库凭据, 将尝试直接匿名拉取 (私有仓库可能失败)" info "失败时可带凭据重试:" info " ARTEX_REGISTRY_USER= ARTEX_REGISTRY_PASS= $0" fi } # ── ① 全部 Docker ────────────────────────────── install_docker(){ ensure_docker if [ ! -f .env ]; then cp .env.example .env 2>/dev/null || true local pw key pw="$(ask 'Postgres 密码 (直接回车则随机生成)' "$(rand)")" key="$(ask 'ANTHROPIC_API_KEY (可留空, 之后在 UI 中设置)' '')" sed -i.bak "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=${pw}|" .env sed -i.bak "s|^ANTHROPIC_API_KEY=.*|ANTHROPIC_API_KEY=${key}|" .env rm -f .env.bak ok "已生成 .env 文件 (POSTGRES_PASSWORD 已设置)" else info "使用已有的 .env 文件" fi # 从 .env 读取镜像 tag (默认 latest) ARTEX_TAG="$(grep -E '^ARTEX_TAG=' .env 2>/dev/null | cut -d= -f2 || true)" ARTEX_TAG="${ARTEX_TAG:-latest}" export ARTEX_TAG login_registry info "拉取镜像并启动…" docker compose pull || true docker compose up -d ok "启动完成 → http://localhost:8787" info "首次打开后请在页面上完成初始化(设置管理员密码)" info "查看日志: docker compose logs -f artex" } # ── ② 本地编译运行 ──────────────────────────── install_local(){ echo "数据库安装方式:" echo " 1) 连接现有 PostgreSQL" echo " 2) 用 Docker 起一个 PostgreSQL (需要 docker)" case "$(ask '选择' 1)" in 2) ensure_docker local pw; pw="$(ask 'Postgres 密码 (直接回车则随机)' "$(rand)")" docker run -d --name artex-pg -p 5432:5432 \ -e POSTGRES_USER=artex -e POSTGRES_PASSWORD="$pw" -e POSTGRES_DB=artex \ -v artex-pg:/var/lib/postgresql/data postgres:16-alpine DB_HOST=127.0.0.1 DB_PORT=5432 DB_USER=artex DB_PASS="$pw" DB_NAME=artex DB_SSL=disable ;; *) DB_HOST="$(ask '数据库地址' 127.0.0.1)" DB_PORT="$(ask '端口' 5432)" DB_USER="$(ask '账号' artex)" DB_PASS="$(ask '密码' '')" DB_NAME="$(ask '数据库名' artex)" DB_SSL="$(ask 'sslmode (disable/require)' disable)" ;; esac # 生成 config.json cat > config.json </dev/null 2>&1 || die "未找到 Go, 请先安装 Go (>=1.26): https://go.dev/dl/" ok "Go: $(go version)" # 如需内嵌前端, 需用 node 构建静态产物 if command -v npm >/dev/null 2>&1; then info "正在构建前端静态产物…" ( cd web && npm ci && npm run build:static ) rm -rf server/webui/dist && mkdir -p server/webui && cp -r web/out server/webui/dist info "正在编译内嵌前端的单文件二进制…" CGO_ENABLED=0 go build -tags embedui -trimpath -o artex ./cmd/artex else warn "未找到 npm: 将编译不内嵌前端的纯后端 (前端需另用 npm run dev 启动)" CGO_ENABLED=0 go build -o artex ./cmd/artex fi ok "编译完成 → ./artex" info "启动中… (Ctrl-C 退出)" ./artex } echo "==============================" echo " ARTEX 安装" echo " 1) 全部 Docker 安装" echo " 2) 本地运行 (go 编译)" echo "==============================" case "$(ask '选择' 1)" in 1) install_docker ;; 2) install_local ;; *) die "选择无效" ;; esac