diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml new file mode 100644 index 0000000..0c176e7 --- /dev/null +++ b/.github/workflows/docker-build-push.yml @@ -0,0 +1,64 @@ +name: Build and Push Docker Image to GHCR + +on: + push: + branches: + - main + tags: + - 'v*.*.*' + pull_request: + branches: + - main + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }}/wrdo + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + # 检出代码 + - name: Checkout repository + uses: actions/checkout@v4 + + # 设置 Docker Buildx(支持多平台构建) + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # 登录到 GitHub Container Registry + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # 提取 Docker 镜像元数据(标签、版本等) + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=sha,format=short + type=ref,event=branch,prefix= + type=ref,event=tag + + # 构建并推送 Docker 镜像 + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: ${{ github.event_name != 'pull_request' }} # 仅在 push 时推送 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + ENVIRONMENT=${{ github.event.inputs.environment || 'production' }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..934597b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,66 @@ +FROM node:22-alpine AS base + +# Install dependencies only when needed +FROM base AS deps +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +WORKDIR /app + +RUN npm install -g pnpm + +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ + +RUN pnpm config set registry https://registry.npmmirror.com + +COPY prisma ./prisma +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ +RUN pnpm i --frozen-lockfile + +FROM base AS builder +WORKDIR /app + +RUN npm install -g pnpm + +# ARG DATABASE_URL="" +# ARG AUTH_SECRET="" +# ARG NEXT_PUBLIC_APP_URL=http://localhost:3000 +# ARG GOOGLE_CLIENT_ID="" +# ARG GOOGLE_CLIENT_SECRET="" +# ARG GITHUB_ID="" +# ARG GITHUB_SECRET="" +# ARG LinuxDo_CLIENT_ID="" +# ARG LinuxDo_CLIENT_SECRET="" +# ARG RESEND_API_KEY="res" +# ARG NEXT_PUBLIC_EMAIL_R2_DOMAIN="" +# ARG NEXT_PUBLIC_OPEN_SIGNUP="1" +# ARG NEXT_PUBLIC_GOOGLE_ID="" +# ARG SCREENSHOTONE_BASE_URL="" +# ARG GITHUB_TOKEN="g" + +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +RUN pnpm run build + +FROM base AS runner +WORKDIR /app + +RUN npm install -g pnpm + +ENV NODE_ENV=production +ENV IS_DOCKER=true + +# 复制必要文件 +COPY --from=builder /app/public ./public + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +EXPOSE 3000 + +ENV PORT=3000 + +ENV HOSTNAME="0.0.0.0" +CMD ["node", "server.js"] \ No newline at end of file diff --git a/README.md b/README.md index 8dc93d1..f54f9c3 100644 --- a/README.md +++ b/README.md @@ -46,13 +46,15 @@ See docs about [email worker](https://wr.do/docs/developer/cloudflare-email-work ## Local development -copy `.env.example` to `.env` and fill in the necessary environment variables. - ```bash git clone https://github.com/oiov/wr.do cd wr.do pnpm install +``` +copy `.env.example` to `.env` and fill in the necessary environment variables. + +```bash # run on localhost:3000 pnpm dev ``` @@ -68,6 +70,37 @@ pnpm db:push Follow https://localhost:3000/setup +### Start on Docker + +#### Dockerfile + +```bash +git clone https://github.com/oiov/wr.do +cd wr.do +``` + +Fill in the environment variables in the `Dockerfile`, then build and run. + +```bash +# Build image +docker build -t wrdo . +# Run +docker run -p 3000:3000 wrdo +``` + +#### Docker Compose + +```bash +git clone https://github.com/oiov/wr.do +cd wr.do +``` + +Fill in the environment variables in the `env` file, then: + +```bash +docker compose up -d +``` + ## Legitimacy review - To avoid abuse, applications without website content will be rejected diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e111d5f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,55 @@ +services: + wrdo: + build: + context: . + dockerfile: Dockerfile + image: ghcr.io/oiov/wr.do/wrdo:${TAG:-latest} + container_name: wrdo + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - IS_DOCKER=true + - DATABASE_URL=postgres://postgres:postgres@postgres:5432/wrdo + - AUTH_SECRET=${AUTH_SECRET:-your-auth-secret} + - NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000} + - GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID:-} + - GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET:-} + - GITHUB_ID=${GITHUB_ID:-} + - GITHUB_SECRET=${GITHUB_SECRET:-} + - LinuxDo_CLIENT_ID=${LinuxDo_CLIENT_ID:-} + - LinuxDo_CLIENT_SECRET=${LinuxDo_CLIENT_SECRET:-} + - RESEND_API_KEY=${RESEND_API_KEY:-} + - NEXT_PUBLIC_EMAIL_R2_DOMAIN=${NEXT_PUBLIC_EMAIL_R2_DOMAIN:-} + - NEXT_PUBLIC_OPEN_SIGNUP=${NEXT_PUBLIC_OPEN_SIGNUP:-1} + - NEXT_PUBLIC_GOOGLE_ID=${NEXT_PUBLIC_GOOGLE_ID:-} + - SCREENSHOTONE_BASE_URL=${SCREENSHOTONE_BASE_URL:-} + - GITHUB_TOKEN=${GITHUB_TOKEN:-} + depends_on: + - postgres + networks: + - wrdo-network + restart: unless-stopped + + postgres: + image: postgres:16-alpine + container_name: postgres + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + - POSTGRES_DB=wrdo + volumes: + - postgres-data:/var/lib/postgresql/data + ports: + - "5432:5432" + networks: + - wrdo-network + restart: unless-stopped + +volumes: + postgres-data: + name: wrdo-postgres-data + +networks: + wrdo-network: + driver: bridge diff --git a/env.mjs b/env.mjs index 6b5023a..e11cd78 100644 --- a/env.mjs +++ b/env.mjs @@ -6,22 +6,22 @@ export const env = createEnv({ // This is optional because it's only used in development. // See https://next-auth.js.org/deployment. NEXTAUTH_URL: z.string().url().optional(), - AUTH_SECRET: z.string().min(1), + AUTH_SECRET: z.string().optional(), GOOGLE_CLIENT_ID: z.string().optional(), GOOGLE_CLIENT_SECRET: z.string().optional(), GITHUB_ID: z.string().optional(), GITHUB_SECRET: z.string().optional(), LinuxDo_CLIENT_ID: z.string().optional(), LinuxDo_CLIENT_SECRET: z.string().optional(), - DATABASE_URL: z.string().min(1), + DATABASE_URL: z.string().optional(), RESEND_API_KEY: z.string().optional(), SCREENSHOTONE_BASE_URL: z.string().optional(), GITHUB_TOKEN: z.string().optional(), }, client: { - NEXT_PUBLIC_APP_URL: z.string().min(1), + NEXT_PUBLIC_APP_URL: z.string().optional(), NEXT_PUBLIC_OPEN_SIGNUP: z.string().min(1).default("1"), - NEXT_PUBLIC_EMAIL_R2_DOMAIN: z.string().min(1), + NEXT_PUBLIC_EMAIL_R2_DOMAIN: z.string().optional(), }, runtimeEnv: { NEXTAUTH_URL: process.env.NEXTAUTH_URL, diff --git a/next.config.js b/next.config.js index 48d7d85..160f4e3 100644 --- a/next.config.js +++ b/next.config.js @@ -6,6 +6,7 @@ import("./env.mjs"); const nextConfig = { reactStrictMode: true, swcMinify: true, + output: "standalone", images: { remotePatterns: [ { diff --git a/package.json b/package.json index e2bac50..0f8b629 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,9 @@ "email": "email dev --dir emails --port 3333", "remove-content": "node ./setup.mjs" }, + "prisma": { + "schema": "./prisma/schema.prisma" + }, "dependencies": { "@auth/prisma-adapter": "^2.4.1", "@hookform/resolvers": "^3.9.0", diff --git a/public/_static/images/realtime.png b/public/_static/images/realtime.png new file mode 100644 index 0000000..e69de29