diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 70165a9..d3fa70d 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -1,461 +1,647 @@ -# Placebo.mk Deployment Guide +# Placebo.mk Deployment Guide (Coolify) -## Overview -This document outlines the deployment strategy for Placebo.mk - a Macedonian news site with sarcastic tone, built with TanStack (React), NestJS, and Strapi CMS. +Complete deployment guide for the Placebo.mk satirical news platform on a VPS using Coolify. -## Architecture -- **Frontend**: TanStack (React 19 + Query + Router) + Vite + Tailwind CSS -- **Backend**: NestJS + TypeORM + PostgreSQL (migrated from SQLite) -- **CMS**: Strapi with PostgreSQL -- **Deployment**: Docker + Coolify on VPS +## Table of Contents -## Deployment Options +1. [Architecture Overview](#architecture-overview) +2. [Prerequisites](#prerequisites) +3. [Coolify Setup](#coolify-setup) +4. [Database Configuration](#database-configuration) +5. [Service Deployments](#service-deployments) +6. [Environment Variables Reference](#environment-variables-reference) +7. [Domain & SSL Configuration](#domain--ssl-configuration) +8. [Push Notifications Setup](#push-notifications-setup) +9. [Post-Deployment Checklist](#post-deployment-checklist) +10. [Troubleshooting](#troubleshooting) -### Option 1: Coolify + VPS (Recommended) -**Pros**: Self-hosted, cost-effective, full control, Macedonian audience optimized -**Cons**: Requires server management +--- -### Option 2: Platform-as-a-Service -**Pros**: No server management, automatic scaling -**Cons**: More expensive, less control +## Architecture Overview -## Coolify + VPS Deployment - -### Phase 1: Infrastructure Setup - -#### 1.1 VPS Requirements -- **Provider**: Hetzner (Germany) or DigitalOcean (Amsterdam) -- **Specs**: 4GB RAM, 2 vCPU, 80GB SSD (€10-15/month) -- **OS**: Ubuntu 22.04 LTS -- **Domain**: placebo.mk - -#### 1.2 Install Coolify -```bash -# On VPS -curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash +``` + ┌──────────────────────────────────────┐ + │ Coolify Reverse Proxy │ + │ (Traefik/Caddy) │ + └────────────────┬─────────────────────┘ + │ + ┌────────────────────────────────┼────────────────────────────────┐ + │ │ │ + ▼ ▼ ▼ +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Frontend │ │ PWA │ │ CMS │ +│ (React+Vite) │ │ (React+Vite+SW) │ │ (Strapi) │ +│ nginx:80 │ │ nginx:80 │ │ Node.js:1337 │ +│ placebo.mk │ │ app.placebo.mk │ │ cms.placebo.mk │ +└────────┬────────┘ └────────┬────────┘ └────────┬────────┘ + │ │ │ + │ │ │ + │ │ │ + └────────────────────────────────┼────────────────────────────────┘ + │ + ▼ + ┌─────────────────┐ + │ Backend │ + │ (NestJS) │ + │ Node.js:3000 │ + │ api.placebo.mk │ + └────────┬────────┘ + │ + ▼ + ┌─────────────────┐ + │ PostgreSQL │ + │ :5432 │ + └─────────────────┘ ``` -Coolify automatically installs: -- Docker and Docker Compose -- Traefik reverse proxy -- Let's Encrypt SSL -- Admin interface +### Tech Stack -#### 1.3 Domain Configuration -1. Purchase `placebo.mk` domain -2. Configure DNS A records: - - `placebo.mk` → VPS IP - - `www.placebo.mk` → VPS IP - - `api.placebo.mk` → VPS IP - - `cms.placebo.mk` → VPS IP +| Service | Technology | Port | Purpose | +|---------|------------|------|---------| +| Frontend | React 19 + Vite + Tailwind | 80 | Main website (SSG) | +| PWA | React 19 + Vite + Workbox | 80 | Progressive Web App with push | +| Backend | NestJS + TypeORM | 3000 | REST API | +| CMS | Strapi 5 | 1337 | Content management | +| Database | PostgreSQL 16 | 5432 | Primary data store | -### Phase 2: Database Migration +--- -#### 2.1 PostgreSQL Setup -Create PostgreSQL database via Coolify one-click apps: -- Database name: `placebomk` -- Username: `placebo` -- Password: Generate secure password +## Prerequisites -#### 2.2 Migrate from SQLite -Detailed migration plan available in `scripts/migrate-to-postgres.md` +### Server Requirements -Run migration script: +| Resource | Minimum | Recommended | +|----------|---------|-------------| +| RAM | 4GB | 8GB | +| CPU | 2 vCPU | 4 vCPU | +| Storage | 40GB SSD | 80GB SSD | +| OS | Ubuntu 22.04 | Ubuntu 24.04 | + +### Domain Setup + +Configure these DNS A records pointing to your VPS IP: + +| Type | Name | Purpose | +|------|------|---------| +| A | `@` | Main website (frontend) | +| A | `www` | WWW redirect | +| A | `api` | Backend API | +| A | `app` | PWA | +| A | `cms` | Strapi admin | + +### Required Before Deployment + +- [ ] Coolify installed and accessible +- [ ] Domain DNS configured +- [ ] GitHub repository connected to Coolify +- [ ] SMTP server (optional, for emails) + +--- + +## Coolify Setup + +### 1. Create Project + +1. Login to Coolify dashboard +2. Navigate to **Projects** +3. Click **+ New Project** +4. Name: `placebo` +5. Click **Create** + +### 2. Create Environment + +1. Inside `placebo` project +2. Click **+ New Environment** +3. Name: `production` +4. Click **Create** + +### 3. Connect Repository + +1. Go to **Configuration** → **Source** +2. Click **Connect GitHub** +3. Authorize Coolify to access your repositories +4. Select repository: `your-org/placeboMk` + +--- + +## Database Configuration + +### Create PostgreSQL Service + +1. In `production` environment, click **+ New Resource** +2. Select **Database** → **PostgreSQL** +3. Configure: + +| Setting | Value | +|---------|-------| +| Name | `placebo-db` | +| Version | `16-alpine` | +| PostgreSQL User | `placebo_user` | +| PostgreSQL Password | `` | +| PostgreSQL Database | `placebo_db` | +| Persistent Volume | Enabled | + +4. Click **Deploy** + +### Note Connection Details + +After deployment, note the internal hostname: +``` +Host: placebo-db +Port: 5432 +Database: placebo_db +Username: placebo_user +Password: +``` + +--- + +## Service Deployments + +### 1. Backend (NestJS API) + +#### Create Service + +1. Click **+ New Resource** → **Service** +2. Select **Dockerfile** (not Docker Compose) +3. Name: `placebo-backend` +4. Repository: Select your connected repo + +#### Configuration + +| Setting | Value | +|---------|-------| +| Dockerfile Location | `backend/Dockerfile` | +| Build Context | `backend` | +| Port | `3000` | + +#### Environment Variables + +```env +NODE_ENV=production +PORT=3000 + +# Database +DATABASE_TYPE=postgres +DATABASE_HOST=placebo-db +DATABASE_PORT=5432 +DATABASE_USERNAME=placebo_user +DATABASE_PASSWORD= +DATABASE_NAME=placebo_db +DATABASE_SYNCHRONIZE=false +DATABASE_LOGGING=false + +# JWT Authentication +JWT_SECRET= +JWT_EXPIRATION=86400 + +# CORS +CORS_ORIGIN=https://placebo.mk,https://www.placebo.mk,https://app.placebo.mk + +# Strapi CMS +STRAPI_URL=https://cms.placebo.mk +STRAPI_API_TOKEN= + +# Push Notifications +VAPID_SUBJECT=mailto:contact@placebo.mk +VAPID_PUBLIC_KEY= +VAPID_PRIVATE_KEY= +``` + +#### Generate Secrets + +**JWT Secret:** ```bash -# Test migration locally first -docker-compose up -d postgres -./scripts/migrate-data.sh +openssl rand -base64 64 | tr -d '\n' +``` -# Production migration +**VAPID Keys** (run locally): +```bash cd backend -npm run migrate:postgres +npm install +npm run generate-vapid ``` -#### 2.3 Migration Strategy -1. **Development**: Use SQLite for local development -2. **Staging**: PostgreSQL with test data -3. **Production**: PostgreSQL with migrated data -4. **Backup**: Maintain SQLite backups during transition +#### Health Check -### Phase 3: Application Deployment +| Setting | Value | +|---------|-------| +| Path | `/health` | +| Interval | 30s | +| Timeout | 10s | +| Retries | 3 | -#### 3.1 Connect GitHub Repository -1. Connect your GitHub repo to Coolify -2. Create three applications: - - `placebo-frontend` (from `/frontend`) - - `placebo-backend` (from `/backend`) - - `placebo-cms` (from `/cms/cms`) +#### Domain -#### 3.2 Configure Applications +1. Go to **Domains** tab +2. Add: `api.placebo.mk` +3. Enable **HTTPS** (Let's Encrypt) -**Backend Configuration:** -- **Build Command**: `npm run build` -- **Start Command**: `npm run start:prod` -- **Port**: 3000 -- **Environment Variables**: - ``` - NODE_ENV=production - DATABASE_URL=postgresql://placebo:PASSWORD@postgres:5432/placebomk - CORS_ORIGIN=https://placebo.mk - JWT_SECRET=your-secure-jwt-secret - ``` +--- -**Frontend Configuration:** -- **Build Command**: `npm run build` -- **Output Directory**: `dist` -- **Environment Variables**: - ``` - VITE_API_URL=https://api.placebo.mk/api/v1 - VITE_STRAPI_URL=https://cms.placebo.mk - ``` +### 2. Frontend (Main Website) -**CMS Configuration:** -- **Build Command**: `npm run build` -- **Start Command**: `npm run start` -- **Port**: 1337 -- **Environment Variables**: - ``` - DATABASE_CLIENT=postgres - DATABASE_HOST=postgres - DATABASE_PORT=5432 - DATABASE_NAME=strapi - DATABASE_USERNAME=placebo - DATABASE_PASSWORD=PASSWORD - ``` +#### Create Service -#### 3.3 Configure Domains in Coolify -- `placebo.mk` → Frontend application -- `api.placebo.mk` → Backend API -- `cms.placebo.mk` → Strapi CMS admin +1. Click **+ New Resource** → **Service** → **Dockerfile** +2. Name: `placebo-frontend` -### Phase 4: Security Configuration +#### Configuration -#### 4.1 Environment Variables -Store sensitive data in Coolify environment variables: -- Database passwords -- JWT secrets -- API keys -- Strapi secrets +| Setting | Value | +|---------|-------| +| Dockerfile Location | `frontend/Dockerfile` | +| Build Context | `frontend` | +| Port | `80` | -#### 4.2 SSL Certificates -Coolify automatically: -- Requests Let's Encrypt certificates -- Configures HTTPS redirect -- Auto-renews certificates +#### Build Arguments -#### 4.3 Security Headers -Configure in Coolify: -- HSTS enabled -- CSP headers -- X-Frame-Options -- X-Content-Type-Options +Vite requires environment variables at build time. Add these as **Build Args**: -### Phase 5: Monitoring & Backup - -#### 5.1 Monitoring -- **Coolify built-in monitoring**: Resource usage, uptime -- **Application logs**: Access via Coolify UI -- **Health checks**: Configure endpoints - -#### 5.2 Backup Strategy -1. **Database backups**: Daily via Coolify to S3-compatible storage -2. **Media files**: Strapi uploads to cloud storage -3. **Configuration**: Export Coolify settings regularly - -#### 5.3 Backup Configuration -```yaml -# Coolify backup settings -backup: - schedule: "0 2 * * *" # Daily at 2 AM - retention: 30 # Keep 30 days - destination: s3://backup-bucket/placebo-mk +```env +VITE_API_URL=https://api.placebo.mk/api/v1 +VITE_CMS_URL=https://cms.placebo.mk ``` -## Docker Setup +#### Domain -### Docker Files Created -``` -placeboMk/ -├── docker-compose.yml # Production setup with PostgreSQL -├── docker-compose.dev.yml # Development setup with hot reload -├── backend/ -│ ├── Dockerfile # Production Dockerfile -│ └── Dockerfile.dev # Development Dockerfile -├── frontend/ -│ ├── Dockerfile # Production Dockerfile (Nginx) -│ ├── Dockerfile.dev # Development Dockerfile -│ └── nginx.conf # Nginx configuration -└── cms/cms/ - ├── Dockerfile # Production Dockerfile - └── Dockerfile.dev # Development Dockerfile +1. Add: `placebo.mk` +2. Add: `www.placebo.mk` +3. Enable **HTTPS** + +--- + +### 3. PWA (Progressive Web App) + +#### Create Service + +1. Click **+ New Resource** → **Service** → **Dockerfile** +2. Name: `placebo-pwa` + +#### Configuration + +| Setting | Value | +|---------|-------| +| Dockerfile Location | `pwa/Dockerfile` | +| Build Context | `pwa` | +| Port | `80` | + +#### Build Arguments + +```env +VITE_API_URL=https://api.placebo.mk/api/v1 +VITE_CMS_URL=https://cms.placebo.mk ``` -### Local Development with Docker +#### Persistent Volume (Optional) -#### Prerequisites -- Docker Desktop -- Docker Compose -- Node.js 20+ (optional, for local development without Docker) +For service worker and cache: +- Container Path: `/usr/share/nginx/html` +- Host Path: `pwa-dist` -#### Quick Start - Development Mode +#### Domain + +1. Add: `app.placebo.mk` +2. Enable **HTTPS** + +--- + +### 4. CMS (Strapi) + +#### Create Service + +1. Click **+ New Resource** → **Service** → **Dockerfile** +2. Name: `placebo-cms` + +#### Configuration + +| Setting | Value | +|---------|-------| +| Dockerfile Location | `cms/cms/Dockerfile` | +| Build Context | `cms/cms` | +| Port | `1337` | + +#### Environment Variables + +```env +NODE_ENV=production +HOST=0.0.0.0 +PORT=1337 + +# Database +DATABASE_CLIENT=postgres +DATABASE_HOST=placebo-db +DATABASE_PORT=5432 +DATABASE_NAME=placebo_db +DATABASE_USERNAME=placebo_user +DATABASE_PASSWORD= +DATABASE_SSL=false + +# Security Keys (generate unique values for each) +APP_KEYS= +API_TOKEN_SALT=<32-char-secret> +ADMIN_JWT_SECRET=<32-char-secret> +TRANSFER_TOKEN_SALT=<32-char-secret> +JWT_SECRET=<32-char-secret> + +# CORS +CORS_ORIGIN=https://placebo.mk,https://app.placebo.mk,https://api.placebo.mk +``` + +#### Generate Strapi Keys + +**For APP_KEYS** (4 comma-separated keys): ```bash -# Clone repository -git clone https://github.com/your-org/placeboMk.git -cd placeboMk - -# Start development environment with hot reload -docker-compose -f docker-compose.dev.yml up -d - -# Access applications: -# Frontend: http://localhost:5173 -# Backend API: http://localhost:3000 -# Strapi CMS: http://localhost:1337/admin +node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" +# Run 4 times, join with commas ``` -#### Quick Start - Production Mode (Local Testing) +**For other secrets:** ```bash -# Start production environment -docker-compose up -d - -# Access applications: -# Frontend: http://localhost:3001 -# Backend API: http://localhost:3000 -# Strapi CMS: http://localhost:1337 -# PostgreSQL: localhost:5432 +openssl rand -base64 32 ``` -#### Test Docker Setup +#### Persistent Volumes + +Add these volumes for data persistence: + +| Container Path | Purpose | +|----------------|---------| +| `/app/public/uploads` | Media uploads | +| `/app/.tmp` | Temporary files | + +#### Domain + +1. Add: `cms.placebo.mk` +2. Enable **HTTPS** + +--- + +## Environment Variables Reference + +### Backend + +| Variable | Required | Description | +|----------|----------|-------------| +| `NODE_ENV` | Yes | Set to `production` | +| `DATABASE_HOST` | Yes | PostgreSQL hostname | +| `DATABASE_PORT` | Yes | PostgreSQL port (5432) | +| `DATABASE_USERNAME` | Yes | Database username | +| `DATABASE_PASSWORD` | Yes | Database password | +| `DATABASE_NAME` | Yes | Database name | +| `DATABASE_SYNCHRONIZE` | Yes | Set to `false` in production | +| `JWT_SECRET` | Yes | 64+ character secret | +| `CORS_ORIGIN` | Yes | Comma-separated allowed origins | +| `VAPID_PUBLIC_KEY` | Yes | For push notifications | +| `VAPID_PRIVATE_KEY` | Yes | For push notifications | +| `STRAPI_URL` | Yes | CMS URL | +| `STRAPI_API_TOKEN` | Yes | API token from Strapi | + +### Frontend/PWA (Build Args) + +| Variable | Required | Description | +|----------|----------|-------------| +| `VITE_API_URL` | Yes | Backend API URL | +| `VITE_CMS_URL` | Yes | CMS URL | + +### CMS (Strapi) + +| Variable | Required | Description | +|----------|----------|-------------| +| `APP_KEYS` | Yes | 4 comma-separated 32-byte keys | +| `API_TOKEN_SALT` | Yes | Random 32-byte string | +| `ADMIN_JWT_SECRET` | Yes | Random 32-byte string | +| `JWT_SECRET` | Yes | Random 32-byte string | +| `TRANSFER_TOKEN_SALT` | Yes | Random 32-byte string | + +--- + +## Domain & SSL Configuration + +### Configure Each Service + +For each deployed service: + +1. Go to service → **Configuration** → **Domains** +2. Click **Add Domain** +3. Enter domain (e.g., `api.placebo.mk`) +4. Enable **HTTPS** +5. Select **Let's Encrypt** +6. Click **Request Certificate** + +### SSL Certificate Generation + +Coolify automatically handles SSL via Let's Encrypt. Ensure: +- DNS is properly configured +- Port 80 and 443 are open +- Domain is accessible from internet + +--- + +## Push Notifications Setup + +### 1. Generate VAPID Keys + +Run locally in the backend directory: + ```bash -# Run comprehensive test -./scripts/test-docker.sh - -# Or manually test -docker-compose build -docker-compose up -d -docker-compose ps +cd backend +npm install +npm run generate-vapid ``` -### Development Commands -```bash -# Development environment -docker-compose -f docker-compose.dev.yml up -d # Start dev -docker-compose -f docker-compose.dev.yml down # Stop dev -docker-compose -f docker-compose.dev.yml logs -f # View dev logs - -# Production environment (local testing) -docker-compose up -d # Start prod -docker-compose down # Stop prod -docker-compose logs -f # View prod logs -docker-compose build # Rebuild images - -# Database operations -docker-compose exec postgres psql -U placebo_user -d placebo_db # PostgreSQL shell -docker-compose exec backend npm run migration:run # Run migrations - -# Service management -docker-compose restart backend # Restart backend -docker-compose restart frontend # Restart frontend -docker-compose restart cms # Restart CMS +Output: +``` +Generating VAPID keys... +VAPID keys generated and added to .env file: + Public Key: BIAna-ulT88Ek5ZdiAiXJikks3T5JEuZjBstuO7bEbUJ6RAoSmXAbum1Pf7JIbo-YQfwXM1Yi-rGTttOuNJUpUE + Private Key: DGtRu68SmAxng-xx3BiTCa9NrztQbsSoECRuzry9VD4 ``` -## Production Checklist +### 2. Configure Backend -### Pre-Deployment -- [ ] Domain DNS propagated (48 hours) -- [ ] SSL certificates issued -- [ ] Database migrated and tested -- [ ] Environment variables configured -- [ ] Backup system tested -- [ ] Monitoring configured +Add to backend environment variables: +```env +VAPID_SUBJECT=mailto:contact@placebo.mk +VAPID_PUBLIC_KEY= +VAPID_PRIVATE_KEY= +``` -### Deployment Day -1. **Morning**: Final testing of staging environment -2. **Afternoon**: Deploy to production (low traffic time) -3. **Evening**: Monitor performance and fix issues +### 3. Verify Setup -### Post-Deployment -- [ ] Verify all services are running -- [ ] Test critical user flows -- [ ] Check SSL certificates -- [ ] Verify backups are working -- [ ] Monitor error rates +1. Deploy backend with VAPID keys +2. Visit: `https://api.placebo.mk/api/v1/push/vapid-public-key` +3. Should return JSON with your public key + +### 4. Test in PWA + +1. Open `https://app.placebo.mk` +2. Wait for notification banner +3. Click "Вклучи" (Enable) +4. Accept browser permission +5. Verify subscription in admin dashboard + +--- + +## Post-Deployment Checklist + +### Backend + +- [ ] Health check passing: `https://api.placebo.mk/health` +- [ ] API docs accessible: `https://api.placebo.mk/api/v1` +- [ ] Database connected (check logs) +- [ ] JWT authentication working +- [ ] CORS accepting frontend origins +- [ ] Push notification endpoint working + +### Frontend + +- [ ] Site loads: `https://placebo.mk` +- [ ] No console errors +- [ ] API calls successful (check Network tab) +- [ ] Images and assets loading +- [ ] Navigation working correctly +- [ ] Admin dashboard accessible + +### PWA + +- [ ] Site loads: `https://app.placebo.mk` +- [ ] Service worker registered (DevTools → Application) +- [ ] Install prompt appears +- [ ] Notification banner shows +- [ ] Push subscription successful +- [ ] Works offline (cached resources) + +### CMS + +- [ ] Admin panel: `https://cms.placebo.mk/admin` +- [ ] First admin user created +- [ ] API token generated for backend +- [ ] Media uploads working +- [ ] Content types created + +--- ## Troubleshooting -### Common Issues +### Backend Won't Start -#### 1. Database Connection Failed -```bash -# Check PostgreSQL logs -docker-compose logs postgres +**Symptoms:** Container keeps restarting -# Test connection -docker-compose exec postgres psql -U placebo -d placebomk -``` +**Solutions:** +1. Check logs: Coolify → Service → Logs +2. Verify database connection: + - `DATABASE_HOST` should be service name (e.g., `placebo-db`) + - Check password matches database password +3. Verify all required env vars are set +4. Check JWT_SECRET is at least 32 characters -#### 2. Build Failures -- Check Node.js version compatibility -- Verify package.json dependencies -- Check build logs in Coolify +### Frontend Shows Blank Page -#### 3. SSL Certificate Issues -- Verify DNS records -- Check Traefik logs: `docker-compose logs traefik` -- Manually renew: `docker-compose exec traefik traefik cert` +**Symptoms:** Page loads but nothing displays -#### 4. CORS Errors -- Verify CORS_ORIGIN environment variable -- Check backend CORS configuration -- Test API calls from frontend +**Solutions:** +1. Check browser console for errors +2. Verify build args were set: + - `VITE_API_URL` must be set at build time +3. Check Network tab for failed API calls +4. Rebuild with correct environment variables -### Debug Commands -```bash -# Check container status -docker-compose ps +### CORS Errors -# View application logs -docker-compose logs --tail=100 backend +**Symptoms:** Browser blocks API requests -# Shell into container -docker-compose exec backend sh +**Solutions:** +1. Add all frontend domains to `CORS_ORIGIN`: + ```env + CORS_ORIGIN=https://placebo.mk,https://www.placebo.mk,https://app.placebo.mk + ``` +2. Restart backend after changing +3. Clear browser cache -# Check network connectivity -docker-compose exec backend curl http://postgres:5432 -``` +### Push Notifications Not Working -## Performance Optimization +**Symptoms:** Subscription fails or notifications don't arrive -### Frontend -- Enable Vite build optimization -- Configure CDN for static assets -- Implement code splitting -- Optimize images +**Solutions:** +1. Verify HTTPS is enabled (required for push) +2. Check VAPID keys are set in backend +3. Test endpoint: `/api/v1/push/vapid-public-key` +4. Check service worker is registered +5. Verify browser notification permission is granted -### Backend -- Implement Redis caching -- Database query optimization -- Enable compression -- Configure connection pooling +### Database Connection Issues -### Database -- Add appropriate indexes -- Regular vacuum and analyze -- Connection pool tuning -- Read replicas for high traffic +**Symptoms:** Backend can't connect to database -## Scaling Strategy +**Solutions:** +1. Verify database service is running +2. Check hostname matches database service name +3. Verify credentials are correct +4. Ensure both services are on the same network +5. Check database logs for errors -### Vertical Scaling (First Step) -- Upgrade VPS: 8GB RAM, 4 vCPU -- Increase PostgreSQL memory -- Add Redis cache +### CMS Admin Not Accessible -### Horizontal Scaling -- Add application replicas -- Load balancing with Traefik -- Database read replicas -- CDN for static assets +**Symptoms:** 404 or redirect loop on `/admin` -### Cost Optimization -- Right-size VPS resources -- Use object storage for media -- Implement caching to reduce database load -- Monitor and optimize queries +**Solutions:** +1. Verify all Strapi secrets are set +2. Check `HOST=0.0.0.0` is set +3. Clear browser cache and cookies +4. Check CMS logs for startup errors -## Maintenance Schedule +--- -### Daily -- Check application logs -- Verify backup completion -- Monitor resource usage -- Review error rates +## Maintenance -### Weekly -- Update dependencies (security patches) -- Review access logs -- Test restore from backup -- Clean up old logs +### Updating Deployments -### Monthly -- Security audit -- Performance review -- Cost optimization review -- Update deployment documentation +1. Push changes to GitHub main branch +2. Coolify → Service → **Redeploy** +3. Or enable automatic deployments on push -## Emergency Procedures +### Database Backups -### Database Corruption -1. Stop affected services -2. Restore from latest backup -3. Verify data integrity -4. Restart services +1. Coolify → Database → **Backups** +2. Enable scheduled backups +3. Recommended retention: 7-30 days +4. Store backups off-server periodically -### Application Crash -1. Check logs for root cause -2. Rollback to previous version -3. Fix issue in development -4. Deploy fix +### Log Management -### DDoS Attack -1. Enable rate limiting -2. Block malicious IPs -3. Scale up resources temporarily -4. Contact hosting provider +- **View logs:** Coolify → Service → Logs +- **Download logs:** Available in service settings +- **Log rotation:** Configured automatically -## Support Contacts +### Monitoring -### Technical Support -- **Coolify Documentation**: https://coolify.io/docs -- **Docker Support**: https://docs.docker.com -- **PostgreSQL Docs**: https://www.postgresql.org/docs +1. Set up Coolify notifications for: + - Service down + - High resource usage + - Deployment failures +2. Monitor disk usage (uploads, logs) +3. Check SSL certificate expiration -### Macedonian Hosting Providers -- **Hetzner**: German-based, good for Macedonian audience -- **DigitalOcean**: Amsterdam datacenter -- **Local Providers**: Check for Macedonian hosting companies +--- -## Cost Estimates +## Security Checklist -| Resource | Monthly Cost | Annual Cost | -|----------|-------------|-------------| -| VPS (4GB/2CPU) | €12 | €144 | -| Domain (placebo.mk) | €15/year | €15 | -| Backup Storage (100GB) | €5 | €60 | -| **Total** | **€17/month** | **€219/year** | +- [ ] All secrets are 32+ characters +- [ ] HTTPS enabled on all services +- [ ] Database not exposed to internet +- [ ] CORS restricted to your domains only +- [ ] CMS admin protected (consider IP whitelist) +- [ ] Regular security updates applied +- [ ] Backups configured and tested +- [ ] Monitoring and alerts enabled -## Success Metrics +--- -### Technical Metrics -- Uptime: >99.9% -- Page load time: <2 seconds -- API response time: <200ms -- Error rate: <0.1% +## Support -### Business Metrics -- Monthly visitors -- Article publication rate -- User engagement -- Revenue (if applicable) - -## Next Steps - -1. **Set up development environment** with Docker -2. **Test migration** from SQLite to PostgreSQL -3. **Deploy to staging** environment -4. **Perform load testing** -5. **Go live** with production deployment - -## Changelog - -### v1.0.0 - Initial Deployment -- Dockerized application stack -- PostgreSQL migration -- Coolify deployment configuration -- Basic monitoring and backup - -### Future Improvements -- Redis caching implementation -- CDN integration -- Advanced monitoring (Prometheus/Grafana) -- Automated testing pipeline -- Macedonian language optimization \ No newline at end of file +For issues specific to: +- **Coolify**: [Coolify Documentation](https://coolify.io/docs) +- **NestJS**: [NestJS Documentation](https://docs.nestjs.com) +- **Strapi**: [Strapi Documentation](https://docs.strapi.io) +- **Vite/PWA**: [Vite PWA Plugin](https://vite-pwa-org.netlify.app) diff --git a/backend/src/app.controller.ts b/backend/src/app.controller.ts index cce879e..ec70588 100644 --- a/backend/src/app.controller.ts +++ b/backend/src/app.controller.ts @@ -1,5 +1,6 @@ import { Controller, Get } from '@nestjs/common'; import { AppService } from './app.service'; +import { Public } from './modules/auth/public.decorator'; @Controller() export class AppController { @@ -9,4 +10,13 @@ export class AppController { getHello(): string { return this.appService.getHello(); } + + @Public() + @Get('health') + healthCheck(): { status: string; timestamp: string } { + return { + status: 'ok', + timestamp: new Date().toISOString(), + }; + } } diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..b30590f --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,155 @@ +# Production Docker Compose for Coolify +# Each service should be deployed separately in Coolify +# This file is for reference and local testing only + +version: '3.8' + +services: + # PostgreSQL Database + postgres: + image: postgres:16-alpine + container_name: placebo-postgres + restart: unless-stopped + environment: + POSTGRES_DB: placebo_db + POSTGRES_USER: placebo_user + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-placebo_password} + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U placebo_user -d placebo_db'] + interval: 10s + timeout: 5s + retries: 5 + networks: + - placebo-network + + # Backend API (NestJS) + backend: + build: + context: ./backend + dockerfile: Dockerfile + container_name: placebo-backend + restart: unless-stopped + environment: + NODE_ENV: production + DATABASE_TYPE: postgres + DATABASE_HOST: postgres + DATABASE_PORT: 5432 + DATABASE_USERNAME: placebo_user + DATABASE_PASSWORD: ${POSTGRES_PASSWORD:-placebo_password} + DATABASE_NAME: placebo_db + DATABASE_SYNCHRONIZE: 'false' + DATABASE_LOGGING: 'false' + JWT_SECRET: ${JWT_SECRET:-change-me-in-production} + JWT_EXPIRATION: '86400' + CORS_ORIGIN: ${CORS_ORIGIN:-http://localhost:3001} + VAPID_SUBJECT: ${VAPID_SUBJECT:-mailto:contact@placebo.mk} + VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY} + VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY} + ports: + - '3000:3000' + depends_on: + postgres: + condition: service_healthy + healthcheck: + test: ['CMD', 'node', '-e', "require('http').get('http://localhost:3000/health', (r) => {if(r.statusCode !== 200) process.exit(1)})"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + networks: + - placebo-network + + # Frontend (React) + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + args: + VITE_API_URL: ${VITE_API_URL:-http://localhost:3000/api/v1} + VITE_CMS_URL: ${VITE_CMS_URL:-http://localhost:1337} + container_name: placebo-frontend + restart: unless-stopped + ports: + - '3001:80' + depends_on: + - backend + healthcheck: + test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:80/'] + interval: 30s + timeout: 10s + retries: 3 + networks: + - placebo-network + + # PWA (Progressive Web App) + pwa: + build: + context: ./pwa + dockerfile: Dockerfile + args: + VITE_API_URL: ${VITE_API_URL:-http://localhost:3000/api/v1} + VITE_CMS_URL: ${VITE_CMS_URL:-http://localhost:1337} + container_name: placebo-pwa + restart: unless-stopped + ports: + - '5174:80' + depends_on: + - backend + healthcheck: + test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:80/'] + interval: 30s + timeout: 10s + retries: 3 + networks: + - placebo-network + + # CMS (Strapi) + cms: + build: + context: ./cms/cms + dockerfile: Dockerfile + container_name: placebo-cms + restart: unless-stopped + environment: + NODE_ENV: production + DATABASE_CLIENT: postgres + DATABASE_HOST: postgres + DATABASE_PORT: 5432 + DATABASE_NAME: placebo_cms_db + DATABASE_USERNAME: placebo_user + DATABASE_PASSWORD: ${POSTGRES_PASSWORD:-placebo_password} + DATABASE_SSL: 'false' + HOST: 0.0.0.0 + PORT: 1337 + APP_KEYS: ${STRAPI_APP_KEYS:-key1,key2,key3,key4} + API_TOKEN_SALT: ${STRAPI_API_TOKEN_SALT:-change-me} + ADMIN_JWT_SECRET: ${STRAPI_ADMIN_JWT_SECRET:-change-me} + TRANSFER_TOKEN_SALT: ${STRAPI_TRANSFER_TOKEN_SALT:-change-me} + JWT_SECRET: ${STRAPI_JWT_SECRET:-change-me} + ports: + - '1337:1337' + depends_on: + postgres: + condition: service_healthy + volumes: + - cms_uploads:/app/public/uploads + healthcheck: + test: ['CMD', 'node', '-e', "require('http').get('http://localhost:1337/_health', (r) => {if(r.statusCode !== 200) process.exit(1)})"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + networks: + - placebo-network + +volumes: + postgres_data: + driver: local + cms_uploads: + driver: local + +networks: + placebo-network: + driver: bridge diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 06d81b9..9edffcb 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -5,6 +5,14 @@ FROM node:20-alpine AS builder WORKDIR /app +# Build arguments for environment variables +ARG VITE_API_URL +ARG VITE_CMS_URL + +# Set environment variables for build +ENV VITE_API_URL=$VITE_API_URL +ENV VITE_CMS_URL=$VITE_CMS_URL + # Copy package files COPY package*.json ./ COPY package-lock.json* ./ @@ -42,4 +50,4 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ EXPOSE 80 # Start nginx -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file +CMD ["nginx", "-g", "daemon off;"] diff --git a/pwa/Dockerfile b/pwa/Dockerfile index 06d81b9..3289378 100644 --- a/pwa/Dockerfile +++ b/pwa/Dockerfile @@ -1,10 +1,18 @@ -# Frontend Dockerfile for Placebo.mk TanStack React App +# PWA Dockerfile for Placebo.mk Progressive Web App # Build stage FROM node:20-alpine AS builder WORKDIR /app +# Build arguments for environment variables +ARG VITE_API_URL +ARG VITE_CMS_URL + +# Set environment variables for build +ENV VITE_API_URL=$VITE_API_URL +ENV VITE_CMS_URL=$VITE_CMS_URL + # Copy package files COPY package*.json ./ COPY package-lock.json* ./ @@ -42,4 +50,4 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ EXPOSE 80 # Start nginx -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file +CMD ["nginx", "-g", "daemon off;"]