fix: resolve critical production deployment issues
Test and Lint / backend-test (push) Successful in 1m11s
continuous-integration/drone/push Build is passing
Test and Lint / frontend-test (push) Successful in 2m21s
Version and Release / version-bump (push) Successful in 33s
Version and Release / trigger-drone (push) Successful in 2s

- Fix database connection error "getaddrinfo ENOTFOUND postgres"
- Add wait-for-db.sh script to ensure PostgreSQL is ready before starting
- Fix email processor initialization timing issue
- Add missing storage path environment variables
- Add database dependency to backend service
- Enhance health check endpoint with database connectivity check
- Update production database defaults to match docker-compose
- Install postgresql-client in Docker image for health checks
- Document all required environment variables in .env.example

Fixes immediate production deployment failures and ensures proper service startup order.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-13 22:14:29 +02:00
parent d1033cb83a
commit 26c05912fc
8 changed files with 187 additions and 49 deletions
+33 -34
View File
@@ -1,42 +1,41 @@
NODE_ENV=development
# Backend Environment Variables Example
# Copy this file to .env and update with your values
# Application
NODE_ENV=production
PORT=3000
# URLs
ADMIN_URL=http://localhost:3000
FRONTEND_URL=http://localhost:3001
# Security
JWT_SECRET=dev-secret-key
JWT_SECRET=your-very-secure-jwt-secret-at-least-32-characters-long
# Email Configuration
SMTP_HOST=mailhog
SMTP_PORT=1025
SMTP_SECURE=false
SMTP_USER=
SMTP_PASS=
EMAIL_FROM=noreply@localhost
# Storage Paths (relative to project root)
STORAGE_PATH=./storage
EVENTS_PATH=./storage/events
ARCHIVE_PATH=./storage/events/archived
# Logging
LOG_LEVEL=info
# URLs
ADMIN_URL=https://yourdomain.com
FRONTEND_URL=https://yourdomain.com
# Database Configuration
# Development: Use SQLite
DATABASE_CLIENT=sqlite3
DATABASE_PATH=./data/photo_sharing.db
DATABASE_CLIENT=pg
DB_HOST=db
DB_PORT=5432
DB_USER=picpeak
DB_PASSWORD=your-secure-database-password
DB_NAME=picpeak
# Production: Use PostgreSQL
# DATABASE_CLIENT=pg
# DB_HOST=localhost
# DB_PORT=5432
# DB_USER=picpeak
# DB_PASSWORD=your-secure-password
# DB_NAME=picpeak
# Email Configuration
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-smtp-username
SMTP_PASS=your-smtp-password
EMAIL_FROM=noreply@yourdomain.com
# Umami Analytics (optional)
UMAMI_URL=
UMAMI_WEBSITE_ID=
# Storage Paths (Docker)
STORAGE_PATH=/app/storage
EVENTS_PATH=/app/storage/events
ARCHIVE_PATH=/app/storage/events/archived
# Analytics (Optional)
UMAMI_URL=https://analytics.yourdomain.com
UMAMI_WEBSITE_ID=your-website-id
# Logging
LOG_LEVEL=info
+6 -3
View File
@@ -16,8 +16,8 @@ FROM node:18-alpine
WORKDIR /app
# Install dumb-init for proper signal handling
RUN apk add --no-cache dumb-init
# Install dumb-init for proper signal handling and postgresql-client for database checks
RUN apk add --no-cache dumb-init postgresql-client
# Create non-root user
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
@@ -26,6 +26,9 @@ RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --chown=nodejs:nodejs . .
# Make wait script executable
RUN chmod +x wait-for-db.sh
# Create necessary directories
RUN mkdir -p storage/events/active storage/events/archived storage/thumbnails data logs && \
chown -R nodejs:nodejs storage data logs
@@ -35,4 +38,4 @@ USER nodejs
EXPOSE 3000
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "server.js"]
CMD ["./wait-for-db.sh", "node", "server.js"]
+4 -4
View File
@@ -27,11 +27,11 @@ const config = {
production: {
client: process.env.DATABASE_CLIENT || 'pg',
connection: {
host: process.env.DB_HOST || 'postgres',
host: process.env.DB_HOST || 'db',
port: process.env.DB_PORT || 5432,
user: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD || 'postgres',
database: process.env.DB_NAME || 'photo_sharing'
user: process.env.DB_USER || 'picpeak',
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME || 'picpeak'
},
pool: {
min: 2,
+22 -4
View File
@@ -13,7 +13,7 @@ const path = require('path');
const { initializeDatabase } = require('./src/database/db');
const { startFileWatcher } = require('./src/services/fileWatcher');
const { startExpirationChecker } = require('./src/services/expirationChecker');
const { startEmailQueueProcessor } = require('./src/services/emailProcessor');
const { initializeTransporter, startEmailQueueProcessor } = require('./src/services/emailProcessor');
const { maintenanceMiddleware } = require('./src/middleware/maintenance');
const { sessionTimeoutMiddleware } = require('./src/middleware/sessionTimeout');
const logger = require('./src/utils/logger');
@@ -152,8 +152,25 @@ app.use('/thumbnails', require('./src/middleware/photoAuth'), setCorsHeaders, se
app.use('/uploads', setCorsHeaders, secureStatic(path.join(__dirname, 'storage/uploads')));
// Health check endpoint
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
app.get('/api/health', async (req, res) => {
try {
// Check database connectivity
await db.raw('SELECT 1');
res.json({
status: 'ok',
database: 'connected',
timestamp: new Date().toISOString()
});
} catch (error) {
logger.error('Health check failed:', error);
res.status(503).json({
status: 'error',
database: 'disconnected',
error: error.message,
timestamp: new Date().toISOString()
});
}
});
// Routes
@@ -189,7 +206,8 @@ async function startServer() {
// Start expiration checker
startExpirationChecker();
// Start email queue processor
// Initialize email transporter and start queue processor
await initializeTransporter();
startEmailQueueProcessor();
app.listen(PORT, () => {
+7 -4
View File
@@ -395,12 +395,15 @@ function stopEmailQueueProcessor() {
}
}
// Initialize on module load
initializeTransporter().then(() => {
startEmailQueueProcessor();
});
// Initialize on module load - DISABLED for production startup
// This will be called from server.js after database is ready
// initializeTransporter().then(() => {
// startEmailQueueProcessor();
// });
module.exports = {
initializeTransporter,
startEmailQueueProcessor,
sendTemplateEmail,
processEmailQueue,
queueEmail,
+25
View File
@@ -0,0 +1,25 @@
#!/bin/sh
# wait-for-db.sh - Wait for PostgreSQL to be ready before starting the application
set -e
host="$DB_HOST"
port="${DB_PORT:-5432}"
user="${DB_USER:-picpeak}"
echo "Waiting for PostgreSQL at $host:$port..."
# Wait for PostgreSQL to be ready
until PGPASSWORD=$DB_PASSWORD psql -h "$host" -p "$port" -U "$user" -d "${DB_NAME:-picpeak}" -c '\q' 2>/dev/null; do
>&2 echo "PostgreSQL is unavailable - sleeping"
sleep 2
done
>&2 echo "PostgreSQL is up - executing command"
# Run migrations
echo "Running database migrations..."
npm run migrate
# Execute the main command
exec "$@"