diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..74c0e6b
--- /dev/null
+++ b/.dockerignore
@@ -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
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 5f0889c..7fa0b92 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -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"
diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml
new file mode 100644
index 0000000..1124933
--- /dev/null
+++ b/.github/workflows/docker-release.yml
@@ -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
diff --git a/.gitignore b/.gitignore
index 3af72b2..92f81e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,5 @@ yarn-error.log*
pnpm-debug.log*
bun.lockb
.drizzle
+.serena
+CLAUDE.md
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..247a1a8
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,38 @@
+FROM node:24-alpine AS base
+
+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"]
diff --git a/Dockerfile.migrate b/Dockerfile.migrate
new file mode 100644
index 0000000..6728662
--- /dev/null
+++ b/Dockerfile.migrate
@@ -0,0 +1,22 @@
+FROM node:24-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}
+
+CMD ["node", "scripts/migrate.mjs"]
diff --git a/README.md b/README.md
index a1e139a..1690007 100644
--- a/README.md
+++ b/README.md
@@ -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
+```
+
## 预览
| | |
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..30ba06a
--- /dev/null
+++ b/docker-compose.yml
@@ -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: # 直连连接(更适合迁移、长事务、某些管理操作)
+ 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
diff --git a/next-env.d.ts b/next-env.d.ts
index c4b7818..9edff1c 100644
--- a/next-env.d.ts
+++ b/next-env.d.ts
@@ -1,6 +1,6 @@
///
///
-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.
diff --git a/next.config.ts b/next.config.ts
index 8209fc6..f6d2920 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -3,6 +3,10 @@ const nextConfig = {
experimental: {
viewTransition: true,
},
+ /* 输出 standalone 运行时,配合 Docker 多阶段构建减小镜像体积 */
+ output: "standalone",
+ /* 生产环境禁用 source maps 以减小镜像大小 */
+ productionBrowserSourceMaps: false,
};
export default nextConfig;