Add Help page with markdown support and routing
This commit is contained in:
738
dockerize.md
Normal file
738
dockerize.md
Normal file
@@ -0,0 +1,738 @@
|
||||
# Docker Production Build Guide
|
||||
|
||||
## Goal
|
||||
Build a production-ready Docker image containing both the frontend (React/Vite) and backend (Express/Node.js) that connects to an external MySQL database. The container will serve the frontend on port 5173 and proxy API requests to the backend running on localhost:3000 inside the container.
|
||||
|
||||
**SUCCESS CRITERIA**: Docker image is built successfully and ready for deployment.
|
||||
|
||||
## Architecture
|
||||
- **Single Docker Container**: Contains both FE and BE
|
||||
- **External Database**: MySQL server (not in container)
|
||||
- **Port Exposure**: Only port 5173 (frontend) exposed
|
||||
- **Internal Communication**: FE → BE via localhost:3000 inside container
|
||||
- **Reverse Proxy**: Caddy handles SSL/domain externally (Unraid setup)
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Create Dockerfile
|
||||
|
||||
**File**: `Dockerfile` (in project root)
|
||||
|
||||
### Requirements:
|
||||
1. **Multi-stage build** for optimal image size
|
||||
2. **Stage 1 - Build Frontend**:
|
||||
- Use `node:20-alpine` as base
|
||||
- Set working directory to `/app`
|
||||
- Copy `package.json`, `pnpm-lock.yaml`, `pnpm-workspace.yaml`
|
||||
- Copy `packages/frontend/package.json`
|
||||
- Install pnpm globally: `npm install -g pnpm`
|
||||
- Install dependencies: `pnpm install --frozen-lockfile`
|
||||
- Copy entire `packages/frontend` directory
|
||||
- Build frontend: `cd packages/frontend && pnpm build`
|
||||
- Output will be in `packages/frontend/dist`
|
||||
|
||||
3. **Stage 2 - Build Backend**:
|
||||
- Use `node:20-alpine` as base
|
||||
- Set working directory to `/app`
|
||||
- Copy workspace files (same as stage 1)
|
||||
- Install pnpm globally
|
||||
- Install dependencies: `pnpm install --frozen-lockfile`
|
||||
- Copy entire `packages/backend` directory
|
||||
- Generate Prisma client: `cd packages/backend && npx prisma generate`
|
||||
- Build backend: `cd packages/backend && pnpm build`
|
||||
- Output will be in `packages/backend/dist`
|
||||
|
||||
4. **Stage 3 - Production Runtime**:
|
||||
- Use `node:20-alpine` as base
|
||||
- Install pnpm globally and `serve` package globally: `npm install -g pnpm serve`
|
||||
- Set working directory to `/app`
|
||||
- Copy production dependencies setup:
|
||||
- Copy `package.json`, `pnpm-workspace.yaml`, `pnpm-lock.yaml`
|
||||
- Copy `packages/backend/package.json` to `packages/backend/`
|
||||
- Copy `packages/frontend/package.json` to `packages/frontend/`
|
||||
- Install ONLY production dependencies: `pnpm install --prod --frozen-lockfile`
|
||||
- Copy backend build artifacts:
|
||||
- From stage 2: `packages/backend/dist` → `/app/packages/backend/dist`
|
||||
- From stage 2: `packages/backend/prisma` → `/app/packages/backend/prisma`
|
||||
- From stage 2: `packages/backend/node_modules/.prisma` → `/app/packages/backend/node_modules/.prisma`
|
||||
- Copy frontend build artifacts:
|
||||
- From stage 1: `packages/frontend/dist` → `/app/packages/frontend/dist`
|
||||
- Create uploads directory: `mkdir -p /app/packages/backend/uploads`
|
||||
- Expose port 5173
|
||||
- Create startup script (see Task 2)
|
||||
- Set CMD to run startup script
|
||||
|
||||
### Important Notes:
|
||||
- Use `.dockerignore` to exclude `node_modules`, `dist`, `.env` files
|
||||
- Ensure Prisma client is generated and copied correctly
|
||||
- Backend needs access to Prisma schema for migrations
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Create Startup Script
|
||||
|
||||
**File**: `docker-entrypoint.sh` (in project root)
|
||||
|
||||
### Requirements:
|
||||
1. **Bash script** with proper shebang: `#!/bin/sh`
|
||||
2. **Set error handling**: `set -e`
|
||||
3. **Environment validation**:
|
||||
- Check required env vars: `DATABASE_URL`, `SESSION_SECRET`
|
||||
- Exit with error message if missing
|
||||
4. **Database migration**:
|
||||
- Navigate to backend: `cd /app/packages/backend`
|
||||
- Run Prisma migrations: `npx prisma migrate deploy`
|
||||
- Optional: Run seed if `RUN_SEED=true` env var is set
|
||||
5. **Start backend**:
|
||||
- Start backend in background: `node dist/server.js &`
|
||||
- Store PID: `BACKEND_PID=$!`
|
||||
6. **Wait for backend**:
|
||||
- Add 5-second sleep or health check loop
|
||||
- Ensure backend is ready before starting frontend
|
||||
7. **Start frontend**:
|
||||
- Serve frontend on port 5173: `serve -s /app/packages/frontend/dist -l 5173`
|
||||
8. **Signal handling**:
|
||||
- Trap SIGTERM/SIGINT to gracefully shutdown both processes
|
||||
- Kill backend PID on exit
|
||||
|
||||
### Script Template:
|
||||
```bash
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "🚀 Starting Znakovni.hr Production Container..."
|
||||
|
||||
# Validate environment
|
||||
if [ -z "$DATABASE_URL" ]; then
|
||||
echo "❌ ERROR: DATABASE_URL is required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run migrations
|
||||
cd /app/packages/backend
|
||||
echo "📦 Running database migrations..."
|
||||
npx prisma migrate deploy
|
||||
|
||||
# Start backend
|
||||
echo "🔧 Starting backend server..."
|
||||
cd /app/packages/backend
|
||||
node dist/server.js &
|
||||
BACKEND_PID=$!
|
||||
|
||||
# Wait for backend
|
||||
sleep 5
|
||||
|
||||
# Start frontend
|
||||
echo "🎨 Starting frontend server..."
|
||||
cd /app
|
||||
serve -s packages/frontend/dist -l 5173 -n &
|
||||
FRONTEND_PID=$!
|
||||
|
||||
# Trap signals
|
||||
trap "kill $BACKEND_PID $FRONTEND_PID" SIGTERM SIGINT
|
||||
|
||||
# Wait for processes
|
||||
wait $BACKEND_PID $FRONTEND_PID
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Create .dockerignore
|
||||
|
||||
**File**: `.dockerignore` (in project root)
|
||||
|
||||
### Exclude:
|
||||
```
|
||||
node_modules
|
||||
dist
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
*.log
|
||||
.git
|
||||
.gitignore
|
||||
.vscode
|
||||
.idea
|
||||
*.md
|
||||
!README.md
|
||||
packages/*/node_modules
|
||||
packages/*/dist
|
||||
packages/backend/uploads/*
|
||||
!packages/backend/uploads/.gitkeep
|
||||
.DS_Store
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Update Frontend Build Configuration
|
||||
|
||||
**File**: `packages/frontend/vite.config.ts`
|
||||
|
||||
### Changes Required:
|
||||
1. **Remove hardcoded host**: Change `host: '192.168.1.238'` to `host: '0.0.0.0'` or remove entirely
|
||||
2. **Production build**: Ensure build outputs to `dist` directory
|
||||
3. **API proxy**: Remove proxy config (not needed in production, handled by env var)
|
||||
|
||||
### Updated Config:
|
||||
```typescript
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import path from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 5173,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:3000',
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Update Frontend API Configuration
|
||||
|
||||
**File**: `packages/frontend/.env.production` (create new file)
|
||||
|
||||
### Content:
|
||||
```env
|
||||
VITE_API_URL=http://localhost:3000
|
||||
```
|
||||
|
||||
**Note**: In production Docker container, frontend connects to backend via localhost since they're in the same container.
|
||||
|
||||
---
|
||||
|
||||
## Task 6: Update Backend Server Configuration
|
||||
|
||||
**File**: `packages/backend/src/server.ts`
|
||||
|
||||
### Verify/Update:
|
||||
1. **HOST binding**: Ensure `HOST = process.env.HOST || '0.0.0.0'` (line 85)
|
||||
2. **PORT**: Ensure `PORT = parseInt(process.env.PORT || '3000', 10)` (line 16)
|
||||
3. **CORS origin**: Should accept `FRONTEND_URL` from env (line 24)
|
||||
4. **Static uploads**: Ensure uploads path is correct (line 57-58)
|
||||
|
||||
### No changes needed if these are already correct (they are based on codebase retrieval).
|
||||
|
||||
---
|
||||
|
||||
## Task 7: Create Docker Compose Example
|
||||
|
||||
**File**: `docker-compose.yml` (in project root)
|
||||
|
||||
### Purpose:
|
||||
Provide example for running the container with proper environment variables.
|
||||
|
||||
### Content:
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
znakovni:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: znakovni:latest
|
||||
container_name: znakovni-app
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5173:5173"
|
||||
environment:
|
||||
# Database Configuration
|
||||
DATABASE_URL: "mysql://user:password@192.168.1.74:3306/znakovni"
|
||||
# Optional: Shadow database for Prisma migrations
|
||||
# SHADOW_DATABASE_URL: "mysql://user:password@192.168.1.74:3306/znakovni_shadow"
|
||||
|
||||
# Server Configuration
|
||||
NODE_ENV: production
|
||||
PORT: 3000
|
||||
HOST: 0.0.0.0
|
||||
FRONTEND_URL: http://localhost:5173
|
||||
|
||||
# Session Secret (CHANGE THIS!)
|
||||
SESSION_SECRET: your-super-secret-session-key-change-in-production-min-32-chars
|
||||
|
||||
# OAuth - Google (optional, configure if using)
|
||||
# GOOGLE_CLIENT_ID: your-google-client-id
|
||||
# GOOGLE_CLIENT_SECRET: your-google-client-secret
|
||||
# GOOGLE_CALLBACK_URL: https://yourdomain.com/api/auth/google/callback
|
||||
|
||||
# OAuth - Microsoft (optional, configure if using)
|
||||
# MICROSOFT_CLIENT_ID: your-microsoft-client-id
|
||||
# MICROSOFT_CLIENT_SECRET: your-microsoft-client-secret
|
||||
# MICROSOFT_CALLBACK_URL: https://yourdomain.com/api/auth/microsoft/callback
|
||||
|
||||
# File Upload
|
||||
UPLOAD_DIR: ./uploads
|
||||
MAX_FILE_SIZE: 104857600
|
||||
|
||||
# Optional: Run database seed on startup
|
||||
# RUN_SEED: "false"
|
||||
|
||||
volumes:
|
||||
# Persist uploaded files
|
||||
- ./uploads:/app/packages/backend/uploads
|
||||
|
||||
networks:
|
||||
- znakovni-network
|
||||
|
||||
# Health check
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/api/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
networks:
|
||||
znakovni-network:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 8: Create Production Environment Template
|
||||
|
||||
**File**: `.env.production.example` (in project root)
|
||||
|
||||
### Content:
|
||||
```env
|
||||
# ===========================================
|
||||
# ZNAKOVNI.HR PRODUCTION CONFIGURATION
|
||||
# ===========================================
|
||||
|
||||
# Database Configuration (REQUIRED)
|
||||
# Point to your external MySQL server
|
||||
DATABASE_URL="mysql://username:password@192.168.1.74:3306/znakovni"
|
||||
|
||||
# Optional: Shadow database for Prisma migrations
|
||||
# SHADOW_DATABASE_URL="mysql://username:password@192.168.1.74:3306/znakovni_shadow"
|
||||
|
||||
# Server Configuration
|
||||
NODE_ENV=production
|
||||
PORT=3000
|
||||
HOST=0.0.0.0
|
||||
FRONTEND_URL=http://localhost:5173
|
||||
|
||||
# Session Secret (REQUIRED - CHANGE THIS!)
|
||||
# Generate with: openssl rand -base64 32
|
||||
SESSION_SECRET=CHANGE-THIS-TO-A-RANDOM-STRING-MIN-32-CHARACTERS-LONG
|
||||
|
||||
# OAuth - Google (Optional)
|
||||
GOOGLE_CLIENT_ID=
|
||||
GOOGLE_CLIENT_SECRET=
|
||||
GOOGLE_CALLBACK_URL=https://yourdomain.com/api/auth/google/callback
|
||||
|
||||
# OAuth - Microsoft (Optional)
|
||||
MICROSOFT_CLIENT_ID=
|
||||
MICROSOFT_CLIENT_SECRET=
|
||||
MICROSOFT_CALLBACK_URL=https://yourdomain.com/api/auth/microsoft/callback
|
||||
|
||||
# File Upload Configuration
|
||||
UPLOAD_DIR=./uploads
|
||||
MAX_FILE_SIZE=104857600
|
||||
|
||||
# Database Seeding (Optional)
|
||||
# Set to "true" to run seed on container startup
|
||||
RUN_SEED=false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 9: Build Docker Image
|
||||
|
||||
### Build Command:
|
||||
```bash
|
||||
# From project root
|
||||
docker build -t znakovni:latest .
|
||||
```
|
||||
|
||||
### Verify Build Success:
|
||||
```bash
|
||||
# Check image exists
|
||||
docker images | grep znakovni
|
||||
|
||||
# Should show:
|
||||
# znakovni latest <image-id> <time> <size>
|
||||
```
|
||||
|
||||
### Expected Build Output:
|
||||
- ✅ Stage 1: Frontend build completes successfully
|
||||
- ✅ Stage 2: Backend build completes successfully
|
||||
- ✅ Stage 3: Production image created
|
||||
- ✅ Image tagged as `znakovni:latest`
|
||||
- ✅ No build errors
|
||||
|
||||
**TASK COMPLETE** when image is built and shows in `docker images` output.
|
||||
|
||||
---
|
||||
|
||||
## Task 10: Optional - Test Container Locally
|
||||
|
||||
**NOTE**: This task is OPTIONAL. The main goal is to build the image. User will handle deployment.
|
||||
|
||||
### Run Container (Manual):
|
||||
```bash
|
||||
docker run -d \
|
||||
--name znakovni-app \
|
||||
-p 5173:5173 \
|
||||
-e DATABASE_URL="mysql://user:password@192.168.1.74:3306/znakovni" \
|
||||
-e SESSION_SECRET="your-secret-key-here" \
|
||||
-e NODE_ENV=production \
|
||||
-v $(pwd)/uploads:/app/packages/backend/uploads \
|
||||
znakovni:latest
|
||||
```
|
||||
|
||||
### Run with Docker Compose:
|
||||
```bash
|
||||
# Edit docker-compose.yml with your database credentials
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Stop
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
### Verify Container:
|
||||
```bash
|
||||
# Check if container is running
|
||||
docker ps
|
||||
|
||||
# Check logs
|
||||
docker logs znakovni-app
|
||||
|
||||
# Test backend health
|
||||
curl http://localhost:5173/api/health
|
||||
|
||||
# Test frontend
|
||||
curl http://localhost:5173
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 11: Save Docker Image for Deployment
|
||||
|
||||
**NOTE**: This task is for preparing the image for transfer to Unraid. User will handle actual deployment.
|
||||
|
||||
### Save Image to File:
|
||||
```bash
|
||||
# Save image to tar file
|
||||
docker save znakovni:latest -o znakovni.tar
|
||||
|
||||
# Verify file created
|
||||
ls -lh znakovni.tar
|
||||
```
|
||||
|
||||
### Image is Ready for Deployment
|
||||
The `znakovni.tar` file can now be:
|
||||
- Transferred to Unraid server
|
||||
- Loaded with: `docker load -i znakovni.tar`
|
||||
- Deployed by user
|
||||
|
||||
---
|
||||
|
||||
## DEPLOYMENT REFERENCE (For User)
|
||||
|
||||
### Unraid Deployment Steps:
|
||||
1. **Transfer image**: `scp znakovni.tar user@unraid:/mnt/user/appdata/`
|
||||
2. **Load image**: `docker load -i znakovni.tar`
|
||||
3. **Create container** in Unraid UI:
|
||||
- **Repository**: `znakovni:latest`
|
||||
- **Port**: `5173` → `5173` (or any host port)
|
||||
- **Environment Variables**: Add all required vars from `.env.production.example`
|
||||
- **Volume**: Map host path to `/app/packages/backend/uploads`
|
||||
4. **Configure Caddy** reverse proxy:
|
||||
- Point domain to `http://unraid-ip:5173`
|
||||
- Caddy handles SSL/HTTPS
|
||||
|
||||
### Caddy Configuration Example:
|
||||
```
|
||||
znakovni.yourdomain.com {
|
||||
reverse_proxy http://localhost:5173
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 12: Troubleshooting Build Issues
|
||||
|
||||
### Build-Time Issues:
|
||||
|
||||
1. **pnpm install fails**:
|
||||
- Ensure `pnpm-lock.yaml` is present
|
||||
- Check Node.js version compatibility (needs Node 20)
|
||||
- Verify `pnpm-workspace.yaml` is copied correctly
|
||||
|
||||
2. **Frontend build fails**:
|
||||
- Check TypeScript compilation errors
|
||||
- Verify all dependencies are installed
|
||||
- Ensure Vite config is correct
|
||||
- Check for missing environment variables
|
||||
|
||||
3. **Backend build fails**:
|
||||
- Check TypeScript compilation errors
|
||||
- Verify Prisma schema is valid
|
||||
- Ensure all dependencies are installed
|
||||
|
||||
4. **Prisma generate fails**:
|
||||
- Verify `DATABASE_URL` format is correct (even for build)
|
||||
- Check Prisma schema syntax
|
||||
- Ensure MySQL provider is specified
|
||||
|
||||
5. **Docker build context too large**:
|
||||
- Verify `.dockerignore` is present and correct
|
||||
- Ensure `node_modules` and `dist` are excluded
|
||||
|
||||
### Debug Build:
|
||||
```bash
|
||||
# Build with no cache to see all steps
|
||||
docker build --no-cache -t znakovni:latest .
|
||||
|
||||
# Build with progress output
|
||||
docker build --progress=plain -t znakovni:latest .
|
||||
|
||||
# Check build context size
|
||||
docker build --no-cache -t znakovni:latest . 2>&1 | grep "Sending build context"
|
||||
```
|
||||
|
||||
### Runtime Issues (If Testing Container):
|
||||
|
||||
1. **Database Connection Failed**:
|
||||
- Verify `DATABASE_URL` is correct
|
||||
- Ensure MySQL server allows connections from Docker container IP
|
||||
- Check MySQL user permissions: `GRANT ALL ON znakovni.* TO 'user'@'%';`
|
||||
|
||||
2. **Frontend Can't Reach Backend**:
|
||||
- Verify `VITE_API_URL=http://localhost:3000` in frontend build
|
||||
- Check backend is listening on `0.0.0.0:3000`
|
||||
- Verify CORS settings in backend
|
||||
|
||||
3. **Container Exits Immediately**:
|
||||
- Check logs: `docker logs znakovni-app`
|
||||
- Verify all required env vars are set
|
||||
- Ensure database is accessible
|
||||
|
||||
### Debug Commands (For Running Container):
|
||||
```bash
|
||||
# Enter running container
|
||||
docker exec -it znakovni-app sh
|
||||
|
||||
# Check backend process
|
||||
docker exec znakovni-app ps aux | grep node
|
||||
|
||||
# Check environment variables
|
||||
docker exec znakovni-app env | grep DATABASE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## REFERENCE: Security Checklist (For User - Deployment Phase)
|
||||
|
||||
Before deploying to production:
|
||||
|
||||
- [ ] Change `SESSION_SECRET` to a strong random string (min 32 chars)
|
||||
- [ ] Use strong MySQL password
|
||||
- [ ] Configure OAuth credentials if using Google/Microsoft login
|
||||
- [ ] Update OAuth callback URLs to production domain
|
||||
- [ ] Ensure MySQL server has firewall rules (only allow necessary IPs)
|
||||
- [ ] Set up regular database backups
|
||||
- [ ] Configure Caddy with proper SSL certificates
|
||||
- [ ] Review and restrict MySQL user permissions
|
||||
- [ ] Set up monitoring/logging for the container
|
||||
- [ ] Configure automatic container restarts (`restart: unless-stopped`)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
This setup creates a **fully self-contained Docker image** with:
|
||||
- ✅ Frontend (React/Vite) built and served on port 5173
|
||||
- ✅ Backend (Express/Node.js) running on internal port 3000
|
||||
- ✅ Prisma ORM with automatic migrations on startup
|
||||
- ✅ External MySQL database connection
|
||||
- ✅ Persistent uploads via Docker volumes
|
||||
- ✅ Health checks and graceful shutdown
|
||||
- ✅ Production-optimized multi-stage build
|
||||
- ✅ Ready for Unraid + Caddy deployment
|
||||
|
||||
---
|
||||
|
||||
## IMPLEMENTATION PLAN FOR AI AGENTS
|
||||
|
||||
### Phase 1: Create Required Files (Tasks 1-8)
|
||||
1. ✅ Create `Dockerfile` with multi-stage build
|
||||
2. ✅ Create `docker-entrypoint.sh` startup script
|
||||
3. ✅ Create `.dockerignore` file
|
||||
4. ✅ Update `packages/frontend/vite.config.ts`
|
||||
5. ✅ Create `packages/frontend/.env.production`
|
||||
6. ✅ Verify `packages/backend/src/server.ts` configuration
|
||||
7. ✅ Create `docker-compose.yml` example
|
||||
8. ✅ Create `.env.production.example` template
|
||||
|
||||
### Phase 2: Build Docker Image (Task 9)
|
||||
9. ✅ Run `docker build -t znakovni:latest .`
|
||||
10. ✅ Verify image exists with `docker images | grep znakovni`
|
||||
|
||||
### Phase 3: Optional - Save Image (Task 11)
|
||||
11. ⚠️ OPTIONAL: Save image to tar file for transfer
|
||||
|
||||
### SUCCESS CRITERIA:
|
||||
**✅ COMPLETE** when `docker build` succeeds and image `znakovni:latest` exists.
|
||||
|
||||
User will handle deployment to Unraid.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference: Files to Create/Modify
|
||||
|
||||
### New Files to Create:
|
||||
1. ✅ `Dockerfile` - Multi-stage Docker build
|
||||
2. ✅ `docker-entrypoint.sh` - Container startup script (make executable)
|
||||
3. ✅ `.dockerignore` - Exclude unnecessary files from build
|
||||
4. ✅ `docker-compose.yml` - Example compose configuration
|
||||
5. ✅ `.env.production.example` - Production environment template
|
||||
6. ✅ `packages/frontend/.env.production` - Frontend production env
|
||||
|
||||
### Files to Modify:
|
||||
1. ⚠️ `packages/frontend/vite.config.ts` - Update host to `0.0.0.0`
|
||||
2. ✅ `packages/backend/src/server.ts` - Already correct (verify HOST=0.0.0.0)
|
||||
|
||||
### Commands to Run After Creating Files:
|
||||
```bash
|
||||
# Make entrypoint executable
|
||||
chmod +x docker-entrypoint.sh
|
||||
|
||||
# Build the image (THIS IS THE MAIN GOAL)
|
||||
docker build -t znakovni:latest .
|
||||
|
||||
# Verify build success
|
||||
docker images | grep znakovni
|
||||
|
||||
# OPTIONAL: Test run locally
|
||||
docker-compose up -d
|
||||
|
||||
# OPTIONAL: Check logs
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Important Implementation Notes for AI Agents
|
||||
|
||||
### 1. Dockerfile Multi-Stage Build Pattern:
|
||||
- **Stage 1 (frontend-builder)**: Build React app with Vite
|
||||
- **Stage 2 (backend-builder)**: Build Express app with TypeScript + Prisma
|
||||
- **Stage 3 (production)**: Combine built artifacts, minimal runtime
|
||||
|
||||
### 2. Critical Prisma Considerations:
|
||||
- Prisma client MUST be generated during build (`npx prisma generate`)
|
||||
- Copy `node_modules/.prisma` directory to production stage
|
||||
- Copy `prisma/` directory for migration files
|
||||
- Run `prisma migrate deploy` in entrypoint script (NOT `migrate dev`)
|
||||
|
||||
### 3. Frontend Build Environment:
|
||||
- Create `packages/frontend/.env.production` with `VITE_API_URL=http://localhost:3000`
|
||||
- This ensures frontend makes API calls to localhost (same container)
|
||||
- Vite will embed this at build time
|
||||
|
||||
### 4. Networking Inside Container:
|
||||
- Backend binds to `0.0.0.0:3000` (accessible from frontend)
|
||||
- Frontend served on `0.0.0.0:5173` (exposed to host)
|
||||
- Both processes run in same container, communicate via localhost
|
||||
|
||||
### 5. Database Connection:
|
||||
- MySQL server is EXTERNAL (not in container)
|
||||
- Container must be able to reach MySQL IP (192.168.1.74)
|
||||
- Ensure MySQL allows connections from Docker network
|
||||
- Test with: `mysql -h 192.168.1.74 -u user -p znakovni`
|
||||
|
||||
### 6. Volume Mounting for Uploads:
|
||||
- Backend uploads to `/app/packages/backend/uploads`
|
||||
- Mount host directory to persist files across container restarts
|
||||
- Example: `-v /mnt/user/appdata/znakovni/uploads:/app/packages/backend/uploads`
|
||||
|
||||
### 7. Environment Variables Priority:
|
||||
**Required**:
|
||||
- `DATABASE_URL` - MySQL connection string
|
||||
- `SESSION_SECRET` - Random string (min 32 chars)
|
||||
|
||||
**Recommended**:
|
||||
- `NODE_ENV=production`
|
||||
- `PORT=3000`
|
||||
- `HOST=0.0.0.0`
|
||||
- `FRONTEND_URL=http://localhost:5173`
|
||||
|
||||
**Optional**:
|
||||
- OAuth credentials (if using Google/Microsoft login)
|
||||
- `RUN_SEED=true` (to seed database on first run)
|
||||
|
||||
### 8. Startup Sequence:
|
||||
1. Container starts → runs `docker-entrypoint.sh`
|
||||
2. Validate environment variables
|
||||
3. Run Prisma migrations (`migrate deploy`)
|
||||
4. Start backend in background
|
||||
5. Wait 5 seconds for backend to initialize
|
||||
6. Start frontend server (blocking, keeps container alive)
|
||||
7. Trap signals for graceful shutdown
|
||||
|
||||
### 9. Build Success Checklist:
|
||||
- [ ] All files created (Tasks 1-8)
|
||||
- [ ] `docker build` command runs without errors
|
||||
- [ ] Image `znakovni:latest` appears in `docker images` output
|
||||
- [ ] No TypeScript compilation errors
|
||||
- [ ] No Prisma generation errors
|
||||
- [ ] Multi-stage build completes all 3 stages
|
||||
|
||||
### 10. Optional Testing Checklist (If Running Container):
|
||||
- [ ] Container starts and stays running
|
||||
- [ ] Backend health check responds: `curl http://localhost:5173/api/health`
|
||||
- [ ] Frontend loads: `curl http://localhost:5173`
|
||||
- [ ] Database connection works (check logs)
|
||||
- [ ] Uploads persist after container restart
|
||||
|
||||
### 10. Deployment Flow (For User Reference):
|
||||
```bash
|
||||
# 1. Build image (AI AGENT TASK - MAIN GOAL)
|
||||
docker build -t znakovni:latest .
|
||||
|
||||
# 2. OPTIONAL: Save image (if deploying to different machine)
|
||||
docker save znakovni:latest -o znakovni.tar
|
||||
|
||||
# 3-7. USER HANDLES DEPLOYMENT
|
||||
# - Transfer to Unraid
|
||||
# - Load image
|
||||
# - Create container
|
||||
# - Configure Caddy
|
||||
# - Monitor
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## End of Guide
|
||||
|
||||
**GOAL**: Build Docker image `znakovni:latest` successfully.
|
||||
|
||||
**AI AGENT TASKS**:
|
||||
1. Implement Tasks 1-8 (create/modify files)
|
||||
2. Execute Task 9 (build image)
|
||||
3. Verify image exists
|
||||
|
||||
**USER RESPONSIBILITY**: Deployment to Unraid and production configuration.
|
||||
|
||||
This guide provides complete instructions for dockerizing the Znakovni.hr application. Follow the tasks in order, and refer to the troubleshooting section if build issues arise.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user