Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Git
.git
.gitignore
.gitattributes

# Build outputs
dist
build
.next
node_modules
out

# Development
.env.local
.env.*.local
.vscode
.idea
*.swp
*.swo
*~

# Testing
coverage
.nyc_output

# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*

# OS
.DS_Store
Thumbs.db

# CI/CD
.github
.gitlab-ci.yml

# Documentation
README.md
CHANGELOG.md
CLAUDE.md
.serena
docs
4 changes: 4 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ updates:
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
96 changes: 96 additions & 0 deletions .github/workflows/docker-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Build and Push Docker Images

on:
push:
tags:
- 'v*'
workflow_dispatch:

jobs:
# 检查是否修改了drizzle的数据库配置, 修改了才需要更新migrate镜像
detect-migrate-changes:
runs-on: ubuntu-latest
outputs:
should_build_migrate: ${{ steps.filter.outputs.migrate }}

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Detect migration-related changes
id: filter
uses: dorny/paths-filter@v3
with:
filters: |
migrate:
- 'drizzle/**'
- 'scripts/migrate.mjs'
- 'Dockerfile.migrate'

build-app:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Get lowercase repository owner
id: lowercase
run: echo "owner=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT

- name: Build and Push App Image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/amd64
push: true
tags: |
ghcr.io/${{ steps.lowercase.outputs.owner }}/cliproxyapi-monitor:latest
cache-from: type=gha
cache-to: type=gha,mode=min

build-migrate:
runs-on: ubuntu-latest
needs: detect-migrate-changes
if: github.event_name == 'workflow_dispatch' || needs.detect-migrate-changes.outputs.should_build_migrate == 'true'

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Get lowercase repository owner
id: lowercase
run: echo "owner=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT

- name: Build and Push Migration Image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile.migrate
platforms: linux/amd64
push: true
tags: |
ghcr.io/${{ steps.lowercase.outputs.owner }}/cliproxyapi-monitor-migrate:latest
cache-from: type=gha
cache-to: type=gha,mode=min
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ yarn-error.log*
pnpm-debug.log*
bun.lockb
.drizzle
.serena
CLAUDE.md
38 changes: 38 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
FROM node:24-alpine AS base

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

您好,node:24-alpine 这个基础镜像似乎不存在。Node.js 24 尚未发布,目前最新的稳定版本是 22,LTS 版本是 20。使用一个不存在的镜像会导致 Docker 构建失败。建议使用一个存在的、稳定的版本,例如最新的 Node.js 22 (node:22-alpine) 或者当前的 LTS 版本 (node:20-alpine),以确保构建的稳定性和可预测性。

FROM node:22-alpine AS base

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


WORKDIR /app

ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
ENV NEXT_TELEMETRY_DISABLED=1

RUN corepack enable && corepack prepare pnpm@9 --activate

FROM base AS deps

COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

FROM base AS builder

COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm run build-only

FROM node:24-alpine AS runner

WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

RUN addgroup -S nodejs && adduser -S nextjs -G nodejs

COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

CMD ["node", "server.js"]
22 changes: 22 additions & 0 deletions Dockerfile.migrate
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM node:24-alpine

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

您好,与主 Dockerfile 一样,这里使用的 node:24-alpine 基础镜像似乎不存在,这将导致构建失败。建议更换为存在的镜像版本,例如 node:22-alpine 或 LTS 版本 node:20-alpine,以保证构建能够成功。

FROM node:22-alpine


WORKDIR /app

ENV NODE_ENV=production

ARG DRIZZLE_ORM_VERSION=0.45.1
ARG PG_VERSION=8.19.0
ARG NEON_VERSION=1.0.2
ARG WS_VERSION=8.19.0

COPY scripts ./scripts
COPY drizzle ./drizzle

# Keep migration image dependencies minimal and separate from app image.
RUN npm install --omit=dev --no-package-lock \
drizzle-orm@${DRIZZLE_ORM_VERSION} \
pg@${PG_VERSION} \
@neondatabase/serverless@${NEON_VERSION} \
ws@${WS_VERSION}
Comment on lines +7 to +20

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

您好,在 Dockerfile.migrate 中硬编码依赖版本(L7-10 和 L16-20)虽然可以精确控制镜像中的包,但长期来看可能会引入维护问题。当项目根目录的 package.json 中这些依赖更新时,此文件需要手动同步更新,否则可能导致迁移脚本在本地和 CI/CD 环境中行为不一致。此外,这里使用了 npm,而主应用使用的是 pnpm,统一包管理器会更好。

为了在保持镜像体积小的同时提高可维护性,可以考虑创建一个专门用于迁移的 package.json 文件,并使用 pnpm 来管理其依赖。如果不想引入新的 package.json,另一种折衷方案是在 CI 流程中从 pnpm-lock.yaml 文件动态提取版本号,并作为构建参数(--build-arg)传入 Dockerfile.migrate

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个确实


CMD ["node", "scripts/migrate.mjs"]
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,36 @@
- 默认启用 Vercel Cron( Pro 可设每小时,Hobby 每天同步一次,请见 [vercel.json](https://github.com/sxjeru/CLIProxyAPI-Monitor/blob/main/vercel.json) )
- Cloudflare Worker / 其他定时器定期请求同步:可见 [cf-worker-sync.js](https://github.com/sxjeru/CLIProxyAPI-Monitor/blob/main/cf-worker-sync.js)

## Docker部署

| compose file service | migrate-DB | app |
| -------------------- | ------------------------------------------------------------------ | ----------------- |
| = | 用来初始化数据库, 建表啊之类的(先启动, 看到DB里面有表了就可以删了) | 服务端app(后启动) |

- 参考[docker-compose.yml](./docker-compose.yml)文件来进行docker的部署
- 为什么用两个镜像. 因为同时打包的话太大了, 反正数据库也不是会频繁变结构的

| IMAGE | DISK USAGE | CONTENT SIZE |
| ---------------------------------- | ---------- | ------------ |
| cliproxyapi-monitor:latest | 331MB | 80.3MB |
| cliproxyapi-monitor-migrate:latest | 580MB | 96.6MB |

- 运行完migrate-DB, 会有类似log
```log
检查迁移表... (驱动: pg)
执行数据库迁移...
✓ 迁移完成
```

- app的log
```log
▲ Next.js 16.1.6
- Local: http://fa45b0e6e0ad:3000
- Network: http://fa45b0e6e0ad:3000
✓ Starting...
✓ Ready in 426ms
```

## 预览

| | |
Expand Down
39 changes: 39 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
version: '3.8'

services:
migrate-DB:
image: ghcr.io/sxjeru/cliproxyapi-monitor-migrate:latest
container_name: cliproxyapi-monitor-migrate
environment:
DATABASE_URL: ${DATABASE_URL}
DATABASE_DRIVER: ${DATABASE_DRIVER:-pg}
# 针对neon/Supabase
POSTGRES_URL: # 池化连接(适合短连接、高并发函数)
POSTGRES_URL_NON_POOLING: # 直连连接(更适合迁移、长事务、某些管理操作)
Comment on lines +10 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): 直接给 POSTGRES_URL* 赋值会把这些变量设置为空字符串,而不是从宿主环境中继承。

以这种写法,Compose 会将 POSTGRES_URLPOSTGRES_URL_NON_POOLING 显式设为两个空字符串,从而覆盖宿主环境中已有的值。如果这些变量是可选的,要么显式地透传它们(例如 POSTGRES_URL: ${POSTGRES_URL:-}),要么完全从 environment 中删掉,并依赖文档说明,只在实际提供时才设置。

Suggested change
# 针对neon/Supabase
POSTGRES_URL: # 池化连接(适合短连接、高并发函数)
POSTGRES_URL_NON_POOLING: # 直连连接(更适合迁移、长事务、某些管理操作)
# 针对neon/Supabase
POSTGRES_URL: ${POSTGRES_URL:-} # 池化连接(适合短连接、高并发函数)
POSTGRES_URL_NON_POOLING: ${POSTGRES_URL_NON_POOLING:-} # 直连连接(更适合迁移、长事务、某些管理操作)
Original comment in English

suggestion (bug_risk): Bare POSTGRES_URL* assignments will set these vars to empty strings rather than inheriting from the host environment.

In this form, Compose sets POSTGRES_URL and POSTGRES_URL_NON_POOLING to empty strings, overriding any existing host values. If they’re meant to be optional, either pass them through explicitly (e.g. POSTGRES_URL: ${POSTGRES_URL:-}) or omit them from environment and rely on documentation so they’re only set when provided.

Suggested change
# 针对neon/Supabase
POSTGRES_URL: # 池化连接(适合短连接、高并发函数)
POSTGRES_URL_NON_POOLING: # 直连连接(更适合迁移、长事务、某些管理操作)
# 针对neon/Supabase
POSTGRES_URL: ${POSTGRES_URL:-} # 池化连接(适合短连接、高并发函数)
POSTGRES_URL_NON_POOLING: ${POSTGRES_URL_NON_POOLING:-} # 直连连接(更适合迁移、长事务、某些管理操作)

restart: "no"
profiles:
- migration

app:
image: ghcr.io/sxjeru/cliproxyapi-monitor:latest
container_name: cliproxyapi-monitor
ports:
- "3000:3000"
environment:
NODE_ENV: production
CLIPROXY_SECRET_KEY: ${CLIPROXY_SECRET_KEY}
CLIPROXY_API_BASE_URL: ${CLIPROXY_API_BASE_URL}
DATABASE_URL: ${DATABASE_URL}
DATABASE_DRIVER: ${DATABASE_DRIVER:-pg}
PASSWORD: ${PASSWORD}
# 以下是非必要的参数, 详见readme
DATABASE_CA: ${DATABASE_CA}
CRON_SECRET: ${CRON_SECRET}
TIMEZONE: ${TIMEZONE:-Asia/Shanghai}
DATABASE_POOL_MAX: ${DATABASE_POOL_MAX:-5}
DATABASE_POOL_IDLE_TIMEOUT_MS: ${DATABASE_POOL_IDLE_TIMEOUT_MS:-10000}
DATABASE_POOL_CONNECTION_TIMEOUT_MS: ${DATABASE_POOL_CONNECTION_TIMEOUT_MS:-5000}
DATABASE_POOL_MAX_USES: ${DATABASE_POOL_MAX_USES:-7500}
AUTH_FILES_INSERT_CHUNK_SIZE: ${AUTH_FILES_INSERT_CHUNK_SIZE:-500}
USAGE_INSERT_CHUNK_SIZE: ${USAGE_INSERT_CHUNK_SIZE:-1000}
restart: unless-stopped
2 changes: 1 addition & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/dev/types/routes.d.ts";
import "./.next/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
4 changes: 4 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ const nextConfig = {
experimental: {
viewTransition: true,
},
/* 输出 standalone 运行时,配合 Docker 多阶段构建减小镜像体积 */
output: "standalone",
/* 生产环境禁用 source maps 以减小镜像大小 */
productionBrowserSourceMaps: false,
};

export default nextConfig;