diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 7b44764..359c7ae 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -1,150 +1,322 @@ # LittleShop Deployment Guide -## Portainer Deployment to portainer-01 (10.0.0.51) +This guide covers deploying LittleShop and TeleBot using Docker and Docker Compose. -This guide covers deploying LittleShop to your Portainer infrastructure with Traefik routing. +## Quick Deploy (Recommended) -### Prerequisites +The easiest way to deploy is using Docker Compose, which handles networking and configuration automatically: -1. **Portainer** running on `portainer-01 (10.0.0.51)` - - Username: `sysadmin` - - Password: `Phenom12#.` - -2. **Traefik** running on `portainer-03` with: - - External network named `traefik` - - Let's Encrypt SSL certificate resolver named `letsencrypt` - - Entry point named `websecure` (port 443) - -3. **DNS Configuration** - - `littleshop.silverlabs.uk` should point to your Traefik instance - -### Deployment Steps - -#### Step 1: Access Portainer -1. Navigate to `http://10.0.0.51:9000` (or your Portainer URL) -2. Login with `sysadmin` / `Phenom12#.` - -#### Step 2: Create Environment Variables -1. Go to **Stacks** → **Add stack** -2. Name: `littleshop` -3. In the environment variables section, add: - ``` - JWT_SECRET_KEY=YourSuperSecretKeyThatIsAtLeast32CharactersLong! - BTCPAY_SERVER_URL=https://your-btcpay-server.com - BTCPAY_STORE_ID=your-store-id - BTCPAY_API_KEY=your-api-key - BTCPAY_WEBHOOK_SECRET=your-webhook-secret - ``` - -#### Step 3: Deploy the Stack -1. Copy the contents of `docker-compose.yml` into the web editor -2. Click **Deploy the stack** - -#### Step 4: Verify Deployment -1. Check that the container is running in **Containers** view -2. Visit `https://littleshop.silverlabs.uk` to confirm the application is accessible - -### Configuration Details - -#### Traefik Labels Configuration -The docker-compose includes these Traefik labels: -- **Host Rule**: `littleshop.silverlabs.uk` -- **HTTPS**: Enabled with Let's Encrypt -- **Port**: Internal port 8080 -- **Headers**: Proper forwarding headers for ASP.NET Core - -#### Persistent Storage -Three volumes are created: -- `littleshop_data`: SQLite database and application data -- `littleshop_uploads`: Product images and file uploads -- `littleshop_logs`: Application log files - -#### Security Configuration -- Application runs on internal port 8080 -- HTTPS enforced through Traefik -- JWT secrets configurable via environment variables -- Forwarded headers properly configured for reverse proxy - -### Environment Variables - -| Variable | Description | Required | Default | -|----------|-------------|----------|---------| -| `JWT_SECRET_KEY` | Secret key for JWT token signing | Yes | Default provided | -| `BTCPAY_SERVER_URL` | BTCPay Server URL | No | Empty | -| `BTCPAY_STORE_ID` | BTCPay Store ID | No | Empty | -| `BTCPAY_API_KEY` | BTCPay API Key | No | Empty | -| `BTCPAY_WEBHOOK_SECRET` | BTCPay Webhook Secret | No | Empty | - -### Initial Setup - -#### Default Admin Account -On first run, the application creates a default admin account: -- **Username**: `admin` -- **Password**: `admin` -- **⚠️ IMPORTANT**: Change this password immediately after deployment! - -#### Post-Deployment Steps -1. Visit `https://littleshop.silverlabs.uk/Admin` -2. Login with `admin` / `admin` -3. Change the admin password -4. Configure categories and products -5. Set up BTCPay Server integration if needed - -### Troubleshooting - -#### Container Won't Start -- Check environment variables are set correctly -- Verify Traefik network exists: `docker network ls` -- Check container logs in Portainer - -#### SSL Certificate Issues -- Ensure DNS points to Traefik instance -- Check Traefik logs for Let's Encrypt errors -- Verify `letsencrypt` resolver is configured - -#### Application Errors -- Check application logs in `/app/logs/` volume -- Verify database permissions in `/app/data/` volume -- Ensure file upload directory is writable - -#### Database Issues -- Database is automatically created on first run -- Data persists in `littleshop_data` volume -- Location: `/app/data/littleshop.db` - -### Updating the Application - -1. In Portainer, go to **Stacks** → **littleshop** -2. Click **Editor** -3. Update the image tag or configuration as needed -4. Click **Update the stack** - -### Backup and Restore - -#### Backup ```bash -# Backup volumes -docker run --rm -v littleshop_littleshop_data:/data -v $(pwd):/backup alpine tar czf /backup/littleshop-data-backup.tar.gz -C /data . -docker run --rm -v littleshop_littleshop_uploads:/data -v $(pwd):/backup alpine tar czf /backup/littleshop-uploads-backup.tar.gz -C /data . +# Set the Telegram bot token +export TELEGRAM_BOT_TOKEN="your-bot-token-here" + +# Deploy all services +docker-compose up -d + +# View logs +docker-compose logs -f ``` -#### Restore -```bash -# Restore volumes -docker run --rm -v littleshop_littleshop_data:/data -v $(pwd):/backup alpine tar xzf /backup/littleshop-data-backup.tar.gz -C /data -docker run --rm -v littleshop_littleshop_uploads:/data -v $(pwd):/backup alpine tar xzf /backup/littleshop-uploads-backup.tar.gz -C /data +This automatically: +- Creates the `littleshop-network` for inter-service communication +- Configures correct ports (5100:5000) +- Sets up health checks and dependencies +- Connects to external `silverpay-network` + +## Network Architecture + +``` +┌─────────────────────────────────────────────┐ +│ littleshop-network (bridge) │ +│ │ +│ ┌──────────────┐ ┌─────────────────┐ │ +│ │ littleshop │◄─────┤ telebot-service │ │ +│ │ :5000 │ │ │ │ +│ └──────────────┘ └─────────────────┘ │ +│ ▲ │ │ +└────────┼────────────────────────┼───────────┘ + │ │ + Port 5100 │ + (Host Access) │ + │ + ┌─────────────▼───────────┐ + │ silverpay-network │ + │ (external) │ + └─────────────────────────┘ ``` -### Support +## Manual Deployment (Alternative) -For issues or questions: -1. Check application logs in Portainer -2. Verify Traefik configuration -3. Ensure all environment variables are set correctly -4. Check network connectivity between containers +If you need to deploy manually without Docker Compose: ---- +### 1. Build Images -**Deployment Status**: ✅ Ready for Production -**Hostname**: `https://littleshop.silverlabs.uk` -**Admin Panel**: `https://littleshop.silverlabs.uk/Admin` \ No newline at end of file +```bash +# Build LittleShop +cd /mnt/c/Production/Source/LittleShop +docker build -t littleshop:latest . + +# Build TeleBot (if not already built) +cd /mnt/c/Production/Source/TeleBot +docker build -t telebot:latest . +``` + +### 2. Create Network + +```bash +docker network create littleshop-network +``` + +### 3. Deploy LittleShop + +```bash +docker run -d \ + --name littleshop \ + --network littleshop-network \ + -p 5100:5000 \ + -e ASPNETCORE_ENVIRONMENT=Production \ + -e ASPNETCORE_URLS=http://+:5000 \ + -e "ConnectionStrings__DefaultConnection=Data Source=/app/data/littleshop-prod.db" \ + -e "Jwt__Key=LittleShop-Production-JWT-SecretKey-32Characters-2025" \ + -e "Jwt__Issuer=LittleShop" \ + -e "Jwt__Audience=LittleShop" \ + -v littleshop-data:/app/data \ + -v littleshop-uploads:/app/wwwroot/uploads \ + -v littleshop-logs:/app/logs \ + --restart unless-stopped \ + littleshop:latest +``` + +### 4. Deploy TeleBot + +```bash +docker run -d \ + --name telebot-service \ + --network littleshop-network \ + -e ASPNETCORE_ENVIRONMENT=Production \ + -e LittleShop__ApiUrl=http://littleshop:5000 \ + -e LittleShop__UseTor=false \ + -e "Telegram__BotToken=YOUR_BOT_TOKEN_HERE" \ + --restart unless-stopped \ + telebot:latest + +# Connect to SilverPay network +docker network connect silverpay-network telebot-service +``` + +## Common Issues and Solutions + +### Issue: "Name or service not known" + +**Symptom**: TeleBot logs show `System.Net.Sockets.SocketException: Name or service not known` + +**Cause**: Containers are on different networks + +**Solution**: +```bash +# Verify networks +docker inspect littleshop | grep NetworkMode +docker inspect telebot-service | grep NetworkMode + +# Connect to correct network +docker network connect littleshop-network littleshop +docker network connect littleshop-network telebot-service +``` + +### Issue: "Connection refused" on port 5000 + +**Symptom**: TeleBot logs show `Connection refused (littleshop:5000)` + +**Cause**: LittleShop listening on wrong port (usually 8080) + +**Solution**: Ensure `ASPNETCORE_URLS=http://+:5000` is set in environment variables + +### Issue: Sample data appears in production + +**Symptom**: Products/categories pre-populated when expecting empty database + +**Cause**: `ASPNETCORE_ENVIRONMENT` not set to `Production` + +**Solution**: +```bash +# Verify environment +docker exec littleshop env | grep ASPNETCORE_ENVIRONMENT + +# Should output: ASPNETCORE_ENVIRONMENT=Production +``` + +## Database Management + +### Fresh Database (Empty State) + +To start with a completely empty database (admin user only): + +```bash +# Stop containers +docker-compose down + +# Remove database volume +docker volume rm littleshop_littleshop-data + +# Restart +docker-compose up -d +``` + +### Database Backup + +```bash +# Backup database +docker run --rm -v littleshop_littleshop-data:/data -v $(pwd):/backup alpine \ + sh -c "cp /data/littleshop-prod.db /backup/littleshop-backup-$(date +%Y%m%d-%H%M%S).db" +``` + +### Database Restore + +```bash +# Restore database +docker run --rm -v littleshop_littleshop-data:/data -v $(pwd):/backup alpine \ + sh -c "cp /backup/littleshop-backup-YYYYMMDD-HHMMSS.db /data/littleshop-prod.db" + +# Restart container +docker-compose restart littleshop +``` + +## Environment Configuration + +### Required Environment Variables + +**LittleShop:** +- `ASPNETCORE_ENVIRONMENT=Production` - Disables sample data seeding +- `ASPNETCORE_URLS=http://+:5000` - Forces correct internal port +- `ConnectionStrings__DefaultConnection` - Database path +- `Jwt__Key` - JWT signing key (32+ characters) +- `Jwt__Issuer` - JWT issuer claim +- `Jwt__Audience` - JWT audience claim + +**TeleBot:** +- `ASPNETCORE_ENVIRONMENT=Production` - Production settings +- `LittleShop__ApiUrl=http://littleshop:5000` - LittleShop connection +- `LittleShop__UseTor=false` - Direct connection (no Tor) +- `Telegram__BotToken` - Bot authentication token + +### Setting Bot Token + +```bash +# Option 1: Export environment variable (recommended) +export TELEGRAM_BOT_TOKEN="1234567890:ABCdefGHIjklMNOpqrsTUVwxyz" +docker-compose up -d + +# Option 2: .env file (create in project root) +echo "TELEGRAM_BOT_TOKEN=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz" > .env +docker-compose up -d +``` + +## Access Points + +- **Admin Panel**: http://localhost:5100/Admin +- **API**: http://localhost:5100/api +- **Swagger Docs**: http://localhost:5100/swagger +- **Health Check**: http://localhost:5100/api/version + +## Monitoring + +### View Logs + +```bash +# All services +docker-compose logs -f + +# Specific service +docker-compose logs -f littleshop +docker-compose logs -f telebot + +# Last 100 lines +docker-compose logs --tail=100 telebot +``` + +### Health Status + +```bash +# Check container health +docker ps + +# LittleShop health endpoint +curl http://localhost:5100/api/version + +# Verify TeleBot connection +docker logs telebot-service | grep "Bot started successfully" +``` + +### Network Connectivity Test + +```bash +# Test from TeleBot container +docker exec telebot-service curl http://littleshop:5000/api/version + +# Should return: {"version":"1.0.0","environment":"Production"} +``` + +## Updating Services + +### Update LittleShop + +```bash +# Pull latest code +git pull origin main + +# Rebuild and restart +docker-compose up -d --build littleshop +``` + +### Update TeleBot + +```bash +# Pull latest code from TeleBot repo +cd /mnt/c/Production/Source/TeleBot +git pull origin main + +# Rebuild image +docker build -t telebot:latest . + +# Restart service +docker-compose restart telebot +``` + +## Production Checklist + +Before deploying to production: + +- [ ] Set `ASPNETCORE_ENVIRONMENT=Production` +- [ ] Configure unique JWT secret key (32+ characters) +- [ ] Set valid Telegram bot token +- [ ] Verify silverpay-network exists (`docker network ls`) +- [ ] Test database backup/restore process +- [ ] Verify health checks are passing +- [ ] Test TeleBot can connect to LittleShop API +- [ ] Confirm empty database (if required) +- [ ] Document any custom configuration + +## Troubleshooting Commands + +```bash +# List all networks +docker network ls + +# Inspect network +docker network inspect littleshop_littleshop-network + +# Check which networks a container is on +docker inspect littleshop --format='{{json .NetworkSettings.Networks}}' + +# Test DNS resolution +docker exec telebot-service nslookup littleshop + +# Check listening ports +docker exec littleshop netstat -tlnp + +# View environment variables +docker exec littleshop env + +# Force recreate containers +docker-compose up -d --force-recreate +``` diff --git a/docker-compose.yml b/docker-compose.yml index ff7f30a..1e9f6a0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,37 +3,53 @@ version: '3.8' services: littleshop: build: . - image: localhost:5000/littleshop:latest + image: littleshop:latest container_name: littleshop restart: unless-stopped ports: - - "127.0.0.1:5100:5000" # Bind only to localhost + - "5100:5000" # Host:Container environment: - - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_ENVIRONMENT=Production - ASPNETCORE_URLS=http://+:5000 - - ConnectionStrings__DefaultConnection=Data Source=/app/data/littleshop-production.db - - Jwt__Key=${JWT_SECRET_KEY} - - Jwt__Issuer=LittleShop-Production - - Jwt__Audience=LittleShop-Production - - Jwt__ExpiryInHours=24 - - SilverPay__BaseUrl=${SILVERPAY_URL} - - SilverPay__ApiKey=${SILVERPAY_API_KEY} - - SilverPay__WebhookSecret=${SILVERPAY_WEBHOOK_SECRET} - - SilverPay__DefaultWebhookUrl=${SILVERPAY_WEBHOOK_URL} - - SilverPay__AllowUnsignedWebhooks=false - - WebPush__VapidPublicKey=${WEBPUSH_VAPID_PUBLIC_KEY} - - WebPush__VapidPrivateKey=${WEBPUSH_VAPID_PRIVATE_KEY} - - WebPush__VapidSubject=${WEBPUSH_SUBJECT} - - TeleBot__ApiUrl=${TELEBOT_API_URL} - - TeleBot__ApiKey=${TELEBOT_API_KEY} + - ConnectionStrings__DefaultConnection=Data Source=/app/data/littleshop-prod.db + - Jwt__Key=LittleShop-Production-JWT-SecretKey-32Characters-2025 + - Jwt__Issuer=LittleShop + - Jwt__Audience=LittleShop volumes: - - littleshop_data:/app/data - - littleshop_uploads:/app/wwwroot/uploads - - littleshop_logs:/app/logs + - littleshop-data:/app/data + - littleshop-uploads:/app/wwwroot/uploads + - littleshop-logs:/app/logs networks: - littleshop-network healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:5000/api/catalog/products"] + test: ["CMD", "curl", "-f", "http://localhost:5000/api/version"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + telebot: + image: telebot:latest + container_name: telebot-service + restart: unless-stopped + environment: + - ASPNETCORE_ENVIRONMENT=Production + - LittleShop__ApiUrl=http://littleshop:5000 + - LittleShop__UseTor=false + - Telegram__BotToken=${TELEGRAM_BOT_TOKEN} + networks: + - littleshop-network + - silverpay-network + depends_on: + littleshop: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "ps aux | grep dotnet || exit 1"] interval: 30s timeout: 10s retries: 3 @@ -45,16 +61,15 @@ services: max-file: "3" volumes: - littleshop_data: + littleshop-data: driver: local - littleshop_uploads: + littleshop-uploads: driver: local - littleshop_logs: + littleshop-logs: driver: local networks: littleshop-network: driver: bridge - ipam: - config: - - subnet: 172.23.0.0/16 \ No newline at end of file + silverpay-network: + external: true \ No newline at end of file