mirror of
https://github.com/f/awesome-chatgpt-prompts.git
synced 2026-03-03 03:07:00 +00:00
feat(docker): Add Docker deployment guide and configuration instructions
This commit is contained in:
51
.dockerignore
Normal file
51
.dockerignore
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
docker-compose*.yml
|
||||||
|
Dockerfile*
|
||||||
|
|
||||||
|
# Development files
|
||||||
|
.github
|
||||||
|
.claude
|
||||||
|
packages
|
||||||
|
*.md
|
||||||
|
!README.md
|
||||||
|
|
||||||
|
# Test files
|
||||||
|
coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Prisma
|
||||||
|
prisma/migrations/**/migration_lock.toml
|
||||||
62
.github/workflows/docker-publish.yml
vendored
Normal file
62
.github/workflows/docker-publish.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
name: Build and Publish Docker Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
IMAGE_NAME: ${{ github.repository_owner }}/prompts.chat
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to Container Registry
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata (tags, labels)
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=pr
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./docker/Dockerfile
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
298
DOCKER.md
Normal file
298
DOCKER.md
Normal file
@@ -0,0 +1,298 @@
|
|||||||
|
# Docker Deployment Guide
|
||||||
|
|
||||||
|
Run prompts.chat with a single command using Docker.
|
||||||
|
|
||||||
|
## Quick Start (All-in-One Image)
|
||||||
|
|
||||||
|
The easiest way to run prompts.chat - a single container with Node.js and PostgreSQL:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name prompts \
|
||||||
|
-p 80:80 \
|
||||||
|
-v prompts-data:/data \
|
||||||
|
ghcr.io/f/prompts.chat
|
||||||
|
```
|
||||||
|
|
||||||
|
Open http://localhost in your browser.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
| Variable | Description | Default |
|
||||||
|
|----------|-------------|---------|
|
||||||
|
| `AUTH_SECRET` | Secret for authentication tokens | Auto-generated |
|
||||||
|
| `PORT` | Port to run the app on | `80` |
|
||||||
|
| `DATABASE_URL` | PostgreSQL connection string | Internal DB |
|
||||||
|
|
||||||
|
### Production Setup
|
||||||
|
|
||||||
|
For production, always set `AUTH_SECRET`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name prompts \
|
||||||
|
-p 80:80 \
|
||||||
|
-v prompts-data:/data \
|
||||||
|
-e AUTH_SECRET="your-secret-key-min-32-chars-long" \
|
||||||
|
ghcr.io/f/prompts.chat
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate a secure secret:
|
||||||
|
```bash
|
||||||
|
openssl rand -base64 32
|
||||||
|
```
|
||||||
|
|
||||||
|
### With OAuth Providers
|
||||||
|
|
||||||
|
Enable GitHub/Google authentication:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name prompts \
|
||||||
|
-p 80:80 \
|
||||||
|
-v prompts-data:/data \
|
||||||
|
-e AUTH_SECRET="your-secret-key" \
|
||||||
|
-e AUTH_GITHUB_ID="your-github-client-id" \
|
||||||
|
-e AUTH_GITHUB_SECRET="your-github-client-secret" \
|
||||||
|
-e AUTH_GOOGLE_ID="your-google-client-id" \
|
||||||
|
-e AUTH_GOOGLE_SECRET="your-google-client-secret" \
|
||||||
|
ghcr.io/f/prompts.chat
|
||||||
|
```
|
||||||
|
|
||||||
|
### With AI Search (OpenAI)
|
||||||
|
|
||||||
|
Enable semantic search with OpenAI embeddings:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name prompts \
|
||||||
|
-p 80:80 \
|
||||||
|
-v prompts-data:/data \
|
||||||
|
-e AUTH_SECRET="your-secret-key" \
|
||||||
|
-e OPENAI_API_KEY="sk-..." \
|
||||||
|
ghcr.io/f/prompts.chat
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker Compose (Separate Containers)
|
||||||
|
|
||||||
|
For more control, use Docker Compose with separate app and database containers:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone the repository
|
||||||
|
git clone https://github.com/f/awesome-chatgpt-prompts.git
|
||||||
|
cd awesome-chatgpt-prompts
|
||||||
|
|
||||||
|
# Create .env file
|
||||||
|
echo "AUTH_SECRET=$(openssl rand -base64 32)" > .env
|
||||||
|
|
||||||
|
# Start services
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### docker-compose.yml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/Dockerfile.app
|
||||||
|
ports:
|
||||||
|
- "80:3000"
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=postgresql://prompts:prompts@db:5432/prompts
|
||||||
|
- AUTH_SECRET=${AUTH_SECRET}
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=prompts
|
||||||
|
- POSTGRES_PASSWORD=prompts
|
||||||
|
- POSTGRES_DB=prompts
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U prompts"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Persistence
|
||||||
|
|
||||||
|
### All-in-One Image
|
||||||
|
|
||||||
|
Data is stored in `/data` inside the container:
|
||||||
|
- `/data/postgres` - PostgreSQL database files
|
||||||
|
|
||||||
|
Mount a volume to persist data:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
-v prompts-data:/data \
|
||||||
|
ghcr.io/f/prompts.chat
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backup database
|
||||||
|
docker exec prompts pg_dump -U prompts prompts > backup.sql
|
||||||
|
|
||||||
|
# Restore database
|
||||||
|
docker exec -i prompts psql -U prompts prompts < backup.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building Locally
|
||||||
|
|
||||||
|
Build the all-in-one image:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -f docker/Dockerfile -t prompts.chat .
|
||||||
|
docker run -p 80:80 prompts.chat
|
||||||
|
```
|
||||||
|
|
||||||
|
Build the app-only image (for docker-compose):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -f docker/Dockerfile.app -t prompts.chat-app .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Health Check
|
||||||
|
|
||||||
|
The container includes a health check endpoint:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost/api/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "healthy",
|
||||||
|
"timestamp": "2024-01-01T00:00:00.000Z",
|
||||||
|
"database": "connected"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### View Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# All logs
|
||||||
|
docker logs prompts
|
||||||
|
|
||||||
|
# Follow logs
|
||||||
|
docker logs -f prompts
|
||||||
|
|
||||||
|
# PostgreSQL logs (inside container)
|
||||||
|
docker exec prompts cat /var/log/supervisor/postgresql.log
|
||||||
|
|
||||||
|
# Next.js logs (inside container)
|
||||||
|
docker exec prompts cat /var/log/supervisor/nextjs.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Access
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Connect to PostgreSQL
|
||||||
|
docker exec -it prompts psql -U prompts -d prompts
|
||||||
|
|
||||||
|
# Run SQL query
|
||||||
|
docker exec prompts psql -U prompts -d prompts -c "SELECT COUNT(*) FROM \"Prompt\""
|
||||||
|
```
|
||||||
|
|
||||||
|
### Container Shell
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it prompts bash
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
**Container won't start:**
|
||||||
|
- Check logs: `docker logs prompts`
|
||||||
|
- Ensure port 80 is available: `lsof -i :80`
|
||||||
|
|
||||||
|
**Database connection errors:**
|
||||||
|
- Wait for PostgreSQL to initialize (can take 30-60 seconds on first run)
|
||||||
|
- Check database logs: `docker exec prompts cat /var/log/supervisor/postgresql.log`
|
||||||
|
|
||||||
|
**Authentication issues:**
|
||||||
|
- Ensure `AUTH_SECRET` is set for production
|
||||||
|
- For OAuth, verify callback URLs are configured correctly
|
||||||
|
|
||||||
|
## Resource Requirements
|
||||||
|
|
||||||
|
Minimum:
|
||||||
|
- 1 CPU core
|
||||||
|
- 1GB RAM
|
||||||
|
- 2GB disk space
|
||||||
|
|
||||||
|
Recommended:
|
||||||
|
- 2 CPU cores
|
||||||
|
- 2GB RAM
|
||||||
|
- 10GB disk space
|
||||||
|
|
||||||
|
## Updating
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pull latest image
|
||||||
|
docker pull ghcr.io/f/prompts.chat
|
||||||
|
|
||||||
|
# Stop and remove old container
|
||||||
|
docker stop prompts && docker rm prompts
|
||||||
|
|
||||||
|
# Start new container (data persists in volume)
|
||||||
|
docker run -d \
|
||||||
|
--name prompts \
|
||||||
|
-p 80:80 \
|
||||||
|
-v prompts-data:/data \
|
||||||
|
-e AUTH_SECRET="your-secret-key" \
|
||||||
|
ghcr.io/f/prompts.chat
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
1. **Always set AUTH_SECRET** in production
|
||||||
|
2. **Use HTTPS** - put a reverse proxy (nginx, Caddy, Traefik) in front
|
||||||
|
3. **Limit exposed ports** - only expose what's needed
|
||||||
|
4. **Regular updates** - pull the latest image regularly
|
||||||
|
5. **Backup data** - regularly backup the `/data` volume
|
||||||
|
|
||||||
|
## Example: Running Behind Nginx
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name prompts.example.com;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/prompts.example.com/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/prompts.example.com/privkey.pem;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:80;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
46
docker-compose.yml
Normal file
46
docker-compose.yml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Docker Compose for prompts.chat
|
||||||
|
# Alternative to the all-in-one image - separates app and database
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# docker compose up -d
|
||||||
|
# docker compose down
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/Dockerfile.app
|
||||||
|
ports:
|
||||||
|
- "80:3000"
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- DATABASE_URL=postgresql://prompts:prompts@db:5432/prompts?schema=public
|
||||||
|
- AUTH_SECRET=${AUTH_SECRET:-change-me-in-production}
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=prompts
|
||||||
|
- POSTGRES_PASSWORD=prompts
|
||||||
|
- POSTGRES_DB=prompts
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U prompts -d prompts"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
91
docker/Dockerfile
Normal file
91
docker/Dockerfile
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# prompts.chat All-in-One Docker Image
|
||||||
|
# Contains Node.js + PostgreSQL for single-container deployment
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# docker run -p 80:80 ghcr.io/f/prompts.chat
|
||||||
|
# docker run -p 80:80 -e AUTH_SECRET=xxx ghcr.io/f/prompts.chat
|
||||||
|
# docker run -p 80:80 --name my-prompts ghcr.io/f/prompts.chat
|
||||||
|
|
||||||
|
FROM node:24-bookworm-slim AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files first for better caching
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
COPY prisma ./prisma/
|
||||||
|
|
||||||
|
# Install all dependencies (including dev for build)
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Copy application files
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Remove unnecessary files
|
||||||
|
RUN rm -rf .github .claude packages .git
|
||||||
|
|
||||||
|
# Generate Prisma client and build
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
RUN npx prisma generate && npm run build
|
||||||
|
|
||||||
|
# Production image
|
||||||
|
FROM node:24-bookworm-slim
|
||||||
|
|
||||||
|
# Labels for GitHub Container Registry
|
||||||
|
LABEL org.opencontainers.image.source="https://github.com/f/awesome-chatgpt-prompts"
|
||||||
|
LABEL org.opencontainers.image.description="prompts.chat - Self-hosted AI prompt library"
|
||||||
|
LABEL org.opencontainers.image.licenses="MIT"
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV PORT=80
|
||||||
|
ENV HOSTNAME="0.0.0.0"
|
||||||
|
ENV DATABASE_URL="postgresql://prompts:prompts@localhost:5432/prompts?schema=public"
|
||||||
|
|
||||||
|
# Install PostgreSQL, supervisor, and utilities
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
postgresql-15 \
|
||||||
|
postgresql-contrib-15 \
|
||||||
|
supervisor \
|
||||||
|
curl \
|
||||||
|
openssl \
|
||||||
|
ca-certificates \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& mkdir -p /var/run/postgresql /var/log/supervisor \
|
||||||
|
&& chown -R postgres:postgres /var/run/postgresql
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy built application from builder
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder /app/prisma ./prisma
|
||||||
|
COPY --from=builder /app/prompts.csv ./prompts.csv
|
||||||
|
COPY --from=builder /app/messages ./messages
|
||||||
|
COPY --from=builder /app/prompts.config.ts ./prompts.config.ts
|
||||||
|
COPY --from=builder /app/package.json ./package.json
|
||||||
|
COPY --from=builder /app/.next/standalone ./
|
||||||
|
COPY --from=builder /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
# Copy node_modules for prisma CLI (needed for migrations)
|
||||||
|
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
|
||||||
|
COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma
|
||||||
|
COPY --from=builder /app/node_modules/prisma ./node_modules/prisma
|
||||||
|
|
||||||
|
# Copy Docker configuration files
|
||||||
|
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||||
|
COPY docker/entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
|
# Create data directory for PostgreSQL
|
||||||
|
RUN mkdir -p /data/postgres && chown -R postgres:postgres /data/postgres
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=90s --retries=3 \
|
||||||
|
CMD curl -f http://localhost:80/api/health || exit 1
|
||||||
|
|
||||||
|
# Entrypoint
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
CMD []
|
||||||
68
docker/Dockerfile.app
Normal file
68
docker/Dockerfile.app
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# prompts.chat App-Only Dockerfile
|
||||||
|
# For use with docker-compose (separate database container)
|
||||||
|
|
||||||
|
FROM node:24-alpine AS base
|
||||||
|
|
||||||
|
# Install dependencies only when needed
|
||||||
|
FROM base AS deps
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
COPY prisma ./prisma/
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Rebuild the source code only when needed
|
||||||
|
FROM base AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Remove unnecessary files
|
||||||
|
RUN rm -rf .github .claude packages docker .git
|
||||||
|
|
||||||
|
# Generate Prisma client
|
||||||
|
RUN npx prisma generate
|
||||||
|
|
||||||
|
# Build Next.js
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production image
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
|
# Install netcat for health checks
|
||||||
|
RUN apk add --no-cache netcat-openbsd curl
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
# Copy built files
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder /app/prisma ./prisma
|
||||||
|
COPY --from=builder /app/prompts.csv ./prompts.csv
|
||||||
|
|
||||||
|
# Copy standalone build
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
# Copy entrypoint
|
||||||
|
COPY docker/entrypoint-app.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV PORT=3000
|
||||||
|
ENV HOSTNAME="0.0.0.0"
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
CMD ["node", "server.js"]
|
||||||
24
docker/entrypoint-app.sh
Normal file
24
docker/entrypoint-app.sh
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🚀 Starting prompts.chat..."
|
||||||
|
|
||||||
|
# Wait for database to be ready
|
||||||
|
echo "⏳ Waiting for database..."
|
||||||
|
until nc -z ${DATABASE_HOST:-db} ${DATABASE_PORT:-5432}; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
echo "✅ Database is ready"
|
||||||
|
|
||||||
|
# Run migrations
|
||||||
|
echo "📦 Running database migrations..."
|
||||||
|
npx prisma migrate deploy
|
||||||
|
|
||||||
|
# Check if database needs seeding
|
||||||
|
echo "🌱 Checking database..."
|
||||||
|
# Skip seed check in entrypoint, let the app handle it
|
||||||
|
|
||||||
|
echo "✨ prompts.chat is starting on port ${PORT:-3000}"
|
||||||
|
|
||||||
|
# Execute the main command
|
||||||
|
exec "$@"
|
||||||
109
docker/entrypoint.sh
Normal file
109
docker/entrypoint.sh
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔═══════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ ║"
|
||||||
|
echo "║ 🚀 prompts.chat - AI Prompt Library ║"
|
||||||
|
echo "║ ║"
|
||||||
|
echo "╚═══════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Generate AUTH_SECRET if not provided
|
||||||
|
if [ -z "$AUTH_SECRET" ]; then
|
||||||
|
export AUTH_SECRET=$(openssl rand -base64 32)
|
||||||
|
echo "⚠ AUTH_SECRET not provided, generated random secret"
|
||||||
|
echo " For production, set AUTH_SECRET environment variable"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PostgreSQL paths
|
||||||
|
PGDATA="/data/postgres"
|
||||||
|
PGBIN="/usr/lib/postgresql/15/bin"
|
||||||
|
|
||||||
|
# Initialize PostgreSQL data directory if needed
|
||||||
|
if [ ! -f "$PGDATA/PG_VERSION" ]; then
|
||||||
|
echo "▶ Initializing PostgreSQL database..."
|
||||||
|
|
||||||
|
# Initialize PostgreSQL
|
||||||
|
su postgres -c "$PGBIN/initdb -D $PGDATA"
|
||||||
|
|
||||||
|
# Configure PostgreSQL
|
||||||
|
cat >> "$PGDATA/postgresql.conf" << EOF
|
||||||
|
listen_addresses = 'localhost'
|
||||||
|
port = 5432
|
||||||
|
max_connections = 100
|
||||||
|
shared_buffers = 128MB
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Configure authentication
|
||||||
|
cat > "$PGDATA/pg_hba.conf" << EOF
|
||||||
|
local all all trust
|
||||||
|
host all all 127.0.0.1/32 trust
|
||||||
|
host all all ::1/128 trust
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Start PostgreSQL temporarily to create database
|
||||||
|
su postgres -c "$PGBIN/pg_ctl -D $PGDATA -l /tmp/pg.log start"
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# Create database and user
|
||||||
|
su postgres -c "$PGBIN/createuser -s prompts 2>/dev/null" || true
|
||||||
|
su postgres -c "$PGBIN/createdb -O prompts prompts 2>/dev/null" || true
|
||||||
|
|
||||||
|
# Stop PostgreSQL (supervisor will start it)
|
||||||
|
su postgres -c "$PGBIN/pg_ctl -D $PGDATA stop"
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
echo "✓ PostgreSQL initialized"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start supervisor (manages PostgreSQL and Next.js)
|
||||||
|
echo "▶ Starting services..."
|
||||||
|
/usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf &
|
||||||
|
SUPERVISOR_PID=$!
|
||||||
|
|
||||||
|
# Wait for PostgreSQL to be ready
|
||||||
|
echo "▶ Waiting for PostgreSQL..."
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
if $PGBIN/pg_isready -h localhost -p 5432 >/dev/null 2>&1; then
|
||||||
|
echo "✓ PostgreSQL is ready"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [ $i -eq 30 ]; then
|
||||||
|
echo "✗ PostgreSQL failed to start"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
# Run database migrations
|
||||||
|
echo "▶ Running database migrations..."
|
||||||
|
cd /app
|
||||||
|
./node_modules/prisma/build/index.js migrate deploy
|
||||||
|
echo "✓ Migrations complete"
|
||||||
|
|
||||||
|
# Seed database if empty (check if Prompt table exists and has data)
|
||||||
|
PROMPT_COUNT=$(su postgres -c "$PGBIN/psql -h localhost -U prompts -d prompts -t -c \"SELECT COUNT(*) FROM \\\"Prompt\\\"\"" 2>/dev/null | tr -d ' ' || echo "0")
|
||||||
|
if [ "$PROMPT_COUNT" = "0" ] || [ -z "$PROMPT_COUNT" ]; then
|
||||||
|
echo "▶ Seeding database with prompts..."
|
||||||
|
# Run seed script if available
|
||||||
|
if [ -f "/app/prisma/seed.ts" ]; then
|
||||||
|
npx tsx /app/prisma/seed.ts || echo "⚠ Seeding skipped or failed"
|
||||||
|
fi
|
||||||
|
echo "✓ Database seeded"
|
||||||
|
else
|
||||||
|
echo "✓ Database already has $PROMPT_COUNT prompts"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔═══════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ ║"
|
||||||
|
echo "║ ✅ prompts.chat is running! ║"
|
||||||
|
echo "║ ║"
|
||||||
|
echo "║ 🌐 Open http://localhost:${PORT:-80} in your browser ║"
|
||||||
|
echo "║ ║"
|
||||||
|
echo "╚═══════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Keep container running and forward signals
|
||||||
|
wait $SUPERVISOR_PID
|
||||||
28
docker/supervisord.conf
Normal file
28
docker/supervisord.conf
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
[supervisord]
|
||||||
|
nodaemon=true
|
||||||
|
user=root
|
||||||
|
logfile=/var/log/supervisor/supervisord.log
|
||||||
|
pidfile=/var/run/supervisord.pid
|
||||||
|
childlogdir=/var/log/supervisor
|
||||||
|
|
||||||
|
[program:postgresql]
|
||||||
|
command=/usr/lib/postgresql/15/bin/postgres -D /data/postgres
|
||||||
|
user=postgres
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
priority=10
|
||||||
|
stdout_logfile=/var/log/supervisor/postgresql.log
|
||||||
|
stderr_logfile=/var/log/supervisor/postgresql-error.log
|
||||||
|
|
||||||
|
[program:nextjs]
|
||||||
|
command=node /app/server.js
|
||||||
|
directory=/app
|
||||||
|
user=root
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
priority=20
|
||||||
|
startsecs=10
|
||||||
|
startretries=3
|
||||||
|
environment=NODE_ENV="production",PORT="%(ENV_PORT)s",HOSTNAME="0.0.0.0",DATABASE_URL="%(ENV_DATABASE_URL)s",AUTH_SECRET="%(ENV_AUTH_SECRET)s"
|
||||||
|
stdout_logfile=/var/log/supervisor/nextjs.log
|
||||||
|
stderr_logfile=/var/log/supervisor/nextjs-error.log
|
||||||
@@ -14,7 +14,7 @@ const nextConfig: NextConfig = {
|
|||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
// Enable standalone output for Docker
|
// Enable standalone output for Docker
|
||||||
// output: "standalone",
|
output: "standalone",
|
||||||
// Experimental features
|
// Experimental features
|
||||||
experimental: {
|
experimental: {
|
||||||
// Enable server actions
|
// Enable server actions
|
||||||
|
|||||||
30
src/app/api/health/route.ts
Normal file
30
src/app/api/health/route.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { db } from "@/lib/db";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
try {
|
||||||
|
// Check database connection
|
||||||
|
await db.$queryRaw`SELECT 1`;
|
||||||
|
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
status: "healthy",
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
database: "connected",
|
||||||
|
},
|
||||||
|
{ status: 200 }
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
status: "unhealthy",
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
database: "disconnected",
|
||||||
|
error: error instanceof Error ? error.message : "Unknown error",
|
||||||
|
},
|
||||||
|
{ status: 503 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user