From 7b33b745a0b2c0994492d3b0849babe099db073b Mon Sep 17 00:00:00 2001 From: Nexus Deploy Date: Fri, 5 Jun 2026 22:52:44 +0800 Subject: [PATCH] feat(deploy): add 1Panel Docker one-click install script Automates clone, docker/.env.prod, compose up, and health check for 2C8G prod stack. --- deploy/install-1panel-docker.sh | 270 ++++++++++++++++++ ...6-06-04-1panel-one-click-install-script.md | 33 +++ .../2026-06-04-1panel-docker-production.md | 21 +- 3 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 deploy/install-1panel-docker.sh create mode 100644 docs/changelog/2026-06-04-1panel-one-click-install-script.md diff --git a/deploy/install-1panel-docker.sh b/deploy/install-1panel-docker.sh new file mode 100644 index 00000000..28b715cb --- /dev/null +++ b/deploy/install-1panel-docker.sh @@ -0,0 +1,270 @@ +#!/usr/bin/env bash +# Nexus 6.0 — 1Panel + Docker Compose 一键安装(生产 / 2C8G 调优) +# +# 用法(在服务器 root 或 sudo 下): +# export NEXUS_GITEA_TOKEN='你的Gitea令牌' # 勿提交 Git +# export NEXUS_DOMAIN='api.synaglobal.vip' +# bash deploy/install-1panel-docker.sh +# +# 从宝塔 .env 迁密钥: +# export NEXUS_FROM_ENV='/www/wwwroot/api.synaglobal.vip/.env' +# bash deploy/install-1panel-docker.sh +# +# 已 clone 到 /opt/nexus 时: +# cd /opt/nexus && bash deploy/install-1panel-docker.sh --skip-clone +# +# 环境变量: +# NEXUS_ROOT 默认 /opt/nexus +# NEXUS_GITEA_HOST 默认 66.154.115.8:3000 +# NEXUS_GITEA_TOKEN Gitea HTTP 克隆/拉取用 +# NEXUS_DOMAIN 公网域名(写入 CORS/API_BASE_URL) +# NEXUS_FROM_ENV 旧机 .env 路径,导入 NEXUS_* 与可选 MYSQL 密码 + +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +info() { echo -e "${GREEN}[INFO]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } + +NEXUS_ROOT="${NEXUS_ROOT:-/opt/nexus}" +NEXUS_GITEA_HOST="${NEXUS_GITEA_HOST:-66.154.115.8:3000}" +NEXUS_GITEA_REPO="${NEXUS_GITEA_REPO:-admin/Nexus.git}" +NEXUS_DOMAIN="${NEXUS_DOMAIN:-api.synaglobal.vip}" +COMPOSE_FILE="docker/docker-compose.prod.yml" +ENV_PROD="docker/.env.prod" +SKIP_CLONE=false +FRESH_INSTALL=false +FROM_ENV="${NEXUS_FROM_ENV:-}" + +usage() { + sed -n '2,22p' "$0" | sed 's/^# \{0,1\}//' + exit 0 +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) usage ;; + --skip-clone) SKIP_CLONE=true; shift ;; + --fresh) FRESH_INSTALL=true; shift ;; + --domain) NEXUS_DOMAIN="$2"; shift 2 ;; + --from-env) FROM_ENV="$2"; shift 2 ;; + --root) NEXUS_ROOT="$2"; shift 2 ;; + *) + error "Unknown option: $1" + usage + ;; + esac +done + +if [[ "$(id -u)" -ne 0 ]]; then + error "请使用 root 运行,或: sudo bash $0" + exit 1 +fi + +rand_secret() { + openssl rand -hex 24 2>/dev/null || head -c 24 /dev/urandom | od -An -tx1 | tr -d ' \n' +} + +rand_pass() { + openssl rand -hex 16 2>/dev/null || head -c 16 /dev/urandom | od -An -tx1 | tr -d ' \n' +} + +read_env_val() { + local file="$1" key="$2" + grep -E "^${key}=" "$file" 2>/dev/null | head -1 | cut -d= -f2- | tr -d '\r"' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' +} + +ensure_packages() { + if command -v apt-get >/dev/null 2>&1; then + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq + apt-get install -y -qq git curl openssl ca-certificates >/dev/null 2>&1 || true + fi +} + +ensure_docker() { + if docker compose version >/dev/null 2>&1; then + info "Docker Compose: $(docker compose version --short 2>/dev/null || docker compose version)" + return 0 + fi + error "未检测到 docker compose。" + error "请在 1Panel → 容器 中安装 Docker,或: apt install -y docker.io docker-compose-v2" + exit 1 +} + +clone_or_pull() { + local url + if [[ -n "${NEXUS_GITEA_TOKEN:-}" ]]; then + url="http://admin:${NEXUS_GITEA_TOKEN}@${NEXUS_GITEA_HOST}/${NEXUS_GITEA_REPO}" + else + url="http://${NEXUS_GITEA_HOST}/${NEXUS_GITEA_REPO}" + fi + + if [[ "$SKIP_CLONE" == true ]]; then + if [[ ! -d "$NEXUS_ROOT/.git" ]]; then + error "--skip-clone 但 $NEXUS_ROOT 不是 git 仓库" + exit 1 + fi + info "跳过克隆,在 $NEXUS_ROOT 执行 git pull..." + git -C "$NEXUS_ROOT" pull "$url" main || git -C "$NEXUS_ROOT" pull origin main + return 0 + fi + + mkdir -p "$(dirname "$NEXUS_ROOT")" + if [[ -d "$NEXUS_ROOT/.git" ]]; then + info "仓库已存在,git pull..." + git -C "$NEXUS_ROOT" pull "$url" main || git -C "$NEXUS_ROOT" pull origin main + return 0 + fi + + if [[ -d "$NEXUS_ROOT" ]] && [[ -n "$(ls -A "$NEXUS_ROOT" 2>/dev/null)" ]]; then + error "目录非空且不是 git 仓库: $NEXUS_ROOT" + error "请清空后重试,或: export NEXUS_ROOT=/opt/nexus && rm -rf /opt/nexus" + exit 1 + fi + + rm -rf "$NEXUS_ROOT" + mkdir -p "$NEXUS_ROOT" + info "克隆 Nexus → $NEXUS_ROOT ..." + if [[ -n "${NEXUS_GITEA_TOKEN:-}" ]]; then + git clone "$url" "$NEXUS_ROOT" + else + warn "未设置 NEXUS_GITEA_TOKEN,尝试匿名 clone(可能失败)" + git clone "http://${NEXUS_GITEA_HOST}/${NEXUS_GITEA_REPO}" "$NEXUS_ROOT" + fi +} + +write_env_prod() { + local root="$1" + local env_file="$root/$ENV_PROD" + local example="$root/docker/.env.prod.example" + + if [[ ! -f "$example" ]]; then + error "缺少 $example" + exit 1 + fi + + if [[ -f "$env_file" ]]; then + warn "$env_file 已存在,跳过生成(仅更新域名行)" + sed -i "s|^NEXUS_CORS_ORIGINS=.*|NEXUS_CORS_ORIGINS=https://${NEXUS_DOMAIN}|" "$env_file" + sed -i "s|^NEXUS_API_BASE_URL=.*|NEXUS_API_BASE_URL=https://${NEXUS_DOMAIN}|" "$env_file" + chmod 600 "$env_file" + return 0 + fi + + local mysql_root mysql_pass secret api enc + mysql_root="$(rand_pass)" + mysql_pass="$(rand_pass)" + + if [[ -n "$FROM_ENV" && -f "$FROM_ENV" ]]; then + info "从 $FROM_ENV 导入密钥..." + secret="$(read_env_val "$FROM_ENV" NEXUS_SECRET_KEY)" + api="$(read_env_val "$FROM_ENV" NEXUS_API_KEY)" + enc="$(read_env_val "$FROM_ENV" NEXUS_ENCRYPTION_KEY)" + local old_root old_pass + old_root="$(read_env_val "$FROM_ENV" MYSQL_ROOT_PASSWORD)" + old_pass="$(read_env_val "$FROM_ENV" MYSQL_PASSWORD)" + [[ -n "$old_root" ]] && mysql_root="$old_root" + [[ -n "$old_pass" ]] && mysql_pass="$old_pass" + # DATABASE_URL 里可能带密码 + if [[ -z "$old_pass" ]]; then + local dburl + dburl="$(read_env_val "$FROM_ENV" NEXUS_DATABASE_URL)" + if [[ "$dburl" =~ nexus:([^@]+)@ ]]; then + mysql_pass="${BASH_REMATCH[1]}" + fi + fi + elif [[ "$FRESH_INSTALL" == true ]]; then + secret="" + api="" + enc="" + info "全新安装模式:NEXUS_*_KEY 留空,完成后访问 /app/install.html" + else + secret="$(rand_secret)" + api="$(rand_secret)" + enc="" + warn "未指定 --from-env / --fresh,已生成随机 SECRET_KEY/API_KEY(仅适合新机测试)" + warn "生产迁机请务必: export NEXUS_FROM_ENV=/path/to/.env" + fi + + cp "$example" "$env_file" + chmod 600 "$env_file" + + sed -i "s|^MYSQL_ROOT_PASSWORD=.*|MYSQL_ROOT_PASSWORD=${mysql_root}|" "$env_file" + sed -i "s|^MYSQL_PASSWORD=.*|MYSQL_PASSWORD=${mysql_pass}|" "$env_file" + sed -i "s|^NEXUS_SECRET_KEY=.*|NEXUS_SECRET_KEY=${secret}|" "$env_file" + sed -i "s|^NEXUS_API_KEY=.*|NEXUS_API_KEY=${api}|" "$env_file" + sed -i "s|^NEXUS_ENCRYPTION_KEY=.*|NEXUS_ENCRYPTION_KEY=${enc}|" "$env_file" + sed -i "s|^NEXUS_CORS_ORIGINS=.*|NEXUS_CORS_ORIGINS=https://${NEXUS_DOMAIN}|" "$env_file" + sed -i "s|^NEXUS_API_BASE_URL=.*|NEXUS_API_BASE_URL=https://${NEXUS_DOMAIN}|" "$env_file" + + info "已写入 $env_file" + info " MYSQL_ROOT_PASSWORD 与 MYSQL_PASSWORD 已设置(见文件)" + if [[ "$FRESH_INSTALL" != true && -n "$FROM_ENV" ]]; then + info " 已从旧 .env 导入 NEXUS 密钥" + fi +} + +compose_up() { + local root="$1" + cd "$root" + info "构建并启动 Docker Compose(首次可能 10~20 分钟)..." + docker compose -f "$COMPOSE_FILE" --env-file "$ENV_PROD" up -d --build +} + +verify_health() { + local root="$1" + local i health spa + info "等待服务就绪..." + for i in $(seq 1 60); do + health=$(curl -sf "http://127.0.0.1:8600/health" 2>/dev/null || true) + if [[ "$health" == "ok" ]]; then + spa=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:8600/app/" 2>/dev/null || echo "000") + echo "" + info "═══ 安装完成 ═══" + info " /health → ok" + info " /app/ → HTTP $spa" + info " 代码目录: $root" + echo "" + info "请在 1Panel → 网站 创建站点并反代: http://127.0.0.1:8600" + info " 域名: ${NEXUS_DOMAIN}" + info " 可选自定义配置: $root/deploy/1panel/openresty-nexus.conf.example" + if [[ "$FRESH_INSTALL" == true ]] || ! grep -q '^NEXUS_SECRET_KEY=.\+' "$root/$ENV_PROD" 2>/dev/null; then + info " 浏览器打开: https://${NEXUS_DOMAIN}/app/install.html 完成安装向导" + else + info " 浏览器打开: https://${NEXUS_DOMAIN}/app/" + fi + echo "" + info "常用命令:" + info " cd $root && docker compose -f $COMPOSE_FILE --env-file $ENV_PROD ps" + info " cd $root && docker compose -f $COMPOSE_FILE --env-file $ENV_PROD logs -f nexus" + return 0 + fi + sleep 5 + done + error "健康检查超时。查看日志:" + error " cd $root && docker compose -f $COMPOSE_FILE --env-file $ENV_PROD logs --tail=80 nexus" + exit 1 +} + +main() { + echo "" + echo "==========================================" + echo " Nexus 1Panel + Docker 一键安装" + echo "==========================================" + echo "" + + ensure_packages + ensure_docker + clone_or_pull + write_env_prod "$NEXUS_ROOT" + compose_up "$NEXUS_ROOT" + verify_health "$NEXUS_ROOT" +} + +main "$@" diff --git a/docs/changelog/2026-06-04-1panel-one-click-install-script.md b/docs/changelog/2026-06-04-1panel-one-click-install-script.md new file mode 100644 index 00000000..76456892 --- /dev/null +++ b/docs/changelog/2026-06-04-1panel-one-click-install-script.md @@ -0,0 +1,33 @@ +# Changelog — 1Panel Docker 一键安装脚本 + +## 摘要 + +新增 `deploy/install-1panel-docker.sh`:在 1Panel 服务器上自动 clone/pull、生成 `docker/.env.prod`、启动生产 Compose 并健康检查。 + +## 动机 + +用户刚装好 1Panel,需要减少手工步骤与复制 token 错误。 + +## 涉及文件 + +- `deploy/install-1panel-docker.sh` +- `SECRETS.md` — 用法示例 +- `docs/design/plans/2026-06-04-1panel-docker-production.md` — 引用脚本 + +## 用法 + +```bash +export NEXUS_GITEA_TOKEN='...' +export NEXUS_DOMAIN='api.synaglobal.vip' +bash deploy/install-1panel-docker.sh +``` + +迁机:`export NEXUS_FROM_ENV='/www/wwwroot/api.synaglobal.vip/.env'` + +## 验证 + +`bash -n deploy/install-1panel-docker.sh`;服务器执行后 `/health` → `ok`。 + +## 回滚 + +`docker compose -f docker/docker-compose.prod.yml down`;删除 `/opt/nexus`。 diff --git a/docs/design/plans/2026-06-04-1panel-docker-production.md b/docs/design/plans/2026-06-04-1panel-docker-production.md index acb27d2f..dd1e7e56 100644 --- a/docs/design/plans/2026-06-04-1panel-docker-production.md +++ b/docs/design/plans/2026-06-04-1panel-docker-production.md @@ -119,7 +119,26 @@ sudo bash quick_start.sh --- -### Phase 2 — 准备代码与生产 env +### Phase 2 — 一键安装(推荐) + +在 1Panel **终端**: + +```bash +export NEXUS_GITEA_TOKEN='你的Gitea令牌' +export NEXUS_DOMAIN='api.synaglobal.vip' +# 从宝塔迁机: +# export NEXUS_FROM_ENV='/www/wwwroot/api.synaglobal.vip/.env' + +cd /opt/nexus 2>/dev/null && git pull || true +# 若尚未 clone,从任意目录: +curl -fsSL "http://66.154.115.8:3000/admin/Nexus/raw/branch/main/deploy/install-1panel-docker.sh" | bash +# 或已 clone 后: +# cd /opt/nexus && bash deploy/install-1panel-docker.sh --skip-clone +``` + +脚本:`deploy/install-1panel-docker.sh`(clone → `.env.prod` → `compose up` → `/health`)。 + +### Phase 2b — 手工准备代码与生产 env(可选) 在 1Panel **终端**或 SSH: