feat: docker compose

This commit is contained in:
Arda Kilicdagi
2026-03-31 15:08:05 +03:00
parent fc08dbdefe
commit b05b5dd788
8 changed files with 397 additions and 378 deletions
+5 -1
View File
@@ -10,6 +10,7 @@ dist
# Git # Git
.git .git
.gitignore .gitignore
.gitattributes
# IDE # IDE
.vscode .vscode
@@ -23,10 +24,11 @@ dist
.env.development.local .env.development.local
.env.test.local .env.test.local
.env.production.local .env.production.local
.env.sentry-build-plugin
# Docker # Docker
compose.yml
docker-compose*.yml docker-compose*.yml
Dockerfile*
# Development files # Development files
.github .github
@@ -38,6 +40,8 @@ packages
# Test files # Test files
coverage coverage
.nyc_output .nyc_output
vitest.config.ts
vitest.setup.ts
# OS files # OS files
.DS_Store .DS_Store
+179 -130
View File
@@ -1,45 +1,68 @@
# Docker Deployment Guide # Docker Deployment Guide
Run your own prompts.chat instance with a single command. Run your own prompts.chat instance using Docker Compose.
## Quick Start ## Quick Start
```bash ```bash
docker run -d \ git clone https://github.com/f/prompts.chat.git
--name prompts \ cd prompts.chat
-p 4444:3000 \ docker compose up -d
-v prompts-data:/data \
ghcr.io/f/prompts.chat
``` ```
**First run:** The container will clone the repository and build the app (~3-5 minutes).
**Subsequent runs:** Starts immediately using the cached build.
Open http://localhost:4444 in your browser. Open http://localhost:4444 in your browser.
## Custom Branding ## Using a Pre-built Image
Customize your instance with environment variables: Edit `compose.yml` and replace the `build` block with the published image:
```bash ```yaml
docker run -d \ services:
--name my-prompts \ app:
-p 4444:3000 \ # build:
-v prompts-data:/data \ # context: .
-e PCHAT_NAME="Acme Prompts" \ # dockerfile: docker/Dockerfile
-e PCHAT_DESCRIPTION="Our team's AI prompt library" \ image: ghcr.io/f/prompts.chat:latest
-e PCHAT_COLOR="#ff6600" \
-e PCHAT_AUTH_PROVIDERS="github,google" \
-e PCHAT_LOCALES="en,es,fr" \
ghcr.io/f/prompts.chat
``` ```
> **Note:** Branding is applied during the first build. To change branding later, delete the volume and re-run: Then run:
> ```bash
> docker rm -f my-prompts ```bash
> docker volume rm prompts-data docker compose up -d
> docker run ... # with new env vars ```
> ```
## Standalone (Bring Your Own Database)
If you already have a PostgreSQL instance, you can run just the app container:
```bash
docker build -f docker/Dockerfile -t prompts.chat .
docker run -d \
--name prompts \
-p 4444:3000 \
-e DATABASE_URL="postgresql://user:pass@your-db-host:5432/prompts?schema=public" \
-e AUTH_SECRET="$(openssl rand -base64 32)" \
prompts.chat
```
## Custom Branding
All branding is configured via `PCHAT_*` environment variables at runtime -- no rebuild needed.
```yaml
# compose.yml
services:
app:
environment:
# ... existing vars ...
PCHAT_NAME: "Acme Prompts"
PCHAT_DESCRIPTION: "Our team's AI prompt library"
PCHAT_COLOR: "#ff6600"
PCHAT_AUTH_PROVIDERS: "github,google"
PCHAT_LOCALES: "en,es,fr"
```
Then restart: `docker compose up -d`
## Configuration Variables ## Configuration Variables
@@ -95,103 +118,102 @@ All variables are prefixed with `PCHAT_` to avoid conflicts.
| Variable | Description | Default | | Variable | Description | Default |
|----------|-------------|---------| |----------|-------------|---------|
| `AUTH_SECRET` | Secret for authentication tokens | Auto-generated | | `AUTH_SECRET` | Secret for authentication tokens | Auto-generated (set explicitly for production) |
| `PORT` | Internal container port | `3000` | | `DATABASE_URL` | PostgreSQL connection string | Set in compose.yml |
| `DATABASE_URL` | PostgreSQL connection string | Internal DB | | `DIRECT_URL` | Direct PostgreSQL URL (bypasses poolers) | Same as DATABASE_URL |
| `PORT` | Host port mapping | `4444` |
## Production Setup ## Production Setup
For production, set `AUTH_SECRET` explicitly: For production, always set `AUTH_SECRET` explicitly:
```bash ```bash
docker run -d \ # Generate a secret
--name prompts \ export AUTH_SECRET=$(openssl rand -base64 32)
-p 4444:3000 \
-v prompts-data:/data \ # Start with explicit secret
-e AUTH_SECRET="$(openssl rand -base64 32)" \ docker compose up -d
-e PCHAT_NAME="My Company Prompts" \ ```
ghcr.io/f/prompts.chat
Or add it to a `.env` file next to `compose.yml`:
```env
AUTH_SECRET=your-secret-key-here
``` ```
### With OAuth Providers ### With OAuth Providers
```bash ```yaml
docker run -d \ # compose.yml
--name prompts \ services:
-p 4444:3000 \ app:
-v prompts-data:/data \ environment:
-e AUTH_SECRET="your-secret-key" \ # ... existing vars ...
-e PCHAT_AUTH_PROVIDERS="github,google" \ PCHAT_AUTH_PROVIDERS: "github,google"
-e AUTH_GITHUB_ID="your-github-client-id" \ AUTH_GITHUB_ID: "your-github-client-id"
-e AUTH_GITHUB_SECRET="your-github-client-secret" \ AUTH_GITHUB_SECRET: "your-github-client-secret"
-e AUTH_GOOGLE_ID="your-google-client-id" \ AUTH_GOOGLE_ID: "your-google-client-id"
-e AUTH_GOOGLE_SECRET="your-google-client-secret" \ AUTH_GOOGLE_SECRET: "your-google-client-secret"
ghcr.io/f/prompts.chat
``` ```
### With AI Features (OpenAI) ### With AI Features (OpenAI)
```yaml
# compose.yml
services:
app:
environment:
# ... existing vars ...
PCHAT_FEATURE_AI_SEARCH: "true"
OPENAI_API_KEY: "sk-..."
```
## Database Seeding
Seed the database with example prompts:
```bash ```bash
docker run -d \ docker compose exec app npx prisma db seed
--name prompts \
-p 4444:3000 \
-v prompts-data:/data \
-e PCHAT_FEATURE_AI_SEARCH="true" \
-e OPENAI_API_KEY="sk-..." \
ghcr.io/f/prompts.chat
``` ```
## Custom Logo ## Custom Logo
Mount your logo file: Mount your logo file into the app container:
```bash ```yaml
docker run -d \ # compose.yml
--name prompts \ services:
-p 4444:3000 \ app:
-v prompts-data:/data \ volumes:
-v ./my-logo.svg:/data/app/public/logo.svg \ - ./my-logo.svg:/app/public/logo.svg
-e PCHAT_NAME="My App" \ environment:
ghcr.io/f/prompts.chat PCHAT_LOGO: "/logo.svg"
``` ```
## Data Persistence ## Data Persistence
### All-in-One Image PostgreSQL data is stored in the `postgres_data` named volume and persists across container restarts, rebuilds, and image updates.
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 ### Backup
```bash ```bash
# Backup database # Backup database
docker exec prompts pg_dump -U prompts prompts > backup.sql docker compose exec db pg_dump -U prompts prompts > backup.sql
# Restore database # Restore database
docker exec -i prompts psql -U prompts prompts < backup.sql docker compose exec -T db psql -U prompts prompts < backup.sql
``` ```
## Building Locally ## Building Locally
Build and run locally:
```bash ```bash
docker build -f docker/Dockerfile -t prompts.chat . docker compose build
docker run -p 4444:3000 -v prompts-data:/data prompts.chat docker compose up -d
``` ```
## Health Check ## Health Check
The container includes a health check endpoint: The app container includes a health check endpoint:
```bash ```bash
curl http://localhost:4444/api/health curl http://localhost:4444/api/health
@@ -211,48 +233,82 @@ Response:
### View Logs ### View Logs
```bash ```bash
# All logs # All services
docker logs prompts docker compose logs
# Follow logs # Follow logs
docker logs -f prompts docker compose logs -f
# PostgreSQL logs (inside container) # App logs only
docker exec prompts cat /var/log/supervisor/postgresql.log docker compose logs app
# Next.js logs (inside container) # Database logs only
docker exec prompts cat /var/log/supervisor/nextjs.log docker compose logs db
``` ```
### Database Access ### Database Access
```bash ```bash
# Connect to PostgreSQL # Connect to PostgreSQL
docker exec -it prompts psql -U prompts -d prompts docker compose exec db psql -U prompts -d prompts
# Run SQL query # Run a query
docker exec prompts psql -U prompts -d prompts -c "SELECT COUNT(*) FROM \"Prompt\"" docker compose exec db psql -U prompts -d prompts -c "SELECT COUNT(*) FROM \"Prompt\""
``` ```
### Container Shell ### Container Shell
```bash ```bash
docker exec -it prompts bash docker compose exec app sh
docker compose exec db bash
``` ```
### Common Issues ### Common Issues
**Container won't start:** **App container keeps restarting:**
- Check logs: `docker logs prompts` - Check logs: `docker compose logs app`
- Ensure port 4444 is available: `lsof -i :4444` - Database may not be ready yet -- the entrypoint retries for up to 60 seconds
**Database connection errors:** **Database connection errors:**
- Wait for PostgreSQL to initialize (can take 30-60 seconds on first run) - Verify the `db` service is healthy: `docker compose ps`
- Check database logs: `docker exec prompts cat /var/log/supervisor/postgresql.log` - Check database logs: `docker compose logs db`
**Authentication issues:** **Authentication issues:**
- Ensure `AUTH_SECRET` is set for production - Set `AUTH_SECRET` explicitly for production
- For OAuth, verify callback URLs are configured correctly - For OAuth, verify callback URLs match your domain
## Updating
```bash
# If using pre-built images
docker compose pull
docker compose up -d
# If building locally
git pull
docker compose build
docker compose up -d
```
Data persists in the `postgres_data` volume across updates.
## Migrating from the Old Single-Image Setup
If you were using the previous all-in-one Docker image:
```bash
# 1. Export your database from the old container
docker exec prompts pg_dump -U prompts prompts > backup.sql
# 2. Stop and remove the old container
docker stop prompts && docker rm prompts
# 3. Start the new compose setup
docker compose up -d
# 4. Import your data
docker compose exec -T db psql -U prompts prompts < backup.sql
```
## Resource Requirements ## Resource Requirements
@@ -266,33 +322,9 @@ Recommended:
- 2GB RAM - 2GB RAM
- 10GB disk space - 10GB disk space
## Updating ## Running Behind a Reverse Proxy
```bash ### Nginx
# 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 4444:3000 \
-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 ```nginx
server { server {
@@ -316,6 +348,23 @@ server {
} }
``` ```
### Caddy
```
prompts.example.com {
reverse_proxy localhost:4444
}
```
## Security Considerations
1. **Always set AUTH_SECRET** in production
2. **Use HTTPS** -- put a reverse proxy (Nginx, Caddy, Traefik) in front
3. **Change default database password** -- update `POSTGRES_PASSWORD` in compose.yml and the connection strings
4. **Limit exposed ports** -- only expose what's needed
5. **Regular updates** -- pull the latest image regularly
6. **Backup data** -- regularly backup the database
## License ## License
MIT MIT
+57
View File
@@ -0,0 +1,57 @@
# Fix Docker setup: proper multi-container architecture with Docker Compose
## Summary
The previous Docker setup bundled PostgreSQL and the Next.js app into a single image managed by `supervisord`. This is an anti-pattern — it violates the "one container, one process" principle, clones the repo and runs the full build at runtime (3–5 min cold start), and makes scaling, updating, and debugging significantly harder.
This PR replaces it with a proper Docker Compose setup following the "Docker way":
- **`app`** — Next.js application container (multi-stage build, standalone output)
- **`db`** — Official `postgres:17-bookworm` container
Database data is persisted via a named Docker volume and survives container restarts, rebuilds, and image updates.
## Changes
### New files
- **`compose.yml`** — Docker Compose configuration with `app` and `db` services, healthchecks, and a named volume for PostgreSQL data
- **`docker/entrypoint.sh`** — Lightweight entrypoint: waits for the database, runs `prisma migrate deploy`, then starts the app
### Modified files
- **`docker/Dockerfile`** — Replaced the single-stage bootstrap image with a proper 4-stage multi-stage build:
- `base` — Shared `node:24-bookworm-slim` with system dependencies
- `deps` — Runs `npm ci` (and `prisma generate` via postinstall)
- `builder` — Runs `npm run build`, produces `.next/standalone/`
- `runner` — Minimal production image; copies standalone output, installs only Prisma CLI for migrations
- **`.dockerignore`** — Added `compose.yml`, test config files
- **`DOCKER.md`** — Complete rewrite covering the new compose-based workflow, standalone single-container usage, migration guide from the old setup, backup/restore, and production deployment tips
### Deleted files
- `docker/bootstrap.sh` — Replaced by `docker/entrypoint.sh`
- `docker/supervisord.conf` — No longer needed (one process per container)
## Key design decisions
- **PCHAT_* env vars work at runtime** — `src/lib/config/index.ts` already applies overrides via `applyEnvOverrides()`. No rebuild is needed to change branding or feature flags; just update environment variables and restart.
- **Migrations run at startup** — `prisma migrate deploy` runs in the entrypoint before the app starts, keeping deployments simple without init containers.
- **Seeding is manual** — `docker compose exec app npx prisma db seed`
- **All images are Debian-based** — `node:24-bookworm-slim` (bookworm) and `postgres:17-bookworm`
- **`.github/workflows/docker-publish.yml` needs no changes** — it already builds `./docker/Dockerfile` with context `.`
## Quick start after this PR
```bash
git clone https://github.com/f/prompts.chat.git
cd prompts.chat
docker compose up -d
```
Open http://localhost:4444
## Testing
- `docker compose build` — builds successfully
- `docker compose up -d` — both services start, DB healthcheck passes before app starts
- `curl http://localhost:4444/api/health` — returns `{"status":"healthy","database":"connected"}`
- `curl http://localhost:4444/` — homepage returns 200
- Data persists across `docker compose down && docker compose up -d`
+44
View File
@@ -0,0 +1,44 @@
services:
db:
image: postgres:17-bookworm
restart: unless-stopped
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 -d prompts"]
interval: 5s
timeout: 5s
retries: 10
start_period: 10s
app:
build:
context: .
dockerfile: docker/Dockerfile
# To use a pre-built image instead of building locally, comment out
# the 'build' block above and uncomment the line below:
# image: ghcr.io/f/prompts.chat:latest
restart: unless-stopped
ports:
- "${PORT:-4444}:3000"
environment:
DATABASE_URL: "postgresql://prompts:prompts@db:5432/prompts?schema=public"
DIRECT_URL: "postgresql://prompts:prompts@db:5432/prompts?schema=public"
AUTH_SECRET: "${AUTH_SECRET:-}"
NODE_ENV: production
# Branding (all optional — set any PCHAT_* variable to customize at runtime)
# PCHAT_NAME: "My Prompt Library"
# PCHAT_DESCRIPTION: "Our team's AI prompt collection"
# PCHAT_COLOR: "#6366f1"
# PCHAT_AUTH_PROVIDERS: "credentials"
# PCHAT_LOCALES: "en"
depends_on:
db:
condition: service_healthy
volumes:
postgres_data:
+73 -28
View File
@@ -1,49 +1,94 @@
# prompts.chat Bootstrap Docker Image # prompts.chat Docker Image
# Lightweight image that clones and builds on first run # Multi-stage build for a production-ready Next.js application
# #
# Usage: # Usage with Docker Compose:
# docker run -p 4444:3000 -v prompts-data:/data ghcr.io/f/prompts.chat # docker compose up -d
# docker run -p 4444:3000 -v prompts-data:/data -e PCHAT_NAME="My App" ghcr.io/f/prompts.chat #
# Usage standalone (bring your own PostgreSQL):
# docker build -f docker/Dockerfile -t prompts.chat .
# docker run -p 4444:3000 -e DATABASE_URL="postgresql://..." prompts.chat
FROM node:24-bookworm-slim # ---- Stage 1: Base ----
FROM node:24-bookworm-slim AS base
RUN apt-get update && apt-get install -y --no-install-recommends \
openssl \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# ---- Stage 2: Dependencies ----
FROM base AS deps
# Copy dependency manifests and Prisma schema (postinstall runs prisma generate)
COPY package.json package-lock.json ./
COPY prisma/schema.prisma prisma/schema.prisma
COPY prisma.config.ts tsconfig.json ./
# Dummy DATABASE_URL for prisma generate (no actual connection is made)
ENV DATABASE_URL="postgresql://dummy:dummy@localhost:5432/dummy"
RUN npm ci
# ---- Stage 3: Builder ----
FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
ENV DATABASE_URL="postgresql://dummy:dummy@localhost:5432/dummy"
RUN npm run build
# ---- Stage 4: Runner ----
FROM node:24-bookworm-slim AS runner
LABEL org.opencontainers.image.source="https://github.com/f/prompts.chat" LABEL org.opencontainers.image.source="https://github.com/f/prompts.chat"
LABEL org.opencontainers.image.description="prompts.chat - Self-hosted AI prompt library" LABEL org.opencontainers.image.description="prompts.chat - Self-hosted AI prompt library"
LABEL org.opencontainers.image.licenses="MIT" LABEL org.opencontainers.image.licenses="MIT"
# Install PostgreSQL, git, and utilities
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
postgresql-15 \
postgresql-contrib-15 \
supervisor \
git \
curl \
openssl \ openssl \
curl \
ca-certificates \ ca-certificates \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/*
&& mkdir -p /var/run/postgresql /var/log/supervisor \
&& chown -R postgres:postgres /var/run/postgresql
# Create directories
WORKDIR /app WORKDIR /app
RUN mkdir -p /data/postgres /data/app && chown -R postgres:postgres /data/postgres
# Copy bootstrap scripts RUN groupadd --system --gid 1001 nodejs && \
COPY docker/bootstrap.sh /bootstrap.sh useradd --system --uid 1001 --gid nodejs nextjs
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
RUN chmod +x /bootstrap.sh # Copy standalone build output
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
# Copy Prisma schema, migrations, and config for runtime migrations
COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma
COPY --from=builder --chown=nextjs:nodejs /app/prisma.config.ts ./prisma.config.ts
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json
COPY --from=builder --chown=nextjs:nodejs /app/tsconfig.json ./tsconfig.json
# Install only Prisma CLI and its dependencies for runtime migrations
RUN npm install --no-save prisma@6.19 dotenv
# Copy entrypoint script
COPY --chown=nextjs:nodejs docker/entrypoint.sh ./entrypoint.sh
RUN chmod +x ./entrypoint.sh
# Environment defaults
ENV NODE_ENV=production ENV NODE_ENV=production
ENV PORT=3000 ENV PORT=3000
ENV HOSTNAME="0.0.0.0" ENV HOSTNAME=0.0.0.0
ENV DATABASE_URL="postgresql://prompts:prompts@localhost:5432/prompts?schema=public" ENV NEXT_TELEMETRY_DISABLED=1
ENV DIRECT_URL="postgresql://prompts:prompts@localhost:5432/prompts?schema=public"
ENV REPO_URL="https://github.com/f/prompts.chat.git"
EXPOSE 3000 EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=10s --start-period=180s --retries=3 \ HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -f http://localhost:3000/api/health || exit 1 CMD curl -f http://localhost:3000/api/health || exit 1
ENTRYPOINT ["/bootstrap.sh"] USER nextjs
ENTRYPOINT ["./entrypoint.sh"]
-181
View File
@@ -1,181 +0,0 @@
#!/bin/bash
set -e
echo ""
echo "╔═══════════════════════════════════════════════════════════════╗"
echo "║ ║"
echo "║ 🚀 prompts.chat - AI Prompt Library ║"
echo "║ ║"
echo "╚═══════════════════════════════════════════════════════════════╝"
echo ""
# Paths
APP_DIR="/data/app"
PGDATA="/data/postgres"
PGBIN="/usr/lib/postgresql/15/bin"
BUILD_MARKER="/data/.built"
# Generate AUTH_SECRET if not provided
if [ -z "$AUTH_SECRET" ]; then
if [ -f "/data/.auth_secret" ]; then
export AUTH_SECRET=$(cat /data/.auth_secret)
else
export AUTH_SECRET=$(openssl rand -base64 32)
echo "$AUTH_SECRET" > /data/.auth_secret
echo "⚠ AUTH_SECRET generated and saved"
fi
fi
# Initialize PostgreSQL if needed
if [ ! -f "$PGDATA/PG_VERSION" ]; then
echo "▶ Initializing PostgreSQL..."
su postgres -c "$PGBIN/initdb -D $PGDATA"
cat >> "$PGDATA/postgresql.conf" << EOF
listen_addresses = 'localhost'
port = 5432
max_connections = 100
shared_buffers = 128MB
EOF
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
su postgres -c "$PGBIN/pg_ctl -D $PGDATA -l /tmp/pg.log start"
sleep 3
su postgres -c "$PGBIN/createuser -s prompts 2>/dev/null" || true
su postgres -c "$PGBIN/createdb -O prompts prompts 2>/dev/null" || true
su postgres -c "$PGBIN/pg_ctl -D $PGDATA stop"
sleep 2
echo "✓ PostgreSQL initialized"
fi
# Clone and build on first run
if [ ! -f "$BUILD_MARKER" ]; then
echo ""
echo "▶ First run detected - building prompts.chat..."
echo ""
# Clone repository
if [ ! -d "$APP_DIR/.git" ]; then
echo "▶ Cloning repository..."
rm -rf "$APP_DIR"
git clone --depth 1 "$REPO_URL" "$APP_DIR"
echo "✓ Repository cloned"
fi
cd "$APP_DIR"
# Clean up unnecessary files
rm -rf .github .claude packages .git
# Install dependencies (including devDependencies needed for build)
echo "▶ Installing dependencies..."
NODE_ENV=development npm ci
echo "✓ Dependencies installed"
# Run docker-setup.js to generate config with branding
echo "▶ Generating configuration..."
node scripts/docker-setup.js
echo "✓ Configuration generated"
# Generate Prisma client
echo "▶ Generating Prisma client..."
npx prisma generate
echo "✓ Prisma client generated"
# Build Next.js
echo "▶ Building Next.js application (this may take a few minutes)..."
npm run build
echo "✓ Build complete"
# Copy static files for standalone mode
echo "▶ Copying static assets..."
cp -r .next/static .next/standalone/.next/
cp -r public .next/standalone/
echo "✓ Static assets copied"
# Mark as built
touch "$BUILD_MARKER"
echo ""
echo "✅ Build complete! Starting application..."
echo ""
else
echo "✓ Using existing build"
cd "$APP_DIR"
fi
# Start supervisord (manages PostgreSQL and Next.js)
echo "▶ Starting services..."
/usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf &
SUPERVISOR_PID=$!
# Wait for PostgreSQL
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 migrations
echo "▶ Running database migrations..."
cd "$APP_DIR"
npx prisma migrate deploy
echo "✓ Migrations complete"
# Seed on first run only
SEED_MARKER="/data/.seeded"
if [ ! -f "$SEED_MARKER" ]; then
echo "▶ Seeding database..."
if npx tsx prisma/seed.ts 2>/dev/null; then
touch "$SEED_MARKER"
echo "✓ Database seeded"
else
echo "⚠ Seeding skipped"
fi
fi
# Wait for supervisord to be ready
echo "▶ Waiting for supervisord..."
for i in $(seq 1 30); do
if kill -0 $SUPERVISOR_PID 2>/dev/null && [ -S /var/run/supervisor.sock ]; then
echo "✓ Supervisord is ready"
break
fi
if ! kill -0 $SUPERVISOR_PID 2>/dev/null; then
echo "✗ Supervisord process died"
exit 1
fi
if [ $i -eq 30 ]; then
echo "✗ Supervisord socket not available after 30s"
exit 1
fi
sleep 1
done
# Start Next.js via supervisorctl
echo "▶ Starting Next.js..."
supervisorctl start nextjs
echo ""
echo "╔═══════════════════════════════════════════════════════════════╗"
echo "║ ║"
echo "║ ✅ prompts.chat is running! ║"
echo "║ ║"
echo "║ 🌐 Open http://localhost:${PORT:-80} in your browser ║"
echo "║ ║"
echo "╚═══════════════════════════════════════════════════════════════╝"
echo ""
wait $SUPERVISOR_PID
+39
View File
@@ -0,0 +1,39 @@
#!/bin/sh
set -e
# Generate AUTH_SECRET if not provided
if [ -z "$AUTH_SECRET" ]; then
AUTH_SECRET=$(openssl rand -base64 32)
export AUTH_SECRET
echo "[prompts.chat] AUTH_SECRET not set -- generated a random value."
echo "[prompts.chat] Set AUTH_SECRET explicitly for production to persist sessions across restarts."
fi
# Wait for PostgreSQL to be ready
echo "[prompts.chat] Waiting for database..."
MAX_RETRIES=30
RETRY_COUNT=0
until node -e "
const net = require('net');
const url = new URL(process.env.DATABASE_URL);
const sock = net.createConnection({ host: url.hostname, port: url.port || 5432 });
sock.on('connect', () => { sock.destroy(); process.exit(0); });
sock.on('error', () => process.exit(1));
" 2>/dev/null; do
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]; then
echo "[prompts.chat] ERROR: Database not reachable after ${MAX_RETRIES} attempts."
exit 1
fi
echo "[prompts.chat] Database not ready (attempt ${RETRY_COUNT}/${MAX_RETRIES}), retrying in 2s..."
sleep 2
done
# Run database migrations
echo "[prompts.chat] Running database migrations..."
npx prisma migrate deploy
echo "[prompts.chat] Migrations applied successfully."
# Start the application
echo "[prompts.chat] Starting application on port ${PORT:-3000}..."
exec node server.js
-38
View File
@@ -1,38 +0,0 @@
[unix_http_server]
file=/var/run/supervisor.sock
chmod=0700
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[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 /data/app/.next/standalone/server.js
directory=/data/app
user=root
autostart=false
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