Generated by Spark: Should handle caprover / cloudflare cors - Check frontend and backend config. example setup: https://frontend.example.com https://backend.example.com

This commit is contained in:
2026-01-17 18:47:08 +00:00
committed by GitHub
parent ca93235cde
commit a469b44a54
17 changed files with 2165 additions and 200 deletions

View File

@@ -1,4 +1,16 @@
# Flask Backend Configuration (Optional)
# If set, the app will automatically use Flask backend instead of IndexedDB
# Example: VITE_FLASK_BACKEND_URL=http://localhost:5000
# Frontend Configuration
# Flask Backend URL - If set, the app will automatically use Flask backend instead of IndexedDB
# Development: VITE_FLASK_BACKEND_URL=http://localhost:5000
# Production: VITE_FLASK_BACKEND_URL=https://backend.example.com
VITE_FLASK_BACKEND_URL=
# Backend Configuration (for backend/app.py)
# CORS Allowed Origins - Comma-separated list of allowed frontend URLs
# Development: CORS_ALLOWED_ORIGINS=*
# Production: CORS_ALLOWED_ORIGINS=https://frontend.example.com
# Multiple: CORS_ALLOWED_ORIGINS=https://frontend.example.com,https://app.example.com
CORS_ALLOWED_ORIGINS=*
# Database Path - Location of SQLite database file in backend
# Default: DATABASE_PATH=/app/data/snippets.db
DATABASE_PATH=/app/data/snippets.db

View File

@@ -148,7 +148,9 @@ Or configure manually in Settings with the remote URL.
| Variable | Description | Default | Example |
|----------|-------------|---------|---------|
| `VITE_FLASK_BACKEND_URL` | Flask backend URL. When set, forces Flask backend usage. | (none) | `http://localhost:5000` |
| `VITE_FLASK_BACKEND_URL` | Flask backend URL. When set, forces Flask backend usage. | (none) | `http://localhost:5000` or `https://backend.example.com` |
| `CORS_ALLOWED_ORIGINS` | Comma-separated list of allowed frontend origins for CORS. | `*` | `https://frontend.example.com` |
| `DATABASE_PATH` | Path to SQLite database file in backend. | `/app/data/snippets.db` | `/app/data/snippets.db` |
## Troubleshooting
@@ -164,7 +166,8 @@ Or configure manually in Settings with the remote URL.
1. Verify backend is running: `curl http://localhost:5000/health`
2. Check URL spelling and port number
3. Review backend logs for errors
4. Ensure CORS is enabled in Flask app
4. Ensure CORS is enabled in Flask app (see CORS-GUIDE.md)
5. For production deployments, see DEPLOYMENT.md
### Environment variable not working
@@ -258,3 +261,6 @@ VITE_FLASK_BACKEND_URL=https://api.your-domain.com
- [Backend API Documentation](./backend/README.md#api-endpoints)
- [Docker Compose Configuration](./docker-compose.yml)
- [Example .env file](./.env.example)
- [CORS Configuration Guide](./CORS-GUIDE.md)
- [Production Deployment Guide](./DEPLOYMENT.md)
- [Deployment Checklist](./DEPLOYMENT-CHECKLIST.md)

383
CORS-CONFIG-SUMMARY.md Normal file
View File

@@ -0,0 +1,383 @@
# CapRover/Cloudflare CORS Configuration Summary
## ✅ What's Been Configured
### Backend CORS Implementation
The Flask backend (`backend/app.py`) now includes comprehensive CORS support:
1. **Environment-based CORS configuration:**
- `CORS_ALLOWED_ORIGINS` environment variable
- Supports wildcard (`*`) for development
- Supports comma-separated list for multiple origins in production
2. **Proper CORS headers:**
- `Access-Control-Allow-Origin`
- `Access-Control-Allow-Methods`
- `Access-Control-Allow-Headers`
- `Access-Control-Allow-Credentials`
3. **Security features:**
- Specific origins in production
- Wildcard only in development
- Credentials support for specific origins
### Frontend Configuration
The frontend automatically detects and uses the backend via:
1. **Environment variable:** `VITE_FLASK_BACKEND_URL`
2. **Automatic configuration:** When set, forces Flask backend usage
3. **Manual configuration:** Settings page (if env var not set)
### Docker Configuration
1. **Backend Dockerfile:**
- Environment variables support
- Persistent volume at `/app/data`
- Health check endpoint
2. **Frontend Dockerfile:**
- Build-time argument for backend URL
- Nginx with proxy support
- Static file serving
3. **Nginx Configuration:**
- Proper proxy headers
- Cache control for SPA
- API proxying
### CapRover Support
1. **captain-definition files:**
- Frontend: Root directory
- Backend: Backend directory
2. **Deployment ready:**
- Separate app deployments
- Environment variable configuration
- Persistent storage support
## 📚 Documentation Created
### Primary Guides
1. **[DEPLOYMENT.md](./DEPLOYMENT.md)**
- Complete CapRover/Cloudflare deployment walkthrough
- Step-by-step instructions
- DNS configuration
- SSL setup
- Testing procedures
- Troubleshooting
2. **[CORS-GUIDE.md](./CORS-GUIDE.md)**
- CORS concepts and configuration
- Testing procedures
- Common errors and solutions
- Automated testing script
- Security best practices
- Debugging tips
3. **[DEPLOYMENT-CHECKLIST.md](./DEPLOYMENT-CHECKLIST.md)**
- Quick reference checklist
- All deployment steps
- Testing verification
- Security checks
- Quick commands
4. **[ENV-CONFIG.md](./ENV-CONFIG.md)**
- Environment variable examples
- Different deployment scenarios
- Common mistakes
- Troubleshooting
### Updated Documentation
1. **[BACKEND-CONFIG.md](./BACKEND-CONFIG.md)**
- Added CORS environment variable
- Updated with deployment links
2. **[backend/README.md](./backend/README.md)**
- Added CORS configuration
- Production deployment section
- Environment variables table
3. **[README.md](./README.md)**
- Added deployment documentation links
- Organized documentation section
### Configuration Files
1. **[.env.example](./.env.example)**
- Frontend variables
- Backend variables
- Comments and examples
2. **[docker-compose.yml](./docker-compose.yml)**
- Updated with new environment variables
- Proper volume paths
3. **[docker-compose.production.yml](./docker-compose.production.yml)**
- Production configuration example
- Network configuration
4. **[nginx.conf](./nginx.conf)**
- Enhanced proxy configuration
- Security headers
- Cache control
### Testing Tools
1. **[test-cors.sh](./test-cors.sh)**
- Automated CORS testing script
- 5 comprehensive tests
- Clear pass/fail indicators
- Usage instructions
## 🚀 Quick Start Guide
### Local Development
```bash
# Backend
cd backend
pip install -r requirements.txt
CORS_ALLOWED_ORIGINS=http://localhost:3000 python app.py
# Frontend
echo "VITE_FLASK_BACKEND_URL=http://localhost:5000" > .env
npm install
npm run dev
```
### Docker Compose
```bash
# Starts both frontend and backend
docker-compose up -d
# Access at http://localhost:3000
```
### CapRover Deployment
```bash
# Deploy backend
cd backend
caprover deploy -a codesnippet-backend
# Configure in CapRover dashboard:
# - CORS_ALLOWED_ORIGINS=https://frontend.example.com
# - DATABASE_PATH=/app/data/snippets.db
# Deploy frontend
cd ..
caprover deploy -a codesnippet-frontend
# Configure in CapRover dashboard:
# - VITE_FLASK_BACKEND_URL=https://backend.example.com
```
## 🔧 Configuration Examples
### Separate Domains (Recommended)
```
Frontend: https://frontend.example.com
Backend: https://backend.example.com
```
**Frontend:**
```bash
VITE_FLASK_BACKEND_URL=https://backend.example.com
```
**Backend:**
```bash
CORS_ALLOWED_ORIGINS=https://frontend.example.com
DATABASE_PATH=/app/data/snippets.db
```
### Single Domain (Proxied)
```
Frontend: https://app.example.com
Backend: https://app.example.com/api (proxied)
```
**Frontend:**
```bash
VITE_FLASK_BACKEND_URL=/api
```
**Backend:**
```bash
CORS_ALLOWED_ORIGINS=*
DATABASE_PATH=/app/data/snippets.db
```
### Multiple Frontends
```
Frontend 1: https://app.example.com
Frontend 2: https://staging.example.com
Backend: https://api.example.com
```
**Backend:**
```bash
CORS_ALLOWED_ORIGINS=https://app.example.com,https://staging.example.com
DATABASE_PATH=/app/data/snippets.db
```
## ✅ Testing CORS
### Quick Test
```bash
curl -H "Origin: https://frontend.example.com" \
-H "Access-Control-Request-Method: GET" \
-X OPTIONS \
https://backend.example.com/api/snippets
```
Should return:
```
Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
```
### Automated Test
```bash
chmod +x test-cors.sh
./test-cors.sh https://backend.example.com https://frontend.example.com
```
## 🔒 Security Checklist
- [ ] HTTPS enabled for both frontend and backend
- [ ] `CORS_ALLOWED_ORIGINS` set to specific domains (not `*`)
- [ ] Cloudflare proxy enabled (orange cloud)
- [ ] SSL/TLS mode set to "Full (strict)"
- [ ] Flask debug mode disabled
- [ ] Database backed up regularly
- [ ] Rate limiting configured
- [ ] Monitoring enabled
## 📋 Environment Variables
### Frontend
| Variable | Required | Example |
|----------|----------|---------|
| `VITE_FLASK_BACKEND_URL` | Yes (for Flask) | `https://backend.example.com` |
### Backend
| Variable | Required | Example |
|----------|----------|---------|
| `CORS_ALLOWED_ORIGINS` | Yes | `https://frontend.example.com` |
| `DATABASE_PATH` | No | `/app/data/snippets.db` (default) |
## 🐛 Troubleshooting
### CORS Errors
**Problem:** Browser shows CORS policy error
**Solution:**
1. Check `CORS_ALLOWED_ORIGINS` includes frontend URL
2. Verify both use HTTPS (not mixed)
3. Restart backend after env changes
4. See [CORS-GUIDE.md](./CORS-GUIDE.md)
### Connection Failed
**Problem:** Frontend can't connect to backend
**Solution:**
1. Test backend: `curl https://backend.example.com/health`
2. Check `VITE_FLASK_BACKEND_URL` is correct
3. Verify backend is running
4. Check firewall/network settings
### SSL Issues
**Problem:** SSL certificate not valid
**Solution:**
1. Wait 5-10 minutes for Let's Encrypt
2. Verify DNS records are correct
3. Check Cloudflare SSL mode: "Full (strict)"
4. Disable and re-enable HTTPS in CapRover
## 📖 Documentation Index
### For Developers
- [Backend Configuration](./BACKEND-CONFIG.md) - Configure storage backends
- [Environment Configuration](./ENV-CONFIG.md) - Environment variable examples
- [Backend API](./backend/README.md) - API documentation
### For DevOps
- [Deployment Guide](./DEPLOYMENT.md) - Complete deployment walkthrough
- [CORS Guide](./CORS-GUIDE.md) - CORS configuration and testing
- [Deployment Checklist](./DEPLOYMENT-CHECKLIST.md) - Quick reference
### For Everyone
- [Quick Start](./QUICKSTART.md) - Get started quickly
- [Application Guide](./README-APP.md) - Using the application
- [Main README](./README.md) - Overview and links
## 🎯 Key Benefits
1. **Flexible Deployment:**
- Single domain or separate domains
- CapRover, Docker, or standalone
- Local development or cloud production
2. **Secure by Default:**
- CORS properly configured
- HTTPS enforced
- Specific origins in production
3. **Easy to Configure:**
- Environment variables
- Clear documentation
- Testing tools included
4. **Production Ready:**
- Cloudflare CDN support
- CapRover deployment ready
- Monitoring and logging
5. **Well Documented:**
- Step-by-step guides
- Configuration examples
- Troubleshooting help
## 🤝 Support
For issues or questions:
1. Check the relevant guide in [Documentation Index](#documentation-index)
2. Review [Troubleshooting](#troubleshooting) section
3. Run the test script: `./test-cors.sh`
4. Check application logs
## 📝 Next Steps
After deployment:
1. ✅ Test all functionality
2. ✅ Configure backups
3. ✅ Set up monitoring
4. ✅ Review security settings
5. ✅ Configure rate limiting
6. ✅ Test disaster recovery
7. ✅ Document your specific configuration
8. ✅ Share with your team
---
**Ready to deploy?** Start with the [Deployment Checklist](./DEPLOYMENT-CHECKLIST.md)!

409
CORS-GUIDE.md Normal file
View File

@@ -0,0 +1,409 @@
# CORS Configuration & Testing Guide
This guide covers Cross-Origin Resource Sharing (CORS) configuration for CodeSnippet when deployed with separate frontend and backend domains.
## Understanding CORS
CORS is a security feature that controls which domains can make requests to your backend API. When your frontend (`https://frontend.example.com`) makes requests to your backend (`https://backend.example.com`), the browser enforces CORS policies.
## Backend CORS Configuration
### Environment Variable: `CORS_ALLOWED_ORIGINS`
The backend Flask application uses the `CORS_ALLOWED_ORIGINS` environment variable to control which origins can access the API.
#### Development (Allow All Origins)
```bash
CORS_ALLOWED_ORIGINS=*
```
**Warning:** Only use `*` in development. This allows ANY website to access your backend API.
#### Production (Specific Origins)
```bash
# Single origin
CORS_ALLOWED_ORIGINS=https://frontend.example.com
# Multiple origins (comma-separated)
CORS_ALLOWED_ORIGINS=https://frontend.example.com,https://app.example.com,https://staging.example.com
```
**Important:** Do NOT include trailing slashes in URLs.
### How It Works
The Flask backend (`backend/app.py`) reads this environment variable and configures CORS accordingly:
```python
ALLOWED_ORIGINS = os.environ.get('CORS_ALLOWED_ORIGINS', '*')
if ALLOWED_ORIGINS == '*':
CORS(app, origins='*', ...) # Development mode
else:
origins_list = [origin.strip() for origin in ALLOWED_ORIGINS.split(',')]
CORS(app, origins=origins_list, ...) # Production mode
```
### CORS Headers Returned
When properly configured, the backend returns these headers:
```
Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
```
## Testing CORS Configuration
### Test 1: Health Check (No CORS)
Simple GET request to verify backend is running:
```bash
curl https://backend.example.com/health
```
Expected response:
```json
{
"status": "healthy",
"timestamp": "2024-01-01T12:00:00"
}
```
### Test 2: Preflight Request (OPTIONS)
Browsers send an OPTIONS request before the actual request to check CORS permissions:
```bash
curl -X OPTIONS https://backend.example.com/api/snippets \
-H "Origin: https://frontend.example.com" \
-H "Access-Control-Request-Method: GET" \
-H "Access-Control-Request-Headers: Content-Type" \
-v
```
Expected headers in response:
```
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: https://frontend.example.com
< Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
< Access-Control-Allow-Headers: Content-Type, Authorization
```
### Test 3: Actual API Request with Origin
Test a real API request with Origin header:
```bash
curl https://backend.example.com/api/snippets \
-H "Origin: https://frontend.example.com" \
-v
```
Should return `Access-Control-Allow-Origin` header and snippet data.
### Test 4: Wrong Origin (Should Fail)
Test that unauthorized origins are blocked:
```bash
curl https://backend.example.com/api/snippets \
-H "Origin: https://malicious-site.com" \
-v
```
Expected: No `Access-Control-Allow-Origin` header (or browser would block).
### Test 5: Browser DevTools Test
1. Open frontend in browser: `https://frontend.example.com`
2. Open DevTools (F12) → Network tab
3. Create a new snippet or load snippets
4. Check the API request:
- Should show status 200 OK
- Response headers should include `Access-Control-Allow-Origin`
- No CORS errors in Console
## Common CORS Errors & Solutions
### Error: "CORS policy: No 'Access-Control-Allow-Origin' header"
**Cause:** Backend is not returning CORS headers
**Solutions:**
1. Verify `CORS_ALLOWED_ORIGINS` is set correctly in backend
2. Check backend logs for errors
3. Ensure Flask-CORS is installed: `pip install flask-cors`
4. Restart backend after environment variable changes
### Error: "CORS policy: Origin 'https://frontend.example.com' not allowed"
**Cause:** Frontend origin not in allowed list
**Solutions:**
1. Check `CORS_ALLOWED_ORIGINS` includes exact frontend URL
2. Ensure no trailing slash: `https://frontend.example.com` not `https://frontend.example.com/`
3. Verify HTTPS vs HTTP matches exactly
4. Check for typos in domain name
### Error: "CORS policy: Request header 'content-type' not allowed"
**Cause:** Backend not allowing required headers
**Solutions:**
1. Verify backend allows `Content-Type` header
2. Check Flask-CORS configuration in `app.py`
3. Ensure `allow_headers` includes `Content-Type`
### Error: Mixed Content (HTTP/HTTPS)
**Cause:** Frontend uses HTTPS but backend uses HTTP (or vice versa)
**Solutions:**
1. Ensure both frontend and backend use HTTPS in production
2. Update `VITE_FLASK_BACKEND_URL` to use `https://`
3. Enable HTTPS in CapRover for both apps
4. Verify Cloudflare SSL/TLS mode is "Full (strict)"
### Error: "CORS policy: Credential is not supported if origin is '*'"
**Cause:** Using `CORS_ALLOWED_ORIGINS=*` with `supports_credentials=True`
**Solutions:**
1. Set specific origins instead of `*`
2. Or disable credentials if not needed
## Deployment Scenarios
### Scenario 1: Separate Domains (Recommended for Production)
```
Frontend: https://frontend.example.com
Backend: https://backend.example.com
```
**Frontend Config:**
```bash
VITE_FLASK_BACKEND_URL=https://backend.example.com
```
**Backend Config:**
```bash
CORS_ALLOWED_ORIGINS=https://frontend.example.com
```
**Pros:** Clean separation, independent scaling
**Cons:** Requires CORS configuration
### Scenario 2: Single Domain with Proxy
```
Frontend: https://app.example.com
Backend: https://app.example.com/api (proxied)
```
**Frontend Config:**
```bash
VITE_FLASK_BACKEND_URL=/api
```
**Backend Config:**
```bash
CORS_ALLOWED_ORIGINS=* # Not needed if proxied through nginx
```
**Nginx Config:** (already configured in `nginx.conf`)
```nginx
location /api {
proxy_pass http://backend:5000;
}
```
**Pros:** No CORS issues (same-origin), simpler configuration
**Cons:** Tight coupling, single domain
### Scenario 3: Multiple Frontends
```
Frontend 1: https://app.example.com
Frontend 2: https://staging.example.com
Backend: https://api.example.com
```
**Frontend Config (both):**
```bash
VITE_FLASK_BACKEND_URL=https://api.example.com
```
**Backend Config:**
```bash
CORS_ALLOWED_ORIGINS=https://app.example.com,https://staging.example.com
```
## Cloudflare-Specific Configuration
### Cloudflare SSL/TLS Mode
Set to **"Full (strict)"** to ensure end-to-end encryption:
1. Cloudflare Dashboard → SSL/TLS → Overview
2. Select "Full (strict)"
3. Ensures both Cloudflare-to-origin and client-to-Cloudflare use SSL
### Cloudflare Always Use HTTPS
1. SSL/TLS → Edge Certificates
2. Enable "Always Use HTTPS"
3. Automatically redirects HTTP to HTTPS
### Cloudflare Transform Rules (Optional)
Add security headers using Transform Rules:
```
Header: Strict-Transport-Security
Value: max-age=31536000; includeSubDomains
Header: X-Content-Type-Options
Value: nosniff
Header: X-Frame-Options
Value: DENY
```
## Automated CORS Testing Script
Save this as `test-cors.sh`:
```bash
#!/bin/bash
FRONTEND_URL="https://frontend.example.com"
BACKEND_URL="https://backend.example.com"
echo "Testing CORS Configuration..."
echo "================================"
echo -e "\n1. Testing Health Endpoint..."
curl -s "$BACKEND_URL/health" | jq .
echo -e "\n2. Testing OPTIONS Preflight..."
curl -X OPTIONS "$BACKEND_URL/api/snippets" \
-H "Origin: $FRONTEND_URL" \
-H "Access-Control-Request-Method: GET" \
-i -s | grep -i "access-control"
echo -e "\n3. Testing GET with Origin..."
curl -s "$BACKEND_URL/api/snippets" \
-H "Origin: $FRONTEND_URL" \
-i | grep -i "access-control"
echo -e "\n4. Testing POST with Origin..."
curl -X POST "$BACKEND_URL/api/snippets" \
-H "Origin: $FRONTEND_URL" \
-H "Content-Type: application/json" \
-d '{"id":"test","title":"Test","code":"test","language":"JavaScript","createdAt":"2024-01-01T00:00:00","updatedAt":"2024-01-01T00:00:00"}' \
-i -s | grep -i "access-control"
echo -e "\nCORS tests complete!"
```
Make it executable and run:
```bash
chmod +x test-cors.sh
./test-cors.sh
```
## Frontend Storage Config Helper
The frontend automatically handles backend configuration through the `getStorageConfig()` function:
### Automatic Configuration
If `VITE_FLASK_BACKEND_URL` is set, the app automatically uses Flask backend:
```typescript
// src/lib/storage.ts
function getDefaultConfig(): StorageConfig {
const flaskUrl = import.meta.env.VITE_FLASK_BACKEND_URL
if (flaskUrl) {
return { backend: 'flask', flaskUrl: flaskUrl }
}
return { backend: 'indexeddb' }
}
```
### Manual Configuration
Users can also manually configure in Settings page (if no env var is set).
## Debugging Tips
### Enable Verbose Logging
Add logging to backend `app.py`:
```python
import logging
logging.basicConfig(level=logging.DEBUG)
@app.after_request
def after_request(response):
app.logger.debug(f"Response headers: {response.headers}")
return response
```
### Browser DevTools
1. Open DevTools (F12)
2. Network tab → Enable "Preserve log"
3. Filter by "Fetch/XHR"
4. Look for OPTIONS and GET/POST requests
5. Check Response Headers for `Access-Control-Allow-Origin`
6. Check Console for CORS error messages
### CapRover Logs
View real-time backend logs:
```bash
# Via CapRover CLI
caprover logs codesnippet-backend --lines 100 --follow
# Via Dashboard
Apps → codesnippet-backend → Logs
```
## Security Best Practices
### Production CORS Checklist
- [ ] Set specific origins (not `*`)
- [ ] Use HTTPS for all URLs
- [ ] Enable Cloudflare proxy (orange cloud)
- [ ] Set Cloudflare SSL mode to "Full (strict)"
- [ ] Remove trailing slashes from origin URLs
- [ ] Test with automated script
- [ ] Monitor for CORS errors in production logs
- [ ] Document allowed origins
### Regular Audits
Periodically review:
1. Which origins are allowed
2. Whether all origins are still needed
3. CORS-related errors in logs
4. Unauthorized access attempts
## Additional Resources
- [MDN CORS Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
- [Flask-CORS Documentation](https://flask-cors.readthedocs.io/)
- [Cloudflare SSL/TLS Documentation](https://developers.cloudflare.com/ssl/)
- [CapRover Environment Variables](https://caprover.com/docs/app-configuration.html)

190
DEPLOYMENT-CHECKLIST.md Normal file
View File

@@ -0,0 +1,190 @@
# Deployment Checklist
Quick reference checklist for deploying CodeSnippet with separate frontend and backend domains.
## Pre-Deployment
- [ ] CapRover server is running and accessible
- [ ] Domain name configured in DNS provider (Cloudflare)
- [ ] CapRover CLI installed: `npm install -g caprover`
- [ ] Logged into CapRover: `caprover login`
## DNS Configuration (Cloudflare)
- [ ] A record for `frontend.example.com` → CapRover IP (Proxied ☁️)
- [ ] A record for `backend.example.com` → CapRover IP (Proxied ☁️)
- [ ] DNS records propagated (check with `dig` or `nslookup`)
## Backend Deployment
- [ ] Create app in CapRover: `codesnippet-backend`
- [ ] Enable "Has Persistent Data" with path: `/app/data`
- [ ] Set environment variable: `CORS_ALLOWED_ORIGINS=https://frontend.example.com`
- [ ] Set environment variable: `DATABASE_PATH=/app/data/snippets.db`
- [ ] Deploy code: `cd backend && caprover deploy -a codesnippet-backend`
- [ ] Enable HTTPS in CapRover
- [ ] Connect custom domain: `backend.example.com`
- [ ] Force HTTPS redirect enabled
- [ ] SSL certificate issued successfully
- [ ] Test health endpoint: `curl https://backend.example.com/health`
## Frontend Deployment
- [ ] Create app in CapRover: `codesnippet-frontend`
- [ ] Set environment variable: `VITE_FLASK_BACKEND_URL=https://backend.example.com`
- [ ] Deploy code: `caprover deploy -a codesnippet-frontend` (from project root)
- [ ] Enable HTTPS in CapRover
- [ ] Connect custom domain: `frontend.example.com`
- [ ] Force HTTPS redirect enabled
- [ ] SSL certificate issued successfully
- [ ] Test frontend loads: Visit `https://frontend.example.com`
## Cloudflare Configuration
- [ ] SSL/TLS mode set to "Full (strict)"
- [ ] "Always Use HTTPS" enabled
- [ ] "Automatic HTTPS Rewrites" enabled
- [ ] "Auto Minify" enabled (JS, CSS, HTML)
- [ ] "Brotli" compression enabled
## Testing
- [ ] Backend health check responds: `curl https://backend.example.com/health`
- [ ] CORS preflight test passes (see CORS-GUIDE.md)
- [ ] Frontend loads without errors
- [ ] Backend indicator shows "Backend" status (not "Local")
- [ ] Can create new snippet
- [ ] Can view existing snippet
- [ ] Can edit snippet
- [ ] Can delete snippet
- [ ] No CORS errors in browser console (F12)
- [ ] Mobile responsive layout works
## Post-Deployment
- [ ] Database backup strategy configured
- [ ] Monitoring enabled (CapRover metrics)
- [ ] Rate limiting configured (Cloudflare)
- [ ] Error logging reviewed
- [ ] Documentation updated with actual URLs
## Security Verification
- [ ] Both domains use HTTPS only
- [ ] `CORS_ALLOWED_ORIGINS` set to specific domain (not `*`)
- [ ] Flask debug mode disabled (`debug=False`)
- [ ] No sensitive data in environment variables
- [ ] CapRover firewall rules configured
- [ ] Cloudflare security features enabled
## Quick Commands
### Deploy Backend
```bash
cd backend
caprover deploy -a codesnippet-backend
```
### Deploy Frontend
```bash
caprover deploy -a codesnippet-frontend
```
### Check Backend Logs
```bash
caprover logs codesnippet-backend --lines 100 --follow
```
### Check Frontend Logs
```bash
caprover logs codesnippet-frontend --lines 100 --follow
```
### Test CORS
```bash
curl -X OPTIONS https://backend.example.com/api/snippets \
-H "Origin: https://frontend.example.com" \
-H "Access-Control-Request-Method: GET" \
-v
```
### Backup Database
```bash
docker cp captain--codesnippet-backend:/app/data/snippets.db ./backup-$(date +%Y%m%d).db
```
## Troubleshooting Quick Fixes
### Frontend can't connect to backend
1. Check `VITE_FLASK_BACKEND_URL` in frontend environment variables
2. Verify backend is running: `curl https://backend.example.com/health`
3. Check CORS configuration in backend
### CORS errors in browser
1. Verify `CORS_ALLOWED_ORIGINS` includes frontend URL exactly
2. Ensure both use HTTPS (not mixed HTTP/HTTPS)
3. Restart backend app after environment changes
### SSL certificate issues
1. Wait 5-10 minutes for Let's Encrypt
2. Verify DNS records point to CapRover
3. Disable and re-enable HTTPS in CapRover
### Data lost after restart
1. Verify "Has Persistent Data" enabled in backend app
2. Check persistent directory path: `/app/data`
3. Verify volume is mounted correctly
## Environment Variables Quick Reference
### Backend (codesnippet-backend)
```
CORS_ALLOWED_ORIGINS=https://frontend.example.com
DATABASE_PATH=/app/data/snippets.db
```
### Frontend (codesnippet-frontend)
```
VITE_FLASK_BACKEND_URL=https://backend.example.com
```
## Alternative: Single Domain Deployment
For single domain setup (`https://app.example.com`):
### Backend
- [ ] Deploy backend (internal only, no custom domain)
- [ ] Set `CORS_ALLOWED_ORIGINS=*` (not needed if proxied)
### Frontend
- [ ] Set `VITE_FLASK_BACKEND_URL=/api`
- [ ] nginx proxies `/api` to backend (already configured)
- [ ] Deploy with custom domain: `app.example.com`
Benefits: No CORS issues, simpler DNS
Drawbacks: Tightly coupled services
## Rollback Plan
If deployment fails:
1. **Frontend issues:** Redeploy previous version
2. **Backend issues:** Check logs, fix errors, redeploy
3. **Database corruption:** Restore from backup
4. **DNS issues:** Verify Cloudflare settings
5. **SSL issues:** Disable HTTPS temporarily, debug, re-enable
## Maintenance Schedule
- **Daily:** Check error logs
- **Weekly:** Review metrics and performance
- **Monthly:** Update dependencies, security patches
- **Quarterly:** Review and rotate secrets/keys
## Support Resources
- [Full Deployment Guide](./DEPLOYMENT.md)
- [CORS Configuration Guide](./CORS-GUIDE.md)
- [Backend Configuration](./BACKEND-CONFIG.md)
- [CapRover Documentation](https://caprover.com/docs/)
- [Cloudflare Documentation](https://developers.cloudflare.com/)

380
DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,380 @@
# CapRover / Cloudflare Deployment Guide
This guide explains how to deploy CodeSnippet with separate frontend and backend domains using CapRover and Cloudflare.
## Architecture
```
┌─────────────────────────────────────────────┐
│ Cloudflare DNS & Proxy │
│ https://frontend.example.com │
│ https://backend.example.com │
└─────────────┬───────────────────────────────┘
│ HTTPS (Cloudflare SSL)
┌─────────────▼───────────────────────────────┐
│ CapRover Server │
│ │
│ ┌──────────────┐ ┌─────────────────┐ │
│ │ Frontend │ │ Backend │ │
│ │ (Nginx) │ │ (Flask) │ │
│ │ Port 3000 │ │ Port 5000 │ │
│ └──────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────┘
```
## Prerequisites
1. CapRover server installed and configured
2. Domain name configured in Cloudflare
3. DNS records pointing to your CapRover server
4. CapRover CLI installed: `npm install -g caprover`
## Step 1: Configure DNS in Cloudflare
Add these DNS records in Cloudflare:
| Type | Name | Content | Proxy Status |
|------|----------|----------------------|--------------|
| A | frontend | YOUR_CAPROVER_IP | Proxied |
| A | backend | YOUR_CAPROVER_IP | Proxied |
**Important:** Enable "Proxied" (orange cloud) to use Cloudflare's CDN and SSL.
## Step 2: Deploy Backend to CapRover
### Create Backend App
1. Login to CapRover dashboard
2. Go to "Apps" → "One-Click Apps/Databases"
3. Create a new app named `codesnippet-backend`
4. Enable "Has Persistent Data" and set persistent directory to `/app/data`
### Configure Backend Environment Variables
In the backend app settings, add these environment variables:
```bash
CORS_ALLOWED_ORIGINS=https://frontend.example.com
DATABASE_PATH=/app/data/snippets.db
```
### Deploy Backend Code
From the `backend` directory:
```bash
cd backend
caprover deploy -a codesnippet-backend
```
### Enable HTTPS for Backend
1. In CapRover dashboard → Apps → codesnippet-backend
2. Go to "HTTP Settings"
3. Enable "HTTPS"
4. Connect domain: `backend.example.com`
5. Enable "Force HTTPS by redirecting all HTTP traffic to HTTPS"
6. Wait for SSL certificate to be issued
## Step 3: Deploy Frontend to CapRover
### Create Frontend App
1. In CapRover dashboard, create new app: `codesnippet-frontend`
2. No persistent data needed for frontend
### Configure Frontend Environment Variables
In the frontend app settings, add:
```bash
VITE_FLASK_BACKEND_URL=https://backend.example.com
```
### Deploy Frontend Code
From the project root:
```bash
caprover deploy -a codesnippet-frontend
```
### Enable HTTPS for Frontend
1. In CapRover dashboard → Apps → codesnippet-frontend
2. Go to "HTTP Settings"
3. Enable "HTTPS"
4. Connect domain: `frontend.example.com`
5. Enable "Force HTTPS by redirecting all HTTP traffic to HTTPS"
6. Wait for SSL certificate to be issued
## Step 4: Configure Cloudflare Settings
### SSL/TLS Settings
1. Go to Cloudflare dashboard → SSL/TLS
2. Set encryption mode to "Full (strict)"
3. Enable "Always Use HTTPS"
4. Enable "Automatic HTTPS Rewrites"
### Security Settings
1. Go to Security → WAF
2. Consider enabling Bot Fight Mode for backend
3. Set up rate limiting rules if needed
### Speed Settings
1. Enable "Auto Minify" for JavaScript, CSS, HTML
2. Enable "Brotli" compression
3. Set Browser Cache TTL appropriately
## Step 5: Verify Deployment
### Test Backend
```bash
curl https://backend.example.com/health
```
Expected response:
```json
{
"status": "healthy",
"timestamp": "2024-01-01T12:00:00.000000"
}
```
### Test CORS
```bash
curl -H "Origin: https://frontend.example.com" \
-H "Access-Control-Request-Method: GET" \
-X OPTIONS \
https://backend.example.com/api/snippets
```
Should return CORS headers:
```
Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
```
### Test Frontend
Visit `https://frontend.example.com` and verify:
- Frontend loads correctly
- Backend indicator shows "Backend" status
- Can create, edit, and delete snippets
- No CORS errors in browser console
## Environment Variables Reference
### Backend Environment Variables
| Variable | Description | Example |
|-----------------------|------------------------------------------------|----------------------------------|
| `CORS_ALLOWED_ORIGINS`| Comma-separated list of allowed frontend URLs | `https://frontend.example.com` |
| `DATABASE_PATH` | Path to SQLite database file | `/app/data/snippets.db` |
**Note:** Use `*` for `CORS_ALLOWED_ORIGINS` only in development. In production, always specify exact origins.
### Frontend Environment Variables
| Variable | Description | Example |
|---------------------------|------------------------------|--------------------------------|
| `VITE_FLASK_BACKEND_URL` | Backend API URL | `https://backend.example.com` |
## Alternative: Single Domain Setup
If you prefer a single domain (e.g., `https://app.example.com`), you can deploy frontend with nginx proxying to backend:
### Deploy Both Services
1. Deploy backend as before (internal only, no custom domain)
2. Frontend proxies `/api` requests to backend via nginx
### Frontend Configuration
```bash
# Frontend environment variables
VITE_FLASK_BACKEND_URL=/api
```
### Nginx Configuration
The included `nginx.conf` already handles this:
```nginx
location /api {
proxy_pass http://backend:5000;
# ... proxy headers
}
```
### Benefits
- Simplified DNS (single domain)
- No CORS issues (same-origin requests)
- Easier SSL management
### Drawbacks
- Frontend and backend tightly coupled
- Can't independently scale services
- Single point of failure
## Troubleshooting
### CORS Errors
**Problem:** Browser console shows CORS errors
**Solutions:**
1. Verify `CORS_ALLOWED_ORIGINS` in backend matches frontend URL exactly
2. Ensure both domains use HTTPS (not mixed HTTP/HTTPS)
3. Check Cloudflare proxy status is enabled for both domains
4. Clear browser cache and hard refresh
### Backend Connection Failed
**Problem:** Frontend shows "Connection failed" error
**Solutions:**
1. Verify backend is running: `curl https://backend.example.com/health`
2. Check CapRover logs for backend app
3. Verify `VITE_FLASK_BACKEND_URL` in frontend matches backend URL
4. Test from command line: `curl -v https://backend.example.com/api/snippets`
### SSL Certificate Issues
**Problem:** SSL certificate not issued or invalid
**Solutions:**
1. Wait 5-10 minutes for Let's Encrypt to issue certificate
2. Verify DNS records are correct and propagated
3. Check CapRover can reach Let's Encrypt (port 80 open)
4. Try disabling and re-enabling HTTPS in CapRover
### Data Persistence Issues
**Problem:** Backend loses data after restart
**Solutions:**
1. Verify "Has Persistent Data" is enabled in CapRover
2. Check persistent directory path is `/app/data`
3. Verify `DATABASE_PATH` environment variable is correct
4. Check CapRover volume is properly mounted
### Multiple Origins
**Problem:** Need to allow multiple frontend domains
**Solution:** Set comma-separated origins:
```bash
CORS_ALLOWED_ORIGINS=https://frontend.example.com,https://app.example.com,https://staging.example.com
```
## Security Best Practices
### Production Checklist
- [ ] HTTPS enabled for both frontend and backend
- [ ] `CORS_ALLOWED_ORIGINS` set to specific domains (not `*`)
- [ ] Cloudflare proxy enabled (orange cloud)
- [ ] Rate limiting configured in Cloudflare
- [ ] Backend database backed up regularly
- [ ] Environment variables stored securely (not in code)
- [ ] Debug mode disabled in Flask (`debug=False`)
- [ ] Cloudflare WAF rules configured
- [ ] HTTPS-only cookies enabled
- [ ] Security headers configured in nginx
### Recommended Cloudflare Rules
1. **Rate Limiting:** Limit API requests to 100 per minute per IP
2. **Bot Protection:** Challenge or block known bad bots
3. **Geographic Restrictions:** Block countries you don't serve (optional)
4. **DDoS Protection:** Enable automatic DDoS mitigation
## Monitoring
### CapRover Monitoring
1. Enable app metrics in CapRover dashboard
2. Monitor CPU and memory usage
3. Set up alerts for app crashes
4. Review logs regularly for errors
### Cloudflare Analytics
1. Monitor traffic patterns
2. Check for unusual spikes or attacks
3. Review security events
4. Analyze performance metrics
## Backup Strategy
### Automated Backups
Set up a cron job in CapRover to backup database:
```bash
# In backend app settings, add a schedule:
0 2 * * * tar -czf /app/data/backup-$(date +%Y%m%d).tar.gz /app/data/snippets.db
```
### Manual Backup
1. SSH into CapRover server
2. Copy database file:
```bash
docker cp captain--codesnippet-backend:/app/data/snippets.db ./backup.db
```
### Restore from Backup
1. Copy backup to container:
```bash
docker cp ./backup.db captain--codesnippet-backend:/app/data/snippets.db
```
2. Restart backend app in CapRover
## Scaling Considerations
### Vertical Scaling
Increase resources in CapRover:
1. Go to app settings → "Resources"
2. Increase CPU and memory limits
3. Restart app
### Horizontal Scaling
For high traffic:
1. Deploy multiple backend instances in CapRover
2. Use CapRover's load balancing
3. Consider shared database (PostgreSQL instead of SQLite)
4. Use Redis for session management
## Cost Optimization
### Cloudflare
- Free plan includes SSL, CDN, and basic DDoS protection
- Pro plan ($20/mo) adds WAF and additional performance features
### CapRover
- Single VPS can run both frontend and backend
- Recommended: 2 CPU / 4GB RAM minimum
- Estimated cost: $10-20/month (DigitalOcean, Linode, Vultr)
## Support
For issues specific to:
- **CapRover:** https://caprover.com/docs/
- **Cloudflare:** https://support.cloudflare.com/
- **CodeSnippet:** Check the main README.md and BACKEND-CONFIG.md

329
ENV-CONFIG.md Normal file
View File

@@ -0,0 +1,329 @@
# Environment Configuration Examples
This directory contains example environment configurations for different deployment scenarios.
## Quick Reference
Copy the appropriate example to `.env` in the project root:
```bash
cp .env.example .env
# Edit .env with your values
```
## Scenarios
### 1. Local Development (Default)
**Use case:** Developing frontend only with local browser storage
```bash
# No backend URL - uses IndexedDB
VITE_FLASK_BACKEND_URL=
```
### 2. Local Development with Backend
**Use case:** Developing with local Flask backend
```bash
# Frontend (.env)
VITE_FLASK_BACKEND_URL=http://localhost:5000
# Backend (environment or .env)
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
DATABASE_PATH=/app/data/snippets.db
```
### 3. Docker Compose (Full Stack)
**Use case:** Running both frontend and backend with Docker
```bash
# Frontend automatically connects to backend via nginx proxy
# No .env needed - configured in docker-compose.yml
```
**docker-compose.yml already includes:**
```yaml
environment:
- VITE_FLASK_BACKEND_URL=http://localhost:5000
- CORS_ALLOWED_ORIGINS=http://localhost:3000
- DATABASE_PATH=/app/data/snippets.db
```
### 4. Production - Separate Domains (CapRover)
**Use case:** Frontend and backend on different domains
```bash
# Frontend environment variables (in CapRover)
VITE_FLASK_BACKEND_URL=https://backend.example.com
# Backend environment variables (in CapRover)
CORS_ALLOWED_ORIGINS=https://frontend.example.com
DATABASE_PATH=/app/data/snippets.db
```
### 5. Production - Multiple Frontend Domains
**Use case:** Multiple frontend deployments (prod, staging, dev)
```bash
# Backend environment variables
CORS_ALLOWED_ORIGINS=https://app.example.com,https://staging.example.com,https://dev.example.com
DATABASE_PATH=/app/data/snippets.db
# Frontend 1 (Production)
VITE_FLASK_BACKEND_URL=https://api.example.com
# Frontend 2 (Staging)
VITE_FLASK_BACKEND_URL=https://api.example.com
# Frontend 3 (Development)
VITE_FLASK_BACKEND_URL=https://api.example.com
```
### 6. Production - Single Domain (Proxied)
**Use case:** Frontend and backend on same domain, nginx proxy
```bash
# Frontend environment variables
VITE_FLASK_BACKEND_URL=/api
# Backend environment variables
CORS_ALLOWED_ORIGINS=*
# Note: CORS not needed since nginx proxies requests (same-origin)
DATABASE_PATH=/app/data/snippets.db
```
**nginx.conf includes:**
```nginx
location /api {
proxy_pass http://backend:5000;
}
```
## Environment Variables Reference
### Frontend Variables
| Variable | Description | Example |
|----------|-------------|---------|
| `VITE_FLASK_BACKEND_URL` | Backend API URL. When set, forces Flask backend usage. | `https://backend.example.com` |
**Notes:**
- Must start with `VITE_` to be exposed to frontend
- If not set, app uses IndexedDB (local storage)
- Can be relative (`/api`) if using nginx proxy
- Requires rebuild for production: `npm run build`
### Backend Variables
| Variable | Description | Example |
|----------|-------------|---------|
| `CORS_ALLOWED_ORIGINS` | Comma-separated list of allowed origins | `https://frontend.example.com` |
| `DATABASE_PATH` | Path to SQLite database file | `/app/data/snippets.db` |
**Notes:**
- Use `*` only in development
- In production, always specify exact origins
- No trailing slashes on URLs
- Must match frontend URL exactly (including https://)
## Setting Environment Variables
### Local Development (.env file)
```bash
# Create .env file
cat > .env << EOF
VITE_FLASK_BACKEND_URL=http://localhost:5000
EOF
# Start dev server
npm run dev
```
### Docker Build
```bash
# Build with environment variable
docker build --build-arg VITE_FLASK_BACKEND_URL=https://backend.example.com -t frontend .
```
### Docker Compose
```yaml
# docker-compose.yml
services:
frontend:
environment:
- VITE_FLASK_BACKEND_URL=https://backend.example.com
```
### CapRover
1. Go to CapRover dashboard
2. Select your app
3. Click "App Configs" tab
4. Add environment variables in "Environment Variables" section
5. Redeploy app
### Kubernetes
```yaml
# deployment.yaml
env:
- name: VITE_FLASK_BACKEND_URL
value: "https://backend.example.com"
- name: CORS_ALLOWED_ORIGINS
value: "https://frontend.example.com"
```
## Testing Configuration
### Test Frontend Backend Connection
```bash
# Check if environment variable is set
echo $VITE_FLASK_BACKEND_URL
# Check in browser console
console.log(import.meta.env.VITE_FLASK_BACKEND_URL)
```
### Test Backend CORS
```bash
# Quick test
curl -H "Origin: https://frontend.example.com" \
-H "Access-Control-Request-Method: GET" \
-X OPTIONS \
https://backend.example.com/api/snippets
# Full test suite
./test-cors.sh https://backend.example.com https://frontend.example.com
```
## Common Mistakes
### ❌ Wrong: Missing VITE_ Prefix
```bash
FLASK_BACKEND_URL=http://localhost:5000 # Won't work!
```
### ✅ Correct: VITE_ Prefix Required
```bash
VITE_FLASK_BACKEND_URL=http://localhost:5000
```
---
### ❌ Wrong: Trailing Slash in CORS
```bash
CORS_ALLOWED_ORIGINS=https://frontend.example.com/ # Won't work!
```
### ✅ Correct: No Trailing Slash
```bash
CORS_ALLOWED_ORIGINS=https://frontend.example.com
```
---
### ❌ Wrong: HTTP/HTTPS Mismatch
```bash
# Frontend
VITE_FLASK_BACKEND_URL=https://backend.example.com
# Backend
CORS_ALLOWED_ORIGINS=http://frontend.example.com # Wrong protocol!
```
### ✅ Correct: Matching Protocols
```bash
# Frontend
VITE_FLASK_BACKEND_URL=https://backend.example.com
# Backend
CORS_ALLOWED_ORIGINS=https://frontend.example.com
```
---
### ❌ Wrong: Using * in Production
```bash
CORS_ALLOWED_ORIGINS=* # Security risk in production!
```
### ✅ Correct: Specific Origins
```bash
CORS_ALLOWED_ORIGINS=https://frontend.example.com
```
## Troubleshooting
### Environment variable not working
1. **Frontend not rebuilding:**
- Vite requires rebuild for env vars: `npm run build`
- Dev server: Restart `npm run dev`
2. **Variable not prefixed correctly:**
- Must start with `VITE_` for frontend
- Backend vars don't need prefix
3. **Docker not picking up changes:**
- Rebuild: `docker-compose up -d --build`
- Check: `docker-compose config`
### CORS errors persist
1. **Backend not restarted:**
- Restart after env changes
- Check logs: `docker-compose logs backend`
2. **URL mismatch:**
- Frontend URL must match CORS_ALLOWED_ORIGINS exactly
- Check browser console for actual origin
3. **Cloudflare issues:**
- Verify proxy status (orange cloud)
- Check SSL/TLS mode: "Full (strict)"
## Security Best Practices
### Development
- ✅ Use `*` for CORS_ALLOWED_ORIGINS
- ✅ Use http:// for local URLs
- ✅ Keep .env out of version control
### Staging
- ✅ Use specific origins for CORS
- ✅ Use https:// for all URLs
- ✅ Test with production-like configuration
### Production
- ✅ Always use specific origins
- ✅ Always use https://
- ✅ Store secrets in secure environment
- ✅ Never commit .env files
- ✅ Rotate credentials regularly
- ✅ Monitor access logs
## Additional Resources
- [Backend Configuration Guide](./BACKEND-CONFIG.md)
- [CORS Configuration & Testing](./CORS-GUIDE.md)
- [Deployment Guide](./DEPLOYMENT.md)
- [Deployment Checklist](./DEPLOYMENT-CHECKLIST.md)

View File

@@ -74,10 +74,18 @@ When set, the app automatically connects to Flask backend and disables manual co
## 📚 Documentation
### Getting Started
- **[Quick Start Guide](./QUICKSTART.md)** - Get up and running quickly
- **[Application Guide](./README-APP.md)** - Features and usage
- **[Backend Configuration](./BACKEND-CONFIG.md)** - Detailed backend setup
### Backend & Storage
- **[Backend Configuration](./BACKEND-CONFIG.md)** - Detailed backend setup and environment variables
- **[Backend API](./backend/README.md)** - Flask API documentation
### Production Deployment
- **[Deployment Guide](./DEPLOYMENT.md)** - Complete CapRover/Cloudflare deployment walkthrough
- **[CORS Configuration](./CORS-GUIDE.md)** - CORS setup and troubleshooting
- **[Deployment Checklist](./DEPLOYMENT-CHECKLIST.md)** - Quick deployment reference
- **[Docker Examples](./docker-compose.README.md)** - Docker deployment options
## 🛠️ Technology Stack

View File

@@ -7,10 +7,12 @@ RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
RUN mkdir -p /data
RUN mkdir -p /app/data
ENV FLASK_APP=app.py
ENV DATABASE_PATH=/app/data/snippets.db
ENV CORS_ALLOWED_ORIGINS=*
EXPOSE 5000
ENV DB_PATH=/data/snippets.db
CMD ["python", "app.py"]

View File

@@ -113,10 +113,14 @@ docker build -t codesnippet-backend ./backend
```bash
# With volume for persistent data
docker run -p 5000:5000 -v $(pwd)/data:/data codesnippet-backend
docker run -p 5000:5000 -v $(pwd)/data:/app/data codesnippet-backend
# With custom database path
docker run -p 5000:5000 -e DB_PATH=/data/custom.db -v $(pwd)/data:/data codesnippet-backend
# With custom database path and CORS
docker run -p 5000:5000 \
-e DATABASE_PATH=/app/data/custom.db \
-e CORS_ALLOWED_ORIGINS=https://frontend.example.com \
-v $(pwd)/data:/app/data \
codesnippet-backend
```
### Using Docker Compose
@@ -162,7 +166,24 @@ CREATE TABLE snippets (
## Environment Variables
- `DB_PATH` - Path to SQLite database file (default: `/data/snippets.db`)
| Variable | Description | Default | Example |
|----------|-------------|---------|---------|
| `DATABASE_PATH` | Path to SQLite database file | `/app/data/snippets.db` | `/app/data/snippets.db` |
| `CORS_ALLOWED_ORIGINS` | Comma-separated list of allowed frontend origins | `*` (all origins) | `https://frontend.example.com` |
### Production Configuration
For production deployments, always set specific CORS origins:
```bash
# Single origin
export CORS_ALLOWED_ORIGINS=https://frontend.example.com
# Multiple origins
export CORS_ALLOWED_ORIGINS=https://frontend.example.com,https://app.example.com
```
**Important:** Using `*` for CORS in production is a security risk. Only use in development.
## Troubleshooting
@@ -172,9 +193,34 @@ CREATE TABLE snippets (
- Verify the port (5000) is not in use
### CORS Errors
- The backend allows all origins by default
- Modify `CORS(app)` in `app.py` if you need to restrict origins
- The backend allows all origins by default in development (`CORS_ALLOWED_ORIGINS=*`)
- For production, set specific origins: `CORS_ALLOWED_ORIGINS=https://frontend.example.com`
- See [CORS-GUIDE.md](../CORS-GUIDE.md) for detailed CORS configuration and testing
- Verify frontend URL matches exactly (including https:// and no trailing slash)
### Database Locked
- Ensure only one instance of the backend is running
- Check file permissions on the database file
## Production Deployment
For deploying to production with separate frontend and backend domains:
1. **See [DEPLOYMENT.md](../DEPLOYMENT.md)** - Complete CapRover/Cloudflare deployment guide
2. **See [CORS-GUIDE.md](../CORS-GUIDE.md)** - CORS configuration and testing
3. **See [DEPLOYMENT-CHECKLIST.md](../DEPLOYMENT-CHECKLIST.md)** - Quick deployment checklist
### Quick Production Setup
```bash
# Build and deploy backend to CapRover
cd backend
caprover deploy -a codesnippet-backend
# Set environment variables in CapRover dashboard:
# - CORS_ALLOWED_ORIGINS=https://frontend.example.com
# - DATABASE_PATH=/app/data/snippets.db
# Enable persistent storage at /app/data
# Enable HTTPS and connect custom domain
```

View File

@@ -1,41 +1,53 @@
from flask import Flask, request, jsonify
from flask_cors import CORS
from datetime
from datetime import datetime
import sqlite3
import json
import os
app = Flask(__name__)
ALLOWED_ORIGINS = os.environ.get('CORS_ALLOWED_ORIGINS', '*')
if ALLOWED_ORIGINS == '*':
CORS(app,
origins='*',
methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allow_headers=['Content-Type', 'Authorization'],
supports_credentials=False)
else:
origins_list = [origin.strip() for origin in ALLOWED_ORIGINS.split(',')]
CORS(app,
origins=origins_list,
methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allow_headers=['Content-Type', 'Authorization'],
supports_credentials=True)
DATABASE_PATH = os.environ.get('DATABASE_PATH', '/app/data/snippets.db')
os.makedirs(os.path.dirname(DATABASE_PATH), exist_ok=True)
def get_db():
conn = sqlite3.connect(DATABASE_PATH)
conn.row_factory = sqlite3.Row
return conn
def init_db():
conn = get_db()
cursor = conn.cursor()
i
cursor.execute('''
CREATE TABLE IF NOT EXISTS snippets (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
code TEXT NOT NULL,
language TEXT NOT NULL,
description TEXT,
cat
up
''')
conn.commit()
def
def get_snippets():
conn = get_db()
cursor.execute('SELECT *
conn.close()
snippets = []
snippet = dict(ro
snippe
snippet['p
except Exception as e:
tags TEXT,
category TEXT DEFAULT 'general',
componentName TEXT,
previewParams TEXT,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL
)
''')
conn.commit()
conn.close()
@@ -45,9 +57,9 @@ def health():
@app.route('/api/snippets', methods=['GET'])
def get_snippets():
try:
conn = get_db()
preview_params_json =
cursor = conn.cursor()
cursor.execute('SELECT * FROM snippets ORDER BY updatedAt DESC')
rows = cursor.fetchall()
conn.close()
@@ -66,74 +78,72 @@ def get_snippets():
return jsonify({'error': str(e)}), 500
@app.route('/api/snippets/<snippet_id>', methods=['GET'])
UPDATE snippets
data['title
data['language'],
tags_json,
data.get('component
data['up
conn.commit
return jsonify(data
return jsonify({'error'
@app.route('/api/snippets/<snippet_id>', methods=['DELETE
def get_snippet(snippet_id):
try:
conn = get_db()
cursor = conn.cursor()
cursor.execute('SELECT * FROM snippets WHERE id = ?', (snippet_id,))
row = cursor.fetchone()
conn.close()
if not row:
return jsonify({'error': 'Snippet not found'}), 404
snippet = dict(row)
if snippet.get('tags'):
snippet['tags'] = json.loads(snippet['tags'])
if snippet.get('previewParams'):
snippet['previewParams'] = json.loads(snippet['previewParams'])
return jsonify(snippet)
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/snippets', methods=['POST'])
def create_snippet():
try:
data = request.json
conn = get_db()
cursor = conn.cursor()
tags_json = json.dumps(data.get('tags', []))
preview_params_json = json.dumps(data.get('previewParams', {}))
return jsonify
return jsonify({'success': True})
if __name__ == '__main__':
app.run(host='0.0
cursor.execute('''
INSERT INTO snippets (id, title, code, language, description, tags, category, componentName, previewParams, createdAt, updatedAt)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
data['id'],
data['title'],
data['code'],
data['language'],
data.get('description', ''),
tags_json,
data.get('category', 'general'),
data.get('componentName', ''),
preview_params_json,
data['createdAt'],
data['updatedAt']
))
conn.commit()
conn.close()
return jsonify(data), 201
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/snippets/<snippet_id>', methods=['PUT'])
def update_snippet(snippet_id):
try:
data = request.json
conn = get_db()
cursor = conn.cursor()
tags_json = json.dumps(data.get('tags', []))
preview_params_json = json.dumps(data.get('previewParams', {}))
cursor.execute('''
UPDATE snippets
SET title = ?, code = ?, language = ?, description = ?, tags = ?, category = ?, componentName = ?, previewParams = ?, updatedAt = ?
WHERE id = ?
@@ -178,4 +188,4 @@ def delete_snippet(snippet_id):
if __name__ == '__main__':
init_db()
app.run(host='0.0.0.0', port=5000, debug=True)
app.run(host='0.0.0.0', port=5000, debug=False)

View File

@@ -0,0 +1,4 @@
{
"schemaVersion": 2,
"dockerfilePath": "./Dockerfile"
}

4
captain-definition Normal file
View File

@@ -0,0 +1,4 @@
{
"schemaVersion": 2,
"dockerfilePath": "./Dockerfile"
}

View File

@@ -0,0 +1,35 @@
version: '3.8'
services:
backend:
build: ./backend
ports:
- "5000:5000"
volumes:
- snippet-data:/app/data
environment:
- DATABASE_PATH=/app/data/snippets.db
- CORS_ALLOWED_ORIGINS=https://frontend.example.com,https://app.example.com
restart: unless-stopped
networks:
- app-network
frontend:
build:
context: .
args:
- VITE_FLASK_BACKEND_URL=https://backend.example.com
ports:
- "3000:3000"
depends_on:
- backend
restart: unless-stopped
networks:
- app-network
volumes:
snippet-data:
networks:
app-network:
driver: bridge

View File

@@ -6,9 +6,10 @@ services:
ports:
- "5000:5000"
volumes:
- snippet-data:/data
- snippet-data:/app/data
environment:
- DB_PATH=/data/snippets.db
- DATABASE_PATH=/app/data/snippets.db
- CORS_ALLOWED_ORIGINS=http://localhost:3000
restart: unless-stopped
frontend:
@@ -18,8 +19,6 @@ services:
- VITE_FLASK_BACKEND_URL=http://localhost:5000
ports:
- "3000:3000"
environment:
- VITE_FLASK_BACKEND_URL=http://backend:5000
depends_on:
- backend
restart: unless-stopped

View File

@@ -6,6 +6,10 @@ server {
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
location /api {
@@ -14,6 +18,12 @@ server {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_buffering off;
proxy_request_buffering off;
}
}

138
test-cors.sh Normal file
View File

@@ -0,0 +1,138 @@
#!/bin/bash
# CORS Testing Script for CodeSnippet
# Usage: ./test-cors.sh [BACKEND_URL] [FRONTEND_URL]
# Example: ./test-cors.sh https://backend.example.com https://frontend.example.com
BACKEND_URL="${1:-http://localhost:5000}"
FRONTEND_URL="${2:-http://localhost:3000}"
echo "======================================"
echo "CodeSnippet CORS Testing Script"
echo "======================================"
echo "Backend URL: $BACKEND_URL"
echo "Frontend URL: $FRONTEND_URL"
echo "======================================"
echo ""
# Test 1: Health Check
echo "🔍 Test 1: Health Check (No CORS required)"
echo "--------------------------------------"
HEALTH_RESPONSE=$(curl -s "$BACKEND_URL/health")
if [ $? -eq 0 ]; then
echo "✅ Health check successful"
echo "Response: $HEALTH_RESPONSE"
else
echo "❌ Health check failed - backend may not be running"
exit 1
fi
echo ""
# Test 2: OPTIONS Preflight
echo "🔍 Test 2: OPTIONS Preflight Request"
echo "--------------------------------------"
PREFLIGHT_HEADERS=$(curl -s -X OPTIONS "$BACKEND_URL/api/snippets" \
-H "Origin: $FRONTEND_URL" \
-H "Access-Control-Request-Method: GET" \
-H "Access-Control-Request-Headers: Content-Type" \
-i | grep -i "access-control")
if echo "$PREFLIGHT_HEADERS" | grep -q "access-control-allow-origin"; then
echo "✅ CORS preflight successful"
echo "$PREFLIGHT_HEADERS"
else
echo "❌ CORS preflight failed - missing CORS headers"
echo "Response headers:"
curl -s -X OPTIONS "$BACKEND_URL/api/snippets" \
-H "Origin: $FRONTEND_URL" \
-H "Access-Control-Request-Method: GET" \
-i | head -n 20
fi
echo ""
# Test 3: GET with Origin
echo "🔍 Test 3: GET Request with Origin"
echo "--------------------------------------"
GET_HEADERS=$(curl -s "$BACKEND_URL/api/snippets" \
-H "Origin: $FRONTEND_URL" \
-i | grep -i "access-control")
if echo "$GET_HEADERS" | grep -q "access-control-allow-origin"; then
echo "✅ GET request CORS successful"
echo "$GET_HEADERS"
else
echo "❌ GET request CORS failed - missing CORS headers"
fi
echo ""
# Test 4: POST with Origin
echo "🔍 Test 4: POST Request with Origin"
echo "--------------------------------------"
TEST_SNIPPET='{
"id": "test-cors-'$(date +%s)'",
"title": "CORS Test Snippet",
"code": "console.log(\"CORS test\");",
"language": "JavaScript",
"description": "Test snippet for CORS validation",
"tags": ["test"],
"category": "general",
"createdAt": "'$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")'",
"updatedAt": "'$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")'"
}'
POST_RESPONSE=$(curl -s -X POST "$BACKEND_URL/api/snippets" \
-H "Origin: $FRONTEND_URL" \
-H "Content-Type: application/json" \
-d "$TEST_SNIPPET" \
-i)
POST_HEADERS=$(echo "$POST_RESPONSE" | grep -i "access-control")
POST_STATUS=$(echo "$POST_RESPONSE" | head -n 1)
if echo "$POST_HEADERS" | grep -q "access-control-allow-origin"; then
echo "✅ POST request CORS successful"
echo "Status: $POST_STATUS"
echo "$POST_HEADERS"
else
echo "❌ POST request CORS failed - missing CORS headers"
echo "Status: $POST_STATUS"
fi
echo ""
# Test 5: Wrong Origin (Should fail or return no CORS headers)
echo "🔍 Test 5: Request from Unauthorized Origin"
echo "--------------------------------------"
WRONG_ORIGIN="https://malicious-site.com"
WRONG_HEADERS=$(curl -s "$BACKEND_URL/api/snippets" \
-H "Origin: $WRONG_ORIGIN" \
-i | grep -i "access-control")
if [ -z "$WRONG_HEADERS" ]; then
echo "✅ Correctly blocking unauthorized origin"
echo " (No CORS headers returned for $WRONG_ORIGIN)"
elif echo "$WRONG_HEADERS" | grep -q "access-control-allow-origin.*\*"; then
echo "⚠️ Warning: Backend allows all origins (*)"
echo " This is fine for development but should be restricted in production"
else
echo "⚠️ Unexpected CORS response for unauthorized origin"
echo "$WRONG_HEADERS"
fi
echo ""
# Summary
echo "======================================"
echo "Test Summary"
echo "======================================"
echo "Backend URL: $BACKEND_URL"
echo "Frontend URL: $FRONTEND_URL"
echo ""
echo "If all tests passed:"
echo " ✅ Your CORS configuration is working correctly"
echo ""
echo "If tests failed:"
echo " 1. Verify backend is running at $BACKEND_URL"
echo " 2. Check CORS_ALLOWED_ORIGINS environment variable"
echo " 3. Ensure it includes $FRONTEND_URL"
echo " 4. Restart backend after environment changes"
echo " 5. See CORS-GUIDE.md for detailed troubleshooting"
echo "======================================"