Skip to content

Commit

Permalink
👷 build: add docker image for server database (lobehub#3387)
Browse files Browse the repository at this point in the history
* 👷 build: add docker database image

* 💚 ci: add database image workflow

* fix docker build

* Update PULL_REQUEST_TEMPLATE.md

* 🎨 chore: improve migration script

* 🐛 fix: throws error in nextauth db adapter (lobehub#3390)

* 👷 chore: update action

---------

Co-authored-by: cy948 <[email protected]>
  • Loading branch information
arvinxx and cy948 authored Aug 3, 2024
1 parent 56ffa22 commit 72306f5
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 9 deletions.
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ node_modules
npm-debug.log
.next
.git
scripts
docs
.github
*.md
Expand Down
3 changes: 2 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
- [ ] 🐛 fix
- [ ] ♻️ refactor
- [ ] 💄 style
- [ ] 🔨 chore
- [ ] 👷 build
- [ ] ⚡️ perf
- [ ] 📝 docs
- [ ] 🔨 chore

#### 🔀 变更说明 | Description of Change

Expand Down
46 changes: 46 additions & 0 deletions .github/workflows/docker-database.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Publish Database Docker Image

on:
workflow_dispatch:
release:
types: [published]

jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_REGISTRY_USER }}
password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: lobehub/lobe-chat-database
tags: |
type=raw,value=latest
type=ref,event=tag
- name: Set up QEMU
uses: docker/setup-qemu-action@v3

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

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.database # 指定使用 Dockerfile.database 文件
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
170 changes: 170 additions & 0 deletions Dockerfile.database
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
## Base image for all the stages
FROM node:20-alpine AS base

RUN \
# Add user nextjs to run the app
addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 nextjs

## Builder image, install all the dependencies and build the app
FROM base AS builder

ARG USE_NPM_CN_MIRROR

ENV KEY_VAULTS_SECRET="use-for-build" \
NEXT_PUBLIC_SERVICE_MODE="server" \
DATABASE_DRIVER="node" \
DATABASE_URL="postgres://postgres:password@localhost:5432/postgres"

# Sentry
ENV NEXT_PUBLIC_SENTRY_DSN="" \
SENTRY_ORG="" \
SENTRY_PROJECT=""

# Posthog
ENV NEXT_PUBLIC_ANALYTICS_POSTHOG="" \
NEXT_PUBLIC_POSTHOG_HOST="" \
NEXT_PUBLIC_POSTHOG_KEY=""

# Umami
ENV NEXT_PUBLIC_ANALYTICS_UMAMI="" \
NEXT_PUBLIC_UMAMI_SCRIPT_URL="" \
NEXT_PUBLIC_UMAMI_WEBSITE_ID=""

# Node
ENV NODE_OPTIONS="--max-old-space-size=8192"

WORKDIR /app

COPY package.json ./
COPY .npmrc ./

RUN \
# If you want to build docker in China, build with --build-arg USE_NPM_CN_MIRROR=true
if [ "${USE_NPM_CN_MIRROR:-false}" = "true" ]; then \
export SENTRYCLI_CDNURL="https://npmmirror.com/mirrors/sentry-cli"; \
npm config set registry "https://registry.npmmirror.com/"; \
fi \
# Set the registry for corepack
&& export COREPACK_NPM_REGISTRY=$(npm config get registry | sed 's/\/$//') \
# Enable corepack
&& corepack enable \
# Use pnpm for corepack
&& corepack use pnpm \
# Install the dependencies
&& pnpm i \
# Add sharp and db migration dependencies
&& mkdir -p /deps \
&& pnpm add sharp pg drizzle-orm --prefix /deps

COPY . .

# run build standalone for docker version
RUN npm run build:docker

## Application image, copy all the files for production
FROM scratch AS app

COPY --from=builder /app/public /app/public

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder /app/.next/standalone /app/
COPY --from=builder /app/.next/static /app/.next/static

# copy dependencies
COPY --from=builder /deps/node_modules/.pnpm /app/node_modules/.pnpm
COPY --from=builder /deps/node_modules/pg /app/node_modules/pg
COPY --from=builder /deps/node_modules/drizzle-orm /app/node_modules/drizzle-orm

# Copy database migrations
COPY --from=builder /app/src/database/server/migrations /app/migrations
COPY --from=builder /app/scripts/migrateServerDB/docker.cjs /app/docker.cjs

## Production image, copy all the files and run next
FROM base

# Copy all the files from app, set the correct permission for prerender cache
COPY --from=app --chown=nextjs:nodejs /app /app

ENV NODE_ENV="production"

# set hostname to localhost
ENV HOSTNAME="0.0.0.0" \
PORT="3210"

# General Variables
ENV API_KEY_SELECT_MODE="" \
FEATURE_FLAGS=""

# Database
ENV KEY_VAULTS_SECRET="" \
DATABASE_DRIVER="node" \
DATABASE_URL=""

# Next Auth
ENV NEXT_AUTH_SECRET="" \
ACCESS_CODE="" \
NEXTAUTH_URL="" \
NEXT_AUTH_SSO_PROVIDERS=""

# S3
ENV S3_ACCESS_KEY_ID="" \
S3_SECRET_ACCESS_KEY="" \
NEXT_PUBLIC_S3_DOMAIN="" \
S3_ENDPOINT="" \
S3_BUCKET=""

# Model Variables
ENV \
# Ai360
AI360_API_KEY="" \
# Anthropic
ANTHROPIC_API_KEY="" ANTHROPIC_PROXY_URL="" \
# Amazon Bedrock
AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" AWS_REGION="" \
# Azure OpenAI
AZURE_API_KEY="" AZURE_API_VERSION="" AZURE_ENDPOINT="" AZURE_MODEL_LIST="" \
# Baichuan
BAICHUAN_API_KEY="" \
# DeepSeek
DEEPSEEK_API_KEY="" \
# Google
GOOGLE_API_KEY="" GOOGLE_PROXY_URL="" \
# Groq
GROQ_API_KEY="" GROQ_PROXY_URL="" \
# Minimax
MINIMAX_API_KEY="" \
# Mistral
MISTRAL_API_KEY="" \
# Moonshot
MOONSHOT_API_KEY="" MOONSHOT_PROXY_URL="" \
# Novita
NOVITA_API_KEY="" \
# Ollama
OLLAMA_MODEL_LIST="" OLLAMA_PROXY_URL="" \
# OpenAI
OPENAI_API_KEY="" OPENAI_MODEL_LIST="" OPENAI_PROXY_URL="" \
# OpenRouter
OPENROUTER_API_KEY="" OPENROUTER_MODEL_LIST="" \
# Perplexity
PERPLEXITY_API_KEY="" PERPLEXITY_PROXY_URL="" \
# Qwen
QWEN_API_KEY="" \
# Stepfun
STEPFUN_API_KEY="" \
# Taichu
TAICHU_API_KEY="" \
# TogetherAI
TOGETHERAI_API_KEY="" TOGETHERAI_MODEL_LIST="" \
# 01.AI
ZEROONE_API_KEY="" \
# Zhipu
ZHIPU_API_KEY=""

USER nextjs

EXPOSE 3210/tcp

# run migration , then run app
CMD ["sh", "-c", "node /app/docker.cjs && node /app/server.js"]
34 changes: 34 additions & 0 deletions scripts/migrateServerDB/docker.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { join } = require('node:path');
const { Pool } = require('pg');
const { drizzle } = require('drizzle-orm/node-postgres');
const migrator = require('drizzle-orm/node-postgres/migrator');

if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL is not set, please set it in your environment variables.');
}

const client = new Pool({ connectionString: process.env.DATABASE_URL });

const db = drizzle(client);

const runMigrations = async () => {
console.log('[Database] Start to migration...');
await migrator.migrate(db, {
migrationsFolder: join(__dirname, './migrations'),
});

console.log('✅ database migration pass.');
console.log('-------------------------------------');
// eslint-disable-next-line unicorn/no-process-exit
process.exit(0);
};

// eslint-disable-next-line unicorn/prefer-top-level-await
runMigrations().catch((err) => {
console.error(
'❌ Database migrate failed. Please check your database is valid and DATABASE_URL is set correctly. The error detail is below:',
);
console.error(err);
// eslint-disable-next-line unicorn/no-process-exit
process.exit(1);
});
10 changes: 3 additions & 7 deletions src/libs/next-auth/adapter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,9 @@ export function LobeNextAuthDbAdapter(serverDB: NeonDatabase<typeof schema>): Ad
},

async getUser(id): Promise<AdapterUser | null> {
try {
const lobeUser = await UserModel.findById(id);
if (!lobeUser) return null;
return mapLobeUserToAdapterUser(lobeUser);
} catch {
return null;
}
const lobeUser = await UserModel.findById(id);
if (!lobeUser) return null;
return mapLobeUserToAdapterUser(lobeUser);
},

async getUserByAccount(account): Promise<AdapterUser | null> {
Expand Down

0 comments on commit 72306f5

Please sign in to comment.