Skip to content

Commit 6c02cb9

Browse files
committed
feat(deploy): interactive deploy script with remote bootstrap and bind-host selection
- deploy.sh: add 6-step wizard (port / bind / db / secrets / build / confirm) with auto-bootstrap that clones the repo when launched via `bash <(curl ...)` and reads input from /dev/tty for piped invocation - deploy.sh: print full admin secret and detect LAN/public IPs after deploy - compose: bind ports through ${BIND_HOST:-0.0.0.0} so 127.0.0.1 mode keeps the service off external interfaces - README: document one-line remote install and bind-host options
1 parent cc3dc55 commit 6c02cb9

6 files changed

Lines changed: 210 additions & 24 deletions

File tree

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Codex2API 是一个基于 **Go + Gin + React/Vite** 的 Codex 反向代理与管
5454
- [在线 Demo](#在线-demo)
5555
- [界面预览](#界面预览)
5656
- [快速部署](#快速部署)
57+
- [一键交互部署 (推荐)](#一键交互部署-推荐)
5758
- [完整文档](#完整文档)
5859
- [升级与本地开发](#升级与本地开发)
5960
- [环境配置](#环境配置)
@@ -73,6 +74,43 @@ Codex2API 是一个基于 **Go + Gin + React/Vite** 的 Codex 反向代理与管
7374

7475
> 详细部署指南请参考:[DEPLOYMENT.md](docs/DEPLOYMENT.md)
7576
77+
### 一键交互部署 (推荐)
78+
79+
`deploy.sh` 提供 6 步交互式向导,依次询问 **端口 / 监听范围 / 数据库 / 密钥 / 构建方式 / 确认**,自动生成 `.env` 并拉起容器。
80+
81+
**场景 1:尚未克隆仓库(一行远程拉起)**
82+
83+
```bash
84+
bash <(curl -sSL https://raw.githubusercontent.com/james-6-23/codex2api/main/deploy.sh)
85+
```
86+
87+
脚本会自动检测当前目录是否是 `codex2api` 仓库,若不是则克隆到 `./codex2api`,进入目录后再执行部署。
88+
89+
**场景 2:已经 `git clone` 到本地**
90+
91+
```bash
92+
git clone https://github.com/james-6-23/codex2api.git
93+
cd codex2api
94+
bash deploy.sh
95+
```
96+
97+
**监听范围选项**
98+
99+
| 选项 | 绑定地址 | 适用场景 |
100+
| --- | --- | --- |
101+
| 1) 仅本机访问 | `127.0.0.1` | 服务放在 nginx / Caddy 等反向代理后端,外网无法直接访问端口 |
102+
| 2) 全部网络 (默认) | `0.0.0.0` | 直接通过服务器 IP 对外暴露,部署完成后会展示本机 / 内网 / 公网地址 |
103+
104+
绑定地址会写入 `.env``BIND_HOST`,后续可手动修改后 `docker compose up -d` 重启生效。
105+
106+
**可选环境变量**(用于自定义自举行为)
107+
108+
| 变量 | 默认 | 说明 |
109+
| --- | --- | --- |
110+
| `CODEX2API_REPO_URL` | `https://github.com/james-6-23/codex2api.git` | 克隆使用的仓库地址 |
111+
| `CODEX2API_REPO_BRANCH` | `main` | 克隆使用的分支 |
112+
| `CODEX2API_DIR_NAME` | `codex2api` | 克隆到本地的目录名 |
113+
76114
### 部署模式总览
77115

78116
| 模式 | 文件 | 适用场景 |

deploy.sh

Lines changed: 168 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ EOF
3636
echo ""
3737
}
3838

39+
# 输入源:兼容 `bash <(curl ...)` / 管道执行场景,强制从终端读取
40+
_INPUT_FD="/dev/tty"
41+
if [[ ! -r "$_INPUT_FD" ]]; then
42+
_INPUT_FD="/dev/stdin"
43+
fi
44+
3945
# 读取用户输入,支持默认值
4046
ask() {
4147
local prompt="$1" default="$2" varname="$3"
@@ -44,7 +50,7 @@ ask() {
4450
else
4551
printf "${BOLD}%s${NC}: " "$prompt"
4652
fi
47-
read -r input
53+
read -r input < "$_INPUT_FD"
4854
eval "$varname=\"${input:-$default}\""
4955
}
5056

@@ -56,7 +62,7 @@ ask_secret() {
5662
else
5763
printf "${BOLD}%s${NC} (留空则自动生成): " "$prompt"
5864
fi
59-
read -rs input
65+
read -rs input < "$_INPUT_FD"
6066
echo ""
6167
eval "$varname=\"${input:-$default}\""
6268
}
@@ -72,6 +78,61 @@ gen_secret() {
7278
fi
7379
}
7480

81+
# ---------- 自举:确保位于 codex2api 仓库目录 ----------
82+
# 触发条件:
83+
# 1) 通过 `bash <(curl ...)` 远程拉起 (BASH_SOURCE 不是真实文件)
84+
# 2) 或当前目录缺少必要的 compose / deploy.sh 文件
85+
# 行为:
86+
# - 若已在仓库目录: 直接返回
87+
# - 否则: clone 仓库到 ./codex2api,切入并 exec ./deploy.sh
88+
REPO_URL="${CODEX2API_REPO_URL:-https://github.com/james-6-23/codex2api.git}"
89+
REPO_BRANCH="${CODEX2API_REPO_BRANCH:-main}"
90+
REPO_DIR_NAME="${CODEX2API_DIR_NAME:-codex2api}"
91+
92+
is_codex2api_repo() {
93+
[[ -f "docker-compose.yml" ]] && [[ -f "deploy.sh" ]] \
94+
&& grep -q '^name: codex2api' docker-compose.yml 2>/dev/null
95+
}
96+
97+
bootstrap_repo() {
98+
# 已经在仓库目录里:什么都不做
99+
if is_codex2api_repo; then
100+
success "检测到当前目录为 codex2api 仓库"
101+
return 0
102+
fi
103+
104+
warn "当前目录不是 codex2api 仓库,进入自动拉取流程"
105+
106+
if ! command -v git >/dev/null 2>&1; then
107+
error "未找到 git,请先安装 git 后重试"
108+
fi
109+
110+
# 如果同名目录已存在且是仓库,直接复用
111+
if [[ -d "$REPO_DIR_NAME/.git" ]]; then
112+
info "发现已有目录 $REPO_DIR_NAME,尝试更新到最新代码..."
113+
(cd "$REPO_DIR_NAME" && git fetch --depth=1 origin "$REPO_BRANCH" && git reset --hard "origin/$REPO_BRANCH") \
114+
|| warn "拉取更新失败,将沿用已有代码继续部署"
115+
elif [[ -e "$REPO_DIR_NAME" ]]; then
116+
error "目录 $REPO_DIR_NAME 已存在但不是 git 仓库,请手动处理后重试"
117+
else
118+
info "克隆仓库: $REPO_URL ($REPO_BRANCH) → ./$REPO_DIR_NAME"
119+
git clone --depth=1 --branch "$REPO_BRANCH" "$REPO_URL" "$REPO_DIR_NAME" \
120+
|| error "git clone 失败,请检查网络或仓库地址"
121+
fi
122+
123+
cd "$REPO_DIR_NAME" || error "无法进入 $REPO_DIR_NAME 目录"
124+
125+
if ! is_codex2api_repo; then
126+
error "克隆后仍未识别为 codex2api 仓库,请手动检查"
127+
fi
128+
129+
success "已切换到 $(pwd)"
130+
info "重新运行 ./deploy.sh 完成部署..."
131+
echo ""
132+
# exec 掉本进程,避免远程脚本/旧上下文继续运行
133+
exec bash ./deploy.sh "$@"
134+
}
135+
75136
# ---------- 前置检查 ----------
76137
preflight() {
77138
info "检查运行环境..."
@@ -94,7 +155,7 @@ preflight() {
94155
# ---------- 第一步:端口 ----------
95156
step_port() {
96157
echo ""
97-
printf "${BOLD}${CYAN}━━━ 1/5 服务端口 ━━━${NC}\n"
158+
printf "${BOLD}${CYAN}━━━ 1/6 服务端口 ━━━${NC}\n"
98159
ask "服务监听端口" "8080" PORT
99160

100161
if ! [[ "$PORT" =~ ^[0-9]+$ ]] || (( PORT < 1 || PORT > 65535 )); then
@@ -103,10 +164,37 @@ step_port() {
103164
success "端口: $PORT"
104165
}
105166

106-
# ---------- 第二步:数据库模式 ----------
167+
# ---------- 第二步:监听范围 ----------
168+
step_bind() {
169+
echo ""
170+
printf "${BOLD}${CYAN}━━━ 2/6 监听范围 ━━━${NC}\n"
171+
echo ""
172+
echo " 1) 仅本机访问 — 绑定 127.0.0.1,外部无法访问 (内网/反向代理后端推荐)"
173+
echo " 2) 全部网络 — 绑定 0.0.0.0,可通过内网/公网 IP 访问 (默认)"
174+
echo ""
175+
ask "请选择 (1 或 2)" "2" BIND_CHOICE
176+
177+
case "$BIND_CHOICE" in
178+
1|local|loopback|127*)
179+
BIND_HOST="127.0.0.1"
180+
BIND_MODE="loopback"
181+
success "监听范围: 仅本机 (127.0.0.1)"
182+
;;
183+
2|all|public|0*)
184+
BIND_HOST="0.0.0.0"
185+
BIND_MODE="all"
186+
success "监听范围: 全部网络 (0.0.0.0)"
187+
;;
188+
*)
189+
error "无效选择: $BIND_CHOICE"
190+
;;
191+
esac
192+
}
193+
194+
# ---------- 第三步:数据库模式 ----------
107195
step_database() {
108196
echo ""
109-
printf "${BOLD}${CYAN}━━━ 2/5 数据库模式 ━━━${NC}\n"
197+
printf "${BOLD}${CYAN}━━━ 3/6 数据库模式 ━━━${NC}\n"
110198
echo ""
111199
echo " 1) SQLite — 轻量单文件,适合个人 / 测试"
112200
echo " 2) PG+Redis — PostgreSQL + Redis,适合生产 / 多并发"
@@ -151,10 +239,10 @@ step_pg_config() {
151239
ask_secret "Redis 密码 (留空则无密码)" "" REDIS_PASS
152240
}
153241

154-
# ---------- 第三步:密钥 ----------
242+
# ---------- 第四步:密钥 ----------
155243
step_secrets() {
156244
echo ""
157-
printf "${BOLD}${CYAN}━━━ 3/5 安全密钥 ━━━${NC}\n"
245+
printf "${BOLD}${CYAN}━━━ 4/6 安全密钥 ━━━${NC}\n"
158246
echo ""
159247

160248
ask_secret "管理后台密钥 (ADMIN_SECRET)" "" ADMIN_SECRET
@@ -167,10 +255,10 @@ step_secrets() {
167255
ask "下游 API 密钥 (CODEX_API_KEYS, 多个用逗号分隔, 留空不启用)" "" API_KEYS
168256
}
169257

170-
# ---------- 第四步:构建方式 ----------
258+
# ---------- 第五步:构建方式 ----------
171259
step_build_mode() {
172260
echo ""
173-
printf "${BOLD}${CYAN}━━━ 4/5 构建方式 ━━━${NC}\n"
261+
printf "${BOLD}${CYAN}━━━ 5/6 构建方式 ━━━${NC}\n"
174262
echo ""
175263
echo " 1) 拉取镜像 — 使用预构建镜像 (ghcr.io),部署快"
176264
echo " 2) 本地构建 — 从源码编译,适合自定义修改"
@@ -192,12 +280,17 @@ step_build_mode() {
192280
esac
193281
}
194282

195-
# ---------- 第五步:确认 ----------
283+
# ---------- 第六步:确认 ----------
196284
step_confirm() {
197285
echo ""
198-
printf "${BOLD}${CYAN}━━━ 5/5 配置确认 ━━━${NC}\n"
286+
printf "${BOLD}${CYAN}━━━ 6/6 配置确认 ━━━${NC}\n"
199287
echo ""
200288
echo " 端口: $PORT"
289+
if [[ "$BIND_MODE" == "loopback" ]]; then
290+
echo " 监听范围: 127.0.0.1 (仅本机访问)"
291+
else
292+
echo " 监听范围: 0.0.0.0 (全部网络)"
293+
fi
201294
echo " 数据库: $DB_MODE"
202295
if [[ "$DB_MODE" == "sqlite" ]]; then
203296
echo " 数据路径: $SQLITE_PATH"
@@ -208,7 +301,7 @@ step_confirm() {
208301
echo " Redis: 内置容器"
209302
fi
210303
echo " 构建方式: $( [[ "$BUILD_MODE" == "image" ]] && echo "拉取镜像" || echo "本地构建" )"
211-
echo " 管理密钥: ${ADMIN_SECRET:0:6}******"
304+
echo " 管理密钥: ${ADMIN_SECRET}"
212305
if [[ -n "${API_KEYS:-}" ]]; then
213306
echo " API 密钥: 已设置"
214307
else
@@ -242,6 +335,9 @@ generate_env() {
242335
# 服务端口
243336
CODEX_PORT=${PORT}
244337
338+
# 端口绑定地址 (127.0.0.1=仅本机, 0.0.0.0=全部网络)
339+
BIND_HOST=${BIND_HOST}
340+
245341
# 管理后台密钥
246342
ADMIN_SECRET=${ADMIN_SECRET}
247343
@@ -265,6 +361,9 @@ EOF
265361
# 服务端口
266362
CODEX_PORT=${PORT}
267363
364+
# 端口绑定地址 (127.0.0.1=仅本机, 0.0.0.0=全部网络)
365+
BIND_HOST=${BIND_HOST}
366+
268367
# 管理后台密钥
269368
ADMIN_SECRET=${ADMIN_SECRET}
270369
@@ -341,24 +440,73 @@ deploy() {
341440
echo ""
342441
success "部署完成!"
343442
echo ""
443+
444+
local PUBLIC_IP="" LAN_IP=""
445+
446+
if [[ "$BIND_MODE" == "all" ]]; then
447+
# 仅在对外开放时才探测/展示对外 IP
448+
PUBLIC_IP=$(curl -fsS4 --max-time 3 https://ifconfig.me 2>/dev/null \
449+
|| curl -fsS4 --max-time 3 https://api.ipify.org 2>/dev/null \
450+
|| curl -fsS4 --max-time 3 https://ipinfo.io/ip 2>/dev/null \
451+
|| true)
452+
PUBLIC_IP=$(echo "$PUBLIC_IP" | tr -d '[:space:]')
453+
454+
if command -v hostname >/dev/null 2>&1; then
455+
LAN_IP=$(hostname -I 2>/dev/null | awk '{print $1}' || true)
456+
fi
457+
if [[ -z "$LAN_IP" ]] && command -v ip >/dev/null 2>&1; then
458+
LAN_IP=$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1); exit}}')
459+
fi
460+
if [[ -z "$LAN_IP" ]] && command -v ifconfig >/dev/null 2>&1; then
461+
LAN_IP=$(ifconfig 2>/dev/null | awk '/inet /{print $2}' | grep -v '^127\.' | head -n1)
462+
fi
463+
fi
464+
344465
echo " ┌──────────────────────────────────────────┐"
345-
echo " │ │"
346-
echo " │ 服务地址: http://localhost:${PORT} "
347-
echo " │ 管理后台: http://localhost:${PORT}/admin "
348-
echo " │ 管理密钥: ${ADMIN_SECRET:0:6}****** "
349-
echo " │ │"
350-
echo " │ 查看日志: $COMPOSE_CMD -f $COMPOSE_FILE logs -f"
351-
echo " │ 停止服务: $COMPOSE_CMD -f $COMPOSE_FILE down"
352-
echo " │ │"
466+
echo " │ 部署信息 │"
353467
echo " └──────────────────────────────────────────┘"
354468
echo ""
469+
if [[ "$BIND_MODE" == "loopback" ]]; then
470+
echo " 监听范围 : 127.0.0.1 (仅本机访问)"
471+
echo ""
472+
echo " 本地访问 : http://127.0.0.1:${PORT}"
473+
echo " http://127.0.0.1:${PORT}/admin"
474+
else
475+
echo " 监听范围 : 0.0.0.0 (全部网络)"
476+
echo ""
477+
echo " 本地访问 : http://localhost:${PORT}"
478+
echo " http://localhost:${PORT}/admin"
479+
if [[ -n "$LAN_IP" ]]; then
480+
echo " 内网访问 : http://${LAN_IP}:${PORT}"
481+
echo " http://${LAN_IP}:${PORT}/admin"
482+
fi
483+
if [[ -n "$PUBLIC_IP" ]]; then
484+
echo " 公网访问 : http://${PUBLIC_IP}:${PORT}"
485+
echo " http://${PUBLIC_IP}:${PORT}/admin"
486+
fi
487+
fi
488+
echo ""
489+
echo " 管理密钥 : ${ADMIN_SECRET}"
490+
echo ""
491+
echo " 查看日志 : $COMPOSE_CMD -f $COMPOSE_FILE logs -f"
492+
echo " 停止服务 : $COMPOSE_CMD -f $COMPOSE_FILE down"
493+
echo ""
494+
if [[ "$BIND_MODE" == "all" && -n "$PUBLIC_IP" ]]; then
495+
warn "服务对外开放,请确认防火墙/安全组已放行 ${PORT} 端口"
496+
fi
497+
if [[ "$BIND_MODE" == "loopback" ]]; then
498+
info "如需对外暴露,可重新运行 deploy.sh 选择「全部网络」,或在 .env 中将 BIND_HOST 改为 0.0.0.0"
499+
fi
500+
echo ""
355501
}
356502

357503
# ---------- 主流程 ----------
358504
main() {
359505
banner
506+
bootstrap_repo "$@"
360507
preflight
361508
step_port
509+
step_bind
362510
step_database
363511
step_secrets
364512
step_build_mode

docker-compose.local.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ services:
55
build: .
66
container_name: codex2api
77
ports:
8-
- "${CODEX_PORT:-8080}:${CODEX_PORT:-8080}"
8+
- "${BIND_HOST:-0.0.0.0}:${CODEX_PORT:-8080}:${CODEX_PORT:-8080}"
99
env_file:
1010
- .env
1111
volumes:

docker-compose.sqlite.local.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ services:
55
build: .
66
container_name: codex2api-sqlite-local
77
ports:
8-
- "${CODEX_PORT:-8080}:${CODEX_PORT:-8080}"
8+
- "${BIND_HOST:-0.0.0.0}:${CODEX_PORT:-8080}:${CODEX_PORT:-8080}"
99
env_file:
1010
- .env
1111
volumes:

docker-compose.sqlite.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ services:
55
image: ghcr.io/james-6-23/codex2api:latest
66
container_name: codex2api-sqlite
77
ports:
8-
- "${CODEX_PORT:-8080}:${CODEX_PORT:-8080}"
8+
- "${BIND_HOST:-0.0.0.0}:${CODEX_PORT:-8080}:${CODEX_PORT:-8080}"
99
env_file:
1010
- .env
1111
volumes:

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ services:
55
image: ghcr.io/james-6-23/codex2api:latest
66
container_name: codex2api
77
ports:
8-
- "${CODEX_PORT:-8080}:${CODEX_PORT:-8080}"
8+
- "${BIND_HOST:-0.0.0.0}:${CODEX_PORT:-8080}:${CODEX_PORT:-8080}"
99
env_file:
1010
- .env
1111
volumes:

0 commit comments

Comments
 (0)