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;