icebear0828 commited on
Commit
7ff102d
·
1 Parent(s): d44918f

feat: Docker image auto-publish to GHCR + Watchtower support

Browse files

- Add docker-publish.yml workflow: multi-arch (amd64/arm64) build on
every push to master, published to ghcr.io/icebear0828/codex-proxy
- Switch docker-compose.yml from local build to pre-built GHCR image
- Add commented Watchtower service block for automatic updates
- Simplify README Docker section: no git clone needed, just curl configs
- Update dashboard Docker hint to mention Watchtower option
- Add dockerAutoUpdateHint i18n key (en/zh)

.github/workflows/docker-publish.yml ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Publish Docker Image
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ workflow_dispatch:
7
+
8
+ concurrency:
9
+ group: docker-publish
10
+ cancel-in-progress: true
11
+
12
+ permissions:
13
+ contents: read
14
+ packages: write
15
+
16
+ jobs:
17
+ publish:
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Read version from package.json
23
+ id: version
24
+ run: echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"
25
+
26
+ - uses: docker/setup-qemu-action@v3
27
+
28
+ - uses: docker/setup-buildx-action@v3
29
+
30
+ - uses: docker/login-action@v3
31
+ with:
32
+ registry: ghcr.io
33
+ username: ${{ github.actor }}
34
+ password: ${{ secrets.GITHUB_TOKEN }}
35
+
36
+ - uses: docker/metadata-action@v5
37
+ id: meta
38
+ with:
39
+ images: ghcr.io/icebear0828/codex-proxy
40
+ tags: |
41
+ type=raw,value=latest
42
+ type=raw,value=v${{ steps.version.outputs.version }}
43
+
44
+ - uses: docker/build-push-action@v6
45
+ with:
46
+ context: .
47
+ platforms: linux/amd64,linux/arm64
48
+ push: true
49
+ tags: ${{ steps.meta.outputs.tags }}
50
+ labels: ${{ steps.meta.outputs.labels }}
51
+ cache-from: type=gha
52
+ cache-to: type=gha,mode=max
CHANGELOG.md CHANGED
@@ -8,6 +8,7 @@
8
 
9
  ### Added
10
 
 
11
  - 双窗口配额显示:Dashboard 账号卡片同时展示主窗口(小时限制)和次窗口(周限制)的用量百分比、进度条和重置时间,后端 `secondary_window` 不再被忽略
12
  - 更新弹窗 + 自动重启:点击"有可用更新"弹出 Modal 显示 changelog,一键更新后服务器自动重启、前端自动刷新,零人工干预(git 模式 spawn 新进程、Docker/Electron 显示对应操作指引)
13
  - Model-aware 多计划账号路由:不同 plan(free/plus/business)的账号自动路由到各自支持的模型,business 账号可继续使用 gpt-5.4 等高端模型 (#57)
 
8
 
9
  ### Added
10
 
11
+ - Docker 镜像自动发布:push master 自动构建多架构(amd64/arm64)镜像到 GHCR(`ghcr.io/icebear0828/codex-proxy`),docker-compose.yml 切换为预构建镜像,支持 Watchtower 自动更新
12
  - 双窗口配额显示:Dashboard 账号卡片同时展示主窗口(小时限制)和次窗口(周限制)的用量百分比、进度条和重置时间,后端 `secondary_window` 不再被忽略
13
  - 更新弹窗 + 自动重启:点击"有可用更新"弹出 Modal 显示 changelog,一键更新后服务器自动重启、前端自动刷新,零人工干预(git 模式 spawn 新进程、Docker/Electron 显示对应操作指引)
14
  - Model-aware 多计划账号路由:不同 plan(free/plus/business)的账号自动路由到各自支持的模型,business 账号可继续使用 gpt-5.4 等高端模型 (#57)
README.md CHANGED
@@ -79,32 +79,48 @@ Docker 就像一个"打包好的盒子",不需要你自己装各种依赖,
79
 
80
  **前置条件**:电脑上已安装 Docker。如果没有,先去 [docker.com](https://www.docker.com/products/docker-desktop/) 下载安装 Docker Desktop。
81
 
82
- **第 1 步:下载项目代码**
83
 
84
  打开终端(Windows 搜索"cmd"或"PowerShell";macOS 搜索"终端"),输入:
85
 
86
  ```bash
87
- git clone https://github.com/icebear0828/codex-proxy.git
88
- cd codex-proxy
89
  ```
90
 
91
- > 如果提示 `git` 不是命令,需要先安装 Git去 [git-scm.com](https://git-scm.com/) 下载安装。
92
-
93
- **第 2 步:创建配置文件并启动**
94
 
95
  ```bash
 
 
96
  cp .env.example .env # 复制一份配置模板
97
- docker compose up -d # 启动服务(后台运行)
98
  ```
99
 
100
- **第 3 步:登**
 
 
 
 
 
 
 
 
101
 
102
  打开浏览器,访问 `http://localhost:8080`,用 ChatGPT 账号登录。
103
 
104
- > 你的账号数据会保存在项目的 `data/` 文件夹里,重启不会丢失。
105
  >
106
  > **其他 Docker 容器要连本服务?** 用你电脑的局域网 IP(如 `http://192.168.x.x:8080/v1`),不要用 `localhost`。
107
 
 
 
 
 
 
 
 
 
 
 
108
  ---
109
 
110
  ### 方式三:源码运行(开发者 / 想改代码的用户)
 
79
 
80
  **前置条件**:电脑上已安装 Docker。如果没有,先去 [docker.com](https://www.docker.com/products/docker-desktop/) 下载安装 Docker Desktop。
81
 
82
+ **第 1 步:创建项目目录**
83
 
84
  打开终端(Windows 搜索"cmd"或"PowerShell";macOS 搜索"终端"),输入:
85
 
86
  ```bash
87
+ mkdir codex-proxy && cd codex-proxy
 
88
  ```
89
 
90
+ **第 2 :下载配置文件**
 
 
91
 
92
  ```bash
93
+ curl -O https://raw.githubusercontent.com/icebear0828/codex-proxy/master/docker-compose.yml
94
+ curl -O https://raw.githubusercontent.com/icebear0828/codex-proxy/master/.env.example
95
  cp .env.example .env # 复制一份配置模板
 
96
  ```
97
 
98
+ > 也可以用 `git clone` 下载完整源码,然后在项目目中 `docker compose up -d`。
99
+
100
+ **第 3 步:启动**
101
+
102
+ ```bash
103
+ docker compose up -d # 拉取镜像并启动(后台运行)
104
+ ```
105
+
106
+ **第 4 步:登录**
107
 
108
  打开浏览器,访问 `http://localhost:8080`,用 ChatGPT 账号登录。
109
 
110
+ > 你的账号数据会保存在 `data/` 文件夹里,重启不会丢失。
111
  >
112
  > **其他 Docker 容器要连本服务?** 用你电脑的局域网 IP(如 `http://192.168.x.x:8080/v1`),不要用 `localhost`。
113
 
114
+ **可选:自动更新**
115
+
116
+ 取消 `docker-compose.yml` 中 Watchtower 服务的注释,即可实现自动更新:
117
+
118
+ ```bash
119
+ docker compose up -d # 重新启动,Watchtower 将每小时检查更新
120
+ ```
121
+
122
+ Watchtower 会自动检测新镜像版本,拉取并重启容器,无需手动操作。
123
+
124
  ---
125
 
126
  ### 方式三:源码运行(开发者 / 想改代码的用户)
docker-compose.yml CHANGED
@@ -1,6 +1,8 @@
1
  services:
2
  codex-proxy:
3
- build: .
 
 
4
  extra_hosts:
5
  - "host.docker.internal:host-gateway"
6
  ports:
@@ -15,3 +17,11 @@ services:
15
  environment:
16
  - NODE_ENV=production
17
  - PORT=8080
 
 
 
 
 
 
 
 
 
1
  services:
2
  codex-proxy:
3
+ image: ghcr.io/icebear0828/codex-proxy:latest
4
+ # To build from source instead: comment out 'image' above, uncomment 'build' below
5
+ # build: .
6
  extra_hosts:
7
  - "host.docker.internal:host-gateway"
8
  ports:
 
17
  environment:
18
  - NODE_ENV=production
19
  - PORT=8080
20
+
21
+ # -- Automatic updates (uncomment to enable) --
22
+ # watchtower:
23
+ # image: containrrr/watchtower
24
+ # volumes:
25
+ # - /var/run/docker.sock:/var/run/docker.sock
26
+ # command: --cleanup --interval 3600 codex-proxy
27
+ # restart: unless-stopped
shared/i18n/translations.ts CHANGED
@@ -151,6 +151,7 @@ export const translations = {
151
  dockerUpdateCmd: "Run to update:",
152
  downloadUpdate: "Download",
153
  electronUpdateHint: "Updates are handled automatically. Restart the app or check the system tray for update notifications.",
 
154
  updateTitle: "Update Available",
155
  updateRestarting: "Restarting server...",
156
  updatePulling: "Pulling code...",
@@ -329,6 +330,7 @@ export const translations = {
329
  dockerUpdateCmd: "\u6267\u884c\u66f4\u65b0\u547d\u4ee4\uff1a",
330
  downloadUpdate: "\u4e0b\u8f7d",
331
  electronUpdateHint: "\u66f4\u65b0\u7531\u684c\u9762\u7aef\u81ea\u52a8\u5904\u7406\u3002\u91cd\u542f\u5e94\u7528\u6216\u68c0\u67e5\u7cfb\u7edf\u6258\u76d8\u7684\u66f4\u65b0\u901a\u77e5\u3002",
 
332
  updateTitle: "\u6709\u53ef\u7528\u66f4\u65b0",
333
  updateRestarting: "\u6b63\u5728\u91cd\u542f\u670d\u52a1...",
334
  updatePulling: "\u62c9\u53d6\u4ee3\u7801...",
 
151
  dockerUpdateCmd: "Run to update:",
152
  downloadUpdate: "Download",
153
  electronUpdateHint: "Updates are handled automatically. Restart the app or check the system tray for update notifications.",
154
+ dockerAutoUpdateHint: "Or enable Watchtower in docker-compose.yml for automatic updates",
155
  updateTitle: "Update Available",
156
  updateRestarting: "Restarting server...",
157
  updatePulling: "Pulling code...",
 
330
  dockerUpdateCmd: "\u6267\u884c\u66f4\u65b0\u547d\u4ee4\uff1a",
331
  downloadUpdate: "\u4e0b\u8f7d",
332
  electronUpdateHint: "\u66f4\u65b0\u7531\u684c\u9762\u7aef\u81ea\u52a8\u5904\u7406\u3002\u91cd\u542f\u5e94\u7528\u6216\u68c0\u67e5\u7cfb\u7edf\u6258\u76d8\u7684\u66f4\u65b0\u901a\u77e5\u3002",
333
+ dockerAutoUpdateHint: "\u6216\u5728 docker-compose.yml \u4e2d\u542f\u7528 Watchtower \u5b9e\u73b0\u81ea\u52a8\u66f4\u65b0",
334
  updateTitle: "\u6709\u53ef\u7528\u66f4\u65b0",
335
  updateRestarting: "\u6b63\u5728\u91cd\u542f\u670d\u52a1...",
336
  updatePulling: "\u62c9\u53d6\u4ee3\u7801...",
src/routes/web.ts CHANGED
@@ -290,7 +290,7 @@ export function createWebRoutes(accountPool: AccountPool): Hono {
290
  error: "Self-update not available in this deploy mode",
291
  mode,
292
  hint: mode === "docker"
293
- ? "Run: docker compose pull && docker compose up -d"
294
  : mode === "electron"
295
  ? "Updates are handled automatically by the desktop app. Check the system tray for update notifications, or restart the app to trigger a check."
296
  : "Git is not available in this environment",
 
290
  error: "Self-update not available in this deploy mode",
291
  mode,
292
  hint: mode === "docker"
293
+ ? "Run: docker compose pull && docker compose up -d (or enable Watchtower for automatic updates)"
294
  : mode === "electron"
295
  ? "Updates are handled automatically by the desktop app. Check the system tray for update notifications, or restart the app to trigger a check."
296
  : "Git is not available in this environment",
web/src/components/UpdateModal.tsx CHANGED
@@ -179,12 +179,17 @@ export function UpdateModal({
179
  {applying ? t("applyingUpdate") : t("updateNow")}
180
  </button>
181
  ) : mode === "docker" ? (
182
- <button
183
- onClick={() => { navigator.clipboard.writeText("docker compose pull && docker compose up -d"); }}
184
- class="px-4 py-2 text-xs font-semibold bg-primary text-white rounded-lg hover:bg-primary-hover transition-colors"
185
- >
186
- {t("copy")} docker compose pull && docker compose up -d
187
- </button>
 
 
 
 
 
188
  ) : (
189
  <span class="text-xs text-slate-500 dark:text-text-dim italic">
190
  {t("electronUpdateHint")}
 
179
  {applying ? t("applyingUpdate") : t("updateNow")}
180
  </button>
181
  ) : mode === "docker" ? (
182
+ <div class="flex flex-col items-end gap-1.5">
183
+ <button
184
+ onClick={() => { navigator.clipboard.writeText("docker compose pull && docker compose up -d"); }}
185
+ class="px-4 py-2 text-xs font-semibold bg-primary text-white rounded-lg hover:bg-primary-hover transition-colors"
186
+ >
187
+ {t("copy")} docker compose pull && docker compose up -d
188
+ </button>
189
+ <span class="text-[10px] text-slate-400 dark:text-text-dim">
190
+ {t("dockerAutoUpdateHint")}
191
+ </span>
192
+ </div>
193
  ) : (
194
  <span class="text-xs text-slate-500 dark:text-text-dim italic">
195
  {t("electronUpdateHint")}