Compare commits
4 Commits
2ea24a98cd
...
a4262da7c9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4262da7c9 | ||
|
|
024430754c | ||
|
|
d17ed79e29 | ||
|
|
21eb63659a |
4
.dockerignore
Executable file → Normal file
@@ -52,6 +52,4 @@ PLAYER_AUTH.md
|
|||||||
PROGRESS.md
|
PROGRESS.md
|
||||||
README.md
|
README.md
|
||||||
|
|
||||||
# Config templates
|
|
||||||
player_config_template.ini
|
|
||||||
player_auth_module.py
|
|
||||||
|
|||||||
0
.gitignore
vendored
Executable file → Normal file
73
Caddyfile
@@ -1,73 +0,0 @@
|
|||||||
{
|
|
||||||
# Global options
|
|
||||||
email admin@example.com
|
|
||||||
# Admin API for configuration management (listen on all interfaces)
|
|
||||||
admin 0.0.0.0:2019
|
|
||||||
}
|
|
||||||
|
|
||||||
# Shared reverse proxy configuration
|
|
||||||
(reverse_proxy_config) {
|
|
||||||
reverse_proxy digiserver-app:5000 {
|
|
||||||
header_up Host {host}
|
|
||||||
header_up X-Real-IP {remote_host}
|
|
||||||
header_up X-Forwarded-Proto {scheme}
|
|
||||||
|
|
||||||
# Timeouts for large uploads
|
|
||||||
transport http {
|
|
||||||
read_timeout 300s
|
|
||||||
write_timeout 300s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# File upload size limit (2GB)
|
|
||||||
request_body {
|
|
||||||
max_size 2GB
|
|
||||||
}
|
|
||||||
|
|
||||||
# Security headers
|
|
||||||
header {
|
|
||||||
X-Frame-Options "SAMEORIGIN"
|
|
||||||
X-Content-Type-Options "nosniff"
|
|
||||||
X-XSS-Protection "1; mode=block"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
log {
|
|
||||||
output file /var/log/caddy/access.log
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Localhost (development/local access - HTTP only for local dev)
|
|
||||||
http://localhost {
|
|
||||||
import reverse_proxy_config
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main HTTPS entry point with multiple hostnames and IP
|
|
||||||
https://digiserver, https://10.76.152.164, https://digiserver.sibiusb.harting.intra {
|
|
||||||
import reverse_proxy_config
|
|
||||||
tls internal
|
|
||||||
}
|
|
||||||
|
|
||||||
# HTTP redirects to HTTPS for each hostname
|
|
||||||
http://digiserver {
|
|
||||||
redir https://{host}{uri}
|
|
||||||
}
|
|
||||||
|
|
||||||
http://10.76.152.164 {
|
|
||||||
redir https://{host}{uri}
|
|
||||||
}
|
|
||||||
|
|
||||||
http://digiserver.sibiusb.harting.intra {
|
|
||||||
redir https://{host}{uri}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Catch-all for any other HTTP requests
|
|
||||||
http://* {
|
|
||||||
import reverse_proxy_config
|
|
||||||
}
|
|
||||||
|
|
||||||
# Catch-all for any other HTTPS requests (fallback)
|
|
||||||
https://* {
|
|
||||||
import reverse_proxy_config
|
|
||||||
tls internal
|
|
||||||
}
|
|
||||||
0
Dockerfile
Executable file → Normal file
413
HTTPS_STATUS.txt
@@ -1,413 +0,0 @@
|
|||||||
╔═══════════════════════════════════════════════════════════════════════════════╗
|
|
||||||
║ HTTPS MANAGEMENT SYSTEM IMPLEMENTATION ║
|
|
||||||
║ ✅ COMPLETE ║
|
|
||||||
╚═══════════════════════════════════════════════════════════════════════════════╝
|
|
||||||
|
|
||||||
📦 DELIVERABLES
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
✅ CREATED FILES (9 new files)
|
|
||||||
───────────────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
1. 🗄️ DATABASE MODEL
|
|
||||||
└─ app/models/https_config.py
|
|
||||||
• HTTPSConfig database model
|
|
||||||
• Fields: hostname, domain, ip_address, port, status, audit trail
|
|
||||||
• Methods: get_config(), create_or_update(), to_dict()
|
|
||||||
• Auto timestamps for created/updated dates
|
|
||||||
|
|
||||||
2. 🛣️ ADMIN ROUTES
|
|
||||||
└─ app/blueprints/admin.py (UPDATED)
|
|
||||||
• GET /admin/https-config - Configuration page
|
|
||||||
• POST /admin/https-config/update - Update settings
|
|
||||||
• GET /admin/https-config/status - JSON status endpoint
|
|
||||||
• Full validation and error handling
|
|
||||||
• Admin-only access control
|
|
||||||
|
|
||||||
3. 🎨 ADMIN TEMPLATE
|
|
||||||
└─ app/templates/admin/https_config.html
|
|
||||||
• Beautiful, user-friendly configuration interface
|
|
||||||
• Status display section
|
|
||||||
• Configuration form with toggle switch
|
|
||||||
• Input validation feedback
|
|
||||||
• Real-time preview of access points
|
|
||||||
• Comprehensive help sections
|
|
||||||
• Responsive mobile design
|
|
||||||
|
|
||||||
4. 📊 ADMIN DASHBOARD
|
|
||||||
└─ app/templates/admin/admin.html (UPDATED)
|
|
||||||
• New card: "🔒 HTTPS Configuration"
|
|
||||||
• Links to HTTPS configuration page
|
|
||||||
• Gradient design with lock icon
|
|
||||||
|
|
||||||
5. 🔄 DATABASE MIGRATION
|
|
||||||
└─ migrations/add_https_config_table.py
|
|
||||||
• Creates https_config table
|
|
||||||
• Sets up indexes and constraints
|
|
||||||
• Audit trail fields
|
|
||||||
|
|
||||||
6. 🖥️ CLI UTILITY
|
|
||||||
└─ https_manager.py
|
|
||||||
• Command-line interface
|
|
||||||
• Commands: status, enable, disable, show
|
|
||||||
• Useful for automation and scripting
|
|
||||||
|
|
||||||
7. 🚀 SETUP SCRIPT
|
|
||||||
└─ setup_https.sh
|
|
||||||
• Automated setup script
|
|
||||||
• Runs database migration
|
|
||||||
• Displays step-by-step instructions
|
|
||||||
|
|
||||||
8. 📚 DOCUMENTATION
|
|
||||||
├─ HTTPS_CONFIGURATION.md (Comprehensive guide)
|
|
||||||
├─ HTTPS_IMPLEMENTATION_SUMMARY.md (Architecture & details)
|
|
||||||
└─ HTTPS_QUICK_REFERENCE.md (Admin quick start)
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
✅ UPDATED FILES (3 modified files)
|
|
||||||
───────────────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
1. ✏️ app/models/__init__.py
|
|
||||||
• Added HTTPSConfig import
|
|
||||||
• Exported in __all__ list
|
|
||||||
|
|
||||||
2. ✏️ app/blueprints/admin.py
|
|
||||||
• Imported HTTPSConfig model
|
|
||||||
• Added three new routes for HTTPS management
|
|
||||||
• 160+ lines of new admin functionality
|
|
||||||
|
|
||||||
3. ✏️ app/templates/admin/admin.html
|
|
||||||
• Added HTTPS Configuration card to dashboard
|
|
||||||
• Purple gradient with lock icon
|
|
||||||
|
|
||||||
4. ✏️ Caddyfile
|
|
||||||
• Updated to use domain: digiserver.sibiusb.harting.intra
|
|
||||||
• IP fallback: 10.76.152.164
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
🎯 KEY FEATURES
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
✅ WEB INTERFACE
|
|
||||||
• Enable/Disable HTTPS with toggle switch
|
|
||||||
• Configure hostname, domain, IP address, port
|
|
||||||
• Status display with current settings
|
|
||||||
• Real-time preview of access URLs
|
|
||||||
• User-friendly form with validations
|
|
||||||
• Responsive design for all devices
|
|
||||||
|
|
||||||
✅ CONFIGURATION OPTIONS
|
|
||||||
• Hostname: Short server name
|
|
||||||
• Domain: Full domain name (e.g., digiserver.sibiusb.harting.intra)
|
|
||||||
• IP Address: Server IP (e.g., 10.76.152.164)
|
|
||||||
• Port: HTTPS port (default 443)
|
|
||||||
• Enable/Disable toggle
|
|
||||||
|
|
||||||
✅ SECURITY
|
|
||||||
• Admin-only access with permission checks
|
|
||||||
• Input validation (domain, IP, port)
|
|
||||||
• Admin audit trail (who changed what, when)
|
|
||||||
• Server-side validation
|
|
||||||
• Logged in system logs
|
|
||||||
|
|
||||||
✅ VALIDATION
|
|
||||||
• Domain format validation
|
|
||||||
• IPv4 address validation (0-255 range)
|
|
||||||
• Port range validation (1-65535)
|
|
||||||
• Required field checks
|
|
||||||
• User-friendly error messages
|
|
||||||
|
|
||||||
✅ LOGGING
|
|
||||||
• All configuration changes logged
|
|
||||||
• Admin username recorded
|
|
||||||
• Timestamps for all changes
|
|
||||||
• Searchable in admin dashboard
|
|
||||||
|
|
||||||
✅ INTEGRATION
|
|
||||||
• Works with existing Caddy reverse proxy
|
|
||||||
• Automatic Let's Encrypt SSL certificates
|
|
||||||
• No manual certificate management
|
|
||||||
• Automatic certificate renewal
|
|
||||||
• HTTP/HTTPS dual access
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
🚀 QUICK START (5 Minutes)
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
1️⃣ RUN DATABASE MIGRATION
|
|
||||||
┌─ Option A: Automated
|
|
||||||
│ bash setup_https.sh
|
|
||||||
│
|
|
||||||
└─ Option B: Manual
|
|
||||||
python /app/migrations/add_https_config_table.py
|
|
||||||
|
|
||||||
2️⃣ START APPLICATION
|
|
||||||
docker-compose up -d
|
|
||||||
|
|
||||||
3️⃣ LOG IN AS ADMIN
|
|
||||||
• Navigate to admin panel
|
|
||||||
• Use admin credentials
|
|
||||||
|
|
||||||
4️⃣ CONFIGURE HTTPS
|
|
||||||
• Go to: Admin Panel → 🔒 HTTPS Configuration
|
|
||||||
• Toggle: Enable HTTPS ✅
|
|
||||||
• Fill in:
|
|
||||||
- Hostname: digiserver
|
|
||||||
- Domain: digiserver.sibiusb.harting.intra
|
|
||||||
- IP: 10.76.152.164
|
|
||||||
- Port: 443
|
|
||||||
• Click: Save HTTPS Configuration
|
|
||||||
|
|
||||||
5️⃣ VERIFY
|
|
||||||
• Check status shows "✅ HTTPS ENABLED"
|
|
||||||
• Access via: https://digiserver.sibiusb.harting.intra
|
|
||||||
• Fallback: http://10.76.152.164
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
📋 DATABASE SCHEMA
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
TABLE: https_config
|
|
||||||
┌─────────────────┬──────────────┬──────────────────────────────────────┐
|
|
||||||
│ Column │ Type │ Purpose │
|
|
||||||
├─────────────────┼──────────────┼──────────────────────────────────────┤
|
|
||||||
│ id │ INTEGER (PK) │ Primary key │
|
|
||||||
│ https_enabled │ BOOLEAN │ Enable/disable HTTPS │
|
|
||||||
│ hostname │ STRING(255) │ Server hostname (e.g., digiserver) │
|
|
||||||
│ domain │ STRING(255) │ Domain (e.g., domain.local) │
|
|
||||||
│ ip_address │ STRING(45) │ IP address (IPv4/IPv6) │
|
|
||||||
│ port │ INTEGER │ HTTPS port (default 443) │
|
|
||||||
│ created_at │ DATETIME │ Creation timestamp │
|
|
||||||
│ updated_at │ DATETIME │ Last update timestamp │
|
|
||||||
│ updated_by │ STRING(255) │ Admin who made change │
|
|
||||||
└─────────────────┴──────────────┴──────────────────────────────────────┘
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
🔐 SECURITY FEATURES
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
✅ Access Control
|
|
||||||
• Admin-only routes with @admin_required decorator
|
|
||||||
• Permission checks on all endpoints
|
|
||||||
• Login required for configuration access
|
|
||||||
|
|
||||||
✅ Input Validation
|
|
||||||
• Domain format validation
|
|
||||||
• IP address validation (IPv4/IPv6)
|
|
||||||
• Port range validation (1-65535)
|
|
||||||
• Required field validation
|
|
||||||
• Error messages for invalid inputs
|
|
||||||
|
|
||||||
✅ SSL/TLS Management
|
|
||||||
• Automatic Let's Encrypt certificates
|
|
||||||
• Automatic renewal before expiration
|
|
||||||
• Security headers (HSTS, X-Frame-Options, etc.)
|
|
||||||
• HTTP/2 and HTTP/3 support via Caddy
|
|
||||||
|
|
||||||
✅ Audit Trail
|
|
||||||
• All changes logged with timestamp
|
|
||||||
• Admin username recorded
|
|
||||||
• Enable/disable events tracked
|
|
||||||
• Searchable in server logs
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
🛠️ ADMIN COMMANDS
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
CLI UTILITY: https_manager.py
|
|
||||||
───────────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
Show Status:
|
|
||||||
python https_manager.py status
|
|
||||||
|
|
||||||
Enable HTTPS:
|
|
||||||
python https_manager.py enable digiserver digiserver.sibiusb.harting.intra 10.76.152.164 443
|
|
||||||
|
|
||||||
Disable HTTPS:
|
|
||||||
python https_manager.py disable
|
|
||||||
|
|
||||||
Show Configuration:
|
|
||||||
python https_manager.py show
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
📊 ACCESS POINTS
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
AFTER CONFIGURATION:
|
|
||||||
|
|
||||||
┌─ HTTPS (Recommended) ────────────────────────────────────────────┐
|
|
||||||
│ URL: https://digiserver.sibiusb.harting.intra │
|
|
||||||
│ Protocol: HTTPS (SSL/TLS) │
|
|
||||||
│ Port: 443 │
|
|
||||||
│ Certificate: Let's Encrypt (auto-renewed) │
|
|
||||||
│ Use: All secure connections, recommended for everyone │
|
|
||||||
└──────────────────────────────────────────────────────────────────┘
|
|
||||||
|
|
||||||
┌─ HTTP (Fallback) ────────────────────────────────────────────────┐
|
|
||||||
│ URL: http://10.76.152.164 │
|
|
||||||
│ Protocol: HTTP (plain text) │
|
|
||||||
│ Port: 80 │
|
|
||||||
│ Use: Troubleshooting, direct IP access, local network │
|
|
||||||
└──────────────────────────────────────────────────────────────────┘
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
📚 DOCUMENTATION FILES
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
1. HTTPS_QUICK_REFERENCE.md
|
|
||||||
• Quick setup guide (5 minutes)
|
|
||||||
• Admin checklist
|
|
||||||
• Common tasks
|
|
||||||
• Troubleshooting basics
|
|
||||||
• STATUS: ⭐ START HERE!
|
|
||||||
|
|
||||||
2. HTTPS_CONFIGURATION.md
|
|
||||||
• Comprehensive feature guide
|
|
||||||
• Step-by-step workflow
|
|
||||||
• Configuration details
|
|
||||||
• Prerequisites and requirements
|
|
||||||
• Integration overview
|
|
||||||
• Troubleshooting guide
|
|
||||||
• STATUS: For detailed reference
|
|
||||||
|
|
||||||
3. HTTPS_IMPLEMENTATION_SUMMARY.md
|
|
||||||
• Architecture and design
|
|
||||||
• Files created/modified
|
|
||||||
• Database schema
|
|
||||||
• Integration details
|
|
||||||
• Implementation checklist
|
|
||||||
• STATUS: For developers
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
✅ WORKFLOW
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
INITIAL STATE (HTTP ONLY)
|
|
||||||
┌─────────────────────┐
|
|
||||||
│ App on Port 80 │
|
|
||||||
│ HTTP only │
|
|
||||||
└────────┬────────────┘
|
|
||||||
│
|
|
||||||
└─ Accessible at: http://10.76.152.164
|
|
||||||
|
|
||||||
USER CONFIGURES HTTPS
|
|
||||||
┌─────────────────────────────────────────────┐
|
|
||||||
│ Admin Sets: │
|
|
||||||
│ • Hostname: digiserver │
|
|
||||||
│ • Domain: digiserver.sibiusb.harting.intra │
|
|
||||||
│ • IP: 10.76.152.164 │
|
|
||||||
│ • Port: 443 │
|
|
||||||
└────────┬────────────────────────────────────┘
|
|
||||||
│
|
|
||||||
↓
|
|
||||||
CONFIGURATION SAVED
|
|
||||||
┌──────────────────────────────────────────────┐
|
|
||||||
│ • Settings stored in database │
|
|
||||||
│ • Change logged with admin name & timestamp │
|
|
||||||
│ • Status updated in admin panel │
|
|
||||||
└────────┬─────────────────────────────────────┘
|
|
||||||
│
|
|
||||||
↓
|
|
||||||
SYSTEM OPERATIONAL
|
|
||||||
├─ HTTPS Active (Port 443)
|
|
||||||
│ URL: https://digiserver.sibiusb.harting.intra
|
|
||||||
│ Certificate: Auto-managed by Let's Encrypt
|
|
||||||
│
|
|
||||||
└─ HTTP Fallback (Port 80)
|
|
||||||
URL: http://10.76.152.164
|
|
||||||
For troubleshooting and backup access
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
✨ HIGHLIGHTS
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
🎯 USER EXPERIENCE
|
|
||||||
• No manual configuration needed
|
|
||||||
• Simple toggle to enable/disable
|
|
||||||
• Real-time preview of settings
|
|
||||||
• Status display shows current state
|
|
||||||
• Mobile-responsive interface
|
|
||||||
|
|
||||||
🔒 SECURITY
|
|
||||||
• Admin-only access
|
|
||||||
• Input validation on all fields
|
|
||||||
• Audit trail of all changes
|
|
||||||
• Automatic SSL certificates
|
|
||||||
• No sensitive data stored in plain text
|
|
||||||
|
|
||||||
⚙️ FLEXIBILITY
|
|
||||||
• Configurable hostname, domain, IP
|
|
||||||
• Custom port support
|
|
||||||
• Enable/disable without data loss
|
|
||||||
• CLI and web interface both available
|
|
||||||
• Works with existing Caddy setup
|
|
||||||
|
|
||||||
📊 MONITORING
|
|
||||||
• Status endpoint for integration
|
|
||||||
• Logged changes in server logs
|
|
||||||
• Admin dashboard status display
|
|
||||||
• CLI status command
|
|
||||||
|
|
||||||
🚀 AUTOMATION
|
|
||||||
• CLI interface for scripting
|
|
||||||
• Can be automated via setup scripts
|
|
||||||
• Supports headless configuration
|
|
||||||
• REST API endpoint for status
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
📋 CHECKLIST
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
IMPLEMENTATION
|
|
||||||
✅ Database model created (https_config.py)
|
|
||||||
✅ Admin routes added (3 new endpoints)
|
|
||||||
✅ Admin template created (https_config.html)
|
|
||||||
✅ Dashboard card added
|
|
||||||
✅ Database migration created
|
|
||||||
✅ CLI utility implemented
|
|
||||||
✅ Setup script created
|
|
||||||
✅ Documentation completed (3 guides)
|
|
||||||
✅ Code integrated with existing system
|
|
||||||
✅ Admin-only access enforced
|
|
||||||
✅ Input validation implemented
|
|
||||||
✅ Logging implemented
|
|
||||||
✅ Error handling added
|
|
||||||
|
|
||||||
DEPLOYMENT
|
|
||||||
⏳ Run database migration: python migrations/add_https_config_table.py
|
|
||||||
⏳ Start application: docker-compose up -d
|
|
||||||
⏳ Configure via admin panel
|
|
||||||
⏳ Verify access points
|
|
||||||
⏳ Check status display
|
|
||||||
⏳ Review logs for changes
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
🎉 SYSTEM READY
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
All files have been created and integrated.
|
|
||||||
The HTTPS configuration management system is complete and ready to use.
|
|
||||||
|
|
||||||
NEXT STEPS:
|
|
||||||
1. Run database migration
|
|
||||||
2. Restart application
|
|
||||||
3. Access admin panel
|
|
||||||
4. Navigate to HTTPS Configuration
|
|
||||||
5. Enable and configure HTTPS settings
|
|
||||||
6. Verify access points
|
|
||||||
|
|
||||||
For detailed instructions, see: HTTPS_QUICK_REFERENCE.md
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
487
QUICK_DEPLOYMENT.md
Normal file
@@ -0,0 +1,487 @@
|
|||||||
|
# DigiServer v2 - Quick Deployment Guide
|
||||||
|
|
||||||
|
## 📋 Overview
|
||||||
|
|
||||||
|
DigiServer is deployed using Docker Compose with the following architecture:
|
||||||
|
|
||||||
|
```
|
||||||
|
Internet (User)
|
||||||
|
↓
|
||||||
|
Nginx Reverse Proxy (Port 80/443)
|
||||||
|
↓
|
||||||
|
Internal Docker Network
|
||||||
|
↓
|
||||||
|
Flask App (Gunicorn on Port 5000)
|
||||||
|
↓
|
||||||
|
SQLite Database
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Quick Start (One Command)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /srv/digiserver-v2
|
||||||
|
bash deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
1. ✅ Start Docker containers
|
||||||
|
2. ✅ Initialize database
|
||||||
|
3. ✅ Run migrations
|
||||||
|
4. ✅ Configure HTTPS
|
||||||
|
5. ✅ Display access information
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Container Architecture
|
||||||
|
|
||||||
|
### **Container 1: digiserver-app (Flask)**
|
||||||
|
- **Image**: Built from Dockerfile (Python 3.13)
|
||||||
|
- **Port**: 5000 (internal only)
|
||||||
|
- **Volumes**:
|
||||||
|
- `./data:/app` - Persistent application data
|
||||||
|
- `./data/instance:/app/instance` - Database & configuration
|
||||||
|
- `./data/uploads:/app/app/static/uploads` - User uploads
|
||||||
|
- **Startup**: Automatically initializes database on first run
|
||||||
|
- **Health Check**: Every 30 seconds
|
||||||
|
|
||||||
|
### **Container 2: nginx (Reverse Proxy)**
|
||||||
|
- **Image**: nginx:alpine
|
||||||
|
- **Ports**: 80 & 443 (exposed to internet)
|
||||||
|
- **Volumes**:
|
||||||
|
- `nginx.conf` - Main configuration
|
||||||
|
- `./data/nginx-ssl/` - SSL certificates
|
||||||
|
- `./data/nginx-logs/` - Access/error logs
|
||||||
|
- `./data/certbot/` - Let's Encrypt challenges
|
||||||
|
- **Startup**: Waits for Flask app to start
|
||||||
|
- **Health Check**: Every 30 seconds
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Deployment Steps Explained
|
||||||
|
|
||||||
|
### **Step 1: Start Containers**
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
- Builds Flask app image (if needed)
|
||||||
|
- Starts both containers
|
||||||
|
- Waits for containers to be healthy
|
||||||
|
|
||||||
|
### **Step 2: Database Initialization** (docker-entrypoint.sh)
|
||||||
|
When Flask container starts:
|
||||||
|
1. Create required directories
|
||||||
|
2. Check if database exists
|
||||||
|
3. If NOT exists:
|
||||||
|
- Initialize SQLite database (dashboard.db)
|
||||||
|
- Create admin user from environment variables
|
||||||
|
4. Start Gunicorn server (4 workers, 120s timeout)
|
||||||
|
|
||||||
|
### **Step 3: Run Migrations**
|
||||||
|
```bash
|
||||||
|
docker-compose exec -T digiserver-app python /app/migrations/[migration_name].py
|
||||||
|
```
|
||||||
|
Applied migrations:
|
||||||
|
- `add_https_config_table.py` - HTTPS settings
|
||||||
|
- `add_player_user_table.py` - Player user management
|
||||||
|
- `add_email_to_https_config.py` - Email configuration
|
||||||
|
- `migrate_player_user_global.py` - Global settings
|
||||||
|
|
||||||
|
### **Step 4: Configure HTTPS**
|
||||||
|
- SSL certificates stored in `./data/nginx-ssl/`
|
||||||
|
- Pre-generated self-signed certs for development
|
||||||
|
- Ready for Let's Encrypt integration
|
||||||
|
|
||||||
|
### **Step 5: Reverse Proxy Routing** (nginx.conf)
|
||||||
|
```
|
||||||
|
HTTP (80):
|
||||||
|
• Redirect all traffic to HTTPS
|
||||||
|
• Allow ACME challenges for Let's Encrypt
|
||||||
|
|
||||||
|
HTTPS (443):
|
||||||
|
• TLS 1.2+, HTTP/2 enabled
|
||||||
|
• Proxy all requests to Flask app
|
||||||
|
• Security headers added
|
||||||
|
• Gzip compression enabled
|
||||||
|
• Max upload size: 2GB
|
||||||
|
• Proxy timeout: 300s
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Step 6: ProxyFix Middleware** (app/app.py)
|
||||||
|
Extracts real client information from Nginx headers:
|
||||||
|
- `X-Forwarded-For` → Real client IP
|
||||||
|
- `X-Forwarded-Proto` → Protocol (http/https)
|
||||||
|
- `X-Forwarded-Host` → Original hostname
|
||||||
|
- `X-Forwarded-Port` → Original port
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📂 Directory Structure & Persistence
|
||||||
|
|
||||||
|
```
|
||||||
|
/srv/digiserver-v2/
|
||||||
|
├── app/ (Flask application code)
|
||||||
|
├── data/ (PERSISTENT - mounted as Docker volume)
|
||||||
|
│ ├── app/ (Copy of app/ for container)
|
||||||
|
│ ├── instance/ (dashboard.db - SQLite database)
|
||||||
|
│ ├── uploads/ (User uploaded files)
|
||||||
|
│ ├── nginx-ssl/ (SSL certificates)
|
||||||
|
│ ├── nginx-logs/ (Nginx logs)
|
||||||
|
│ └── certbot/ (Let's Encrypt challenges)
|
||||||
|
├── migrations/ (Database schema updates)
|
||||||
|
├── docker-compose.yml (Container orchestration)
|
||||||
|
├── Dockerfile (Flask app image definition)
|
||||||
|
├── nginx.conf (Reverse proxy configuration)
|
||||||
|
└── docker-entrypoint.sh (Container startup script)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Default Credentials
|
||||||
|
|
||||||
|
```
|
||||||
|
Username: admin
|
||||||
|
Password: admin123
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️ **CHANGE IMMEDIATELY IN PRODUCTION!**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌐 Access Points
|
||||||
|
|
||||||
|
After deployment, access the app at:
|
||||||
|
- `https://localhost` (if deployed locally)
|
||||||
|
- `https://192.168.0.121` (if deployed on server)
|
||||||
|
- `https://<DOMAIN>` (if DNS configured)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Environment Variables (Optional)
|
||||||
|
|
||||||
|
Create `.env` file in project root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Network Configuration
|
||||||
|
HOSTNAME=digiserver
|
||||||
|
DOMAIN=digiserver.example.com
|
||||||
|
IP_ADDRESS=192.168.0.121
|
||||||
|
|
||||||
|
# SSL/HTTPS
|
||||||
|
EMAIL=admin@example.com
|
||||||
|
|
||||||
|
# Flask Configuration
|
||||||
|
SECRET_KEY=your-secret-key-here
|
||||||
|
FLASK_ENV=production
|
||||||
|
|
||||||
|
# Admin User
|
||||||
|
ADMIN_USERNAME=admin
|
||||||
|
ADMIN_PASSWORD=admin123
|
||||||
|
```
|
||||||
|
|
||||||
|
Then start with:
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Common Commands
|
||||||
|
|
||||||
|
### **Start/Stop Containers**
|
||||||
|
```bash
|
||||||
|
# Start containers
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Stop containers
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
# Restart containers
|
||||||
|
docker-compose restart
|
||||||
|
|
||||||
|
# Restart specific container
|
||||||
|
docker-compose restart digiserver-app
|
||||||
|
docker-compose restart nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### **View Logs**
|
||||||
|
```bash
|
||||||
|
# All containers
|
||||||
|
docker-compose logs
|
||||||
|
|
||||||
|
# Follow logs (real-time)
|
||||||
|
docker-compose logs -f
|
||||||
|
|
||||||
|
# Specific container
|
||||||
|
docker-compose logs -f digiserver-app
|
||||||
|
docker-compose logs -f nginx
|
||||||
|
|
||||||
|
# Show last 50 lines
|
||||||
|
docker-compose logs --tail=50 digiserver-app
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Container Status**
|
||||||
|
```bash
|
||||||
|
# Show running containers
|
||||||
|
docker-compose ps
|
||||||
|
|
||||||
|
# Show container details
|
||||||
|
docker-compose ps -a
|
||||||
|
|
||||||
|
# Check container health
|
||||||
|
docker ps --format="table {{.Names}}\t{{.Status}}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Database Operations**
|
||||||
|
```bash
|
||||||
|
# Access database shell
|
||||||
|
docker-compose exec digiserver-app sqlite3 /app/instance/dashboard.db
|
||||||
|
|
||||||
|
# Backup database
|
||||||
|
docker-compose exec digiserver-app cp /app/instance/dashboard.db /app/instance/dashboard.db.backup
|
||||||
|
|
||||||
|
# Restore database
|
||||||
|
docker-compose exec digiserver-app cp /app/instance/dashboard.db.backup /app/instance/dashboard.db
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Nginx Operations**
|
||||||
|
```bash
|
||||||
|
# Validate Nginx configuration
|
||||||
|
docker exec digiserver-nginx nginx -t
|
||||||
|
|
||||||
|
# Reload Nginx (without restart)
|
||||||
|
docker exec digiserver-nginx nginx -s reload
|
||||||
|
|
||||||
|
# View Nginx logs
|
||||||
|
docker-compose logs -f nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 SSL Certificate Management
|
||||||
|
|
||||||
|
### **Generate New Self-Signed Certificate**
|
||||||
|
```bash
|
||||||
|
bash generate_nginx_certs.sh 192.168.0.121 365
|
||||||
|
docker-compose restart nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `192.168.0.121` - Domain/IP for certificate
|
||||||
|
- `365` - Certificate validity in days
|
||||||
|
|
||||||
|
### **Set Up Let's Encrypt (Production)**
|
||||||
|
1. Update `DOMAIN` and `EMAIL` in environment
|
||||||
|
2. Modify `nginx.conf` to enable certbot challenges
|
||||||
|
3. Run certbot:
|
||||||
|
```bash
|
||||||
|
docker run --rm -v $(pwd)/data/certbot:/etc/letsencrypt \
|
||||||
|
-v $(pwd)/data/nginx-logs:/var/log/letsencrypt \
|
||||||
|
certbot/certbot certonly --webroot \
|
||||||
|
-w /var/www/certbot \
|
||||||
|
-d yourdomain.com \
|
||||||
|
-m your-email@example.com \
|
||||||
|
--agree-tos
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Troubleshooting
|
||||||
|
|
||||||
|
### **Containers not starting?**
|
||||||
|
```bash
|
||||||
|
# Check docker-compose logs
|
||||||
|
docker-compose logs
|
||||||
|
|
||||||
|
# Check system resources
|
||||||
|
docker stats
|
||||||
|
|
||||||
|
# Restart Docker daemon
|
||||||
|
sudo systemctl restart docker
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Application not responding?**
|
||||||
|
```bash
|
||||||
|
# Check app container health
|
||||||
|
docker-compose ps
|
||||||
|
|
||||||
|
# View app logs
|
||||||
|
docker-compose logs -f digiserver-app
|
||||||
|
|
||||||
|
# Test Flask directly
|
||||||
|
docker-compose exec digiserver-app curl http://localhost:5000/
|
||||||
|
```
|
||||||
|
|
||||||
|
### **HTTPS not working?**
|
||||||
|
```bash
|
||||||
|
# Verify Nginx config
|
||||||
|
docker exec digiserver-nginx nginx -t
|
||||||
|
|
||||||
|
# Check SSL certificates exist
|
||||||
|
ls -la ./data/nginx-ssl/
|
||||||
|
|
||||||
|
# View Nginx error logs
|
||||||
|
docker-compose logs nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Database issues?**
|
||||||
|
```bash
|
||||||
|
# Check database file exists
|
||||||
|
ls -la ./data/instance/dashboard.db
|
||||||
|
|
||||||
|
# Verify database permissions
|
||||||
|
docker-compose exec digiserver-app ls -la /app/instance/
|
||||||
|
|
||||||
|
# Check database tables
|
||||||
|
docker-compose exec digiserver-app sqlite3 /app/instance/dashboard.db ".tables"
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Port already in use?**
|
||||||
|
```bash
|
||||||
|
# Find process using port 80
|
||||||
|
sudo lsof -i :80
|
||||||
|
|
||||||
|
# Find process using port 443
|
||||||
|
sudo lsof -i :443
|
||||||
|
|
||||||
|
# Kill process (if needed)
|
||||||
|
sudo kill -9 <PID>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Health Checks
|
||||||
|
|
||||||
|
Both containers have health checks:
|
||||||
|
|
||||||
|
**Flask App**: Pings `http://localhost:5000/` every 30 seconds
|
||||||
|
**Nginx**: Pings `http://localhost:80/` every 30 seconds
|
||||||
|
|
||||||
|
Check health status:
|
||||||
|
```bash
|
||||||
|
docker-compose ps
|
||||||
|
# Look for "Up (healthy)" status
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Database Backup & Restore
|
||||||
|
|
||||||
|
### **Backup**
|
||||||
|
```bash
|
||||||
|
# Create backup
|
||||||
|
docker-compose exec digiserver-app cp /app/instance/dashboard.db /app/instance/dashboard.backup.db
|
||||||
|
|
||||||
|
# Download to local machine
|
||||||
|
cp ./data/instance/dashboard.backup.db ./backup/
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Restore**
|
||||||
|
```bash
|
||||||
|
# Stop containers
|
||||||
|
docker-compose stop
|
||||||
|
|
||||||
|
# Restore database
|
||||||
|
cp ./backup/dashboard.backup.db ./data/instance/dashboard.db
|
||||||
|
|
||||||
|
# Start containers
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 Performance Tuning
|
||||||
|
|
||||||
|
### **Gunicorn Workers** (docker-entrypoint.sh)
|
||||||
|
```bash
|
||||||
|
# Default: 4 workers
|
||||||
|
# Formula: (2 × CPU_count) + 1
|
||||||
|
# For 4-core CPU: 9 workers
|
||||||
|
|
||||||
|
# Modify docker-entrypoint.sh:
|
||||||
|
gunicorn --workers 9 ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Nginx Worker Processes** (nginx.conf)
|
||||||
|
```nginx
|
||||||
|
# Default: auto (CPU count)
|
||||||
|
worker_processes auto;
|
||||||
|
|
||||||
|
# Or specify manually:
|
||||||
|
worker_processes 4;
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Upload Timeout** (nginx.conf)
|
||||||
|
```nginx
|
||||||
|
# Default: 300s
|
||||||
|
proxy_connect_timeout 300s;
|
||||||
|
proxy_send_timeout 300s;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Security Checklist
|
||||||
|
|
||||||
|
- [ ] Change admin password immediately
|
||||||
|
- [ ] Set strong `SECRET_KEY` environment variable
|
||||||
|
- [ ] Enable firewall rules (allow only ports 80, 443)
|
||||||
|
- [ ] Set up HTTPS with Let's Encrypt
|
||||||
|
- [ ] Configure regular database backups
|
||||||
|
- [ ] Review Nginx security headers
|
||||||
|
- [ ] Update Flask dependencies regularly
|
||||||
|
- [ ] Monitor container logs for errors
|
||||||
|
- [ ] Restrict admin panel access (IP whitelist optional)
|
||||||
|
- [ ] Enable Flask debug mode only in development
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 Support & Documentation
|
||||||
|
|
||||||
|
- **Nginx Setup**: See `NGINX_SETUP_QUICK.md`
|
||||||
|
- **ProxyFix Configuration**: See `PROXY_FIX_SETUP.md`
|
||||||
|
- **Deployment Commands**: See `DEPLOYMENT_COMMANDS.md`
|
||||||
|
- **Issue Troubleshooting**: Check `old_code_documentation/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Deployment Checklist
|
||||||
|
|
||||||
|
- [ ] Docker & Docker Compose installed
|
||||||
|
- [ ] Running from `/srv/digiserver-v2` directory
|
||||||
|
- [ ] Environment variables configured (optional)
|
||||||
|
- [ ] Port 80/443 available
|
||||||
|
- [ ] Sufficient disk space (min 5GB)
|
||||||
|
- [ ] Sufficient RAM (min 2GB free)
|
||||||
|
- [ ] Network connectivity verified
|
||||||
|
- [ ] SSL certificates generated or obtained
|
||||||
|
- [ ] Admin credentials changed (production)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Next Steps After Deployment
|
||||||
|
|
||||||
|
1. **Access Web Interface**
|
||||||
|
- Login with admin credentials
|
||||||
|
- Change password immediately
|
||||||
|
|
||||||
|
2. **Configure Application**
|
||||||
|
- Set up players
|
||||||
|
- Upload content
|
||||||
|
- Configure groups & permissions
|
||||||
|
|
||||||
|
3. **Production Hardening**
|
||||||
|
- Enable Let's Encrypt HTTPS
|
||||||
|
- Configure firewall rules
|
||||||
|
- Set up database backups
|
||||||
|
- Monitor logs
|
||||||
|
|
||||||
|
4. **Optional Enhancements**
|
||||||
|
- Set up custom domain
|
||||||
|
- Configure email notifications
|
||||||
|
- Install optional dependencies (LibreOffice, etc.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated**: January 15, 2026
|
||||||
120
QUICK_START.md
@@ -1,120 +0,0 @@
|
|||||||
# 🚀 DigiServer Deployment - Quick Reference Card
|
|
||||||
|
|
||||||
## Instant Deployment
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /path/to/digiserver-v2
|
|
||||||
./deploy.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
That's it! ✅
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📖 Documentation Files
|
|
||||||
|
|
||||||
| File | Purpose | Size |
|
|
||||||
|------|---------|------|
|
|
||||||
| [DEPLOYMENT_INDEX.md](DEPLOYMENT_INDEX.md) | Navigation guide | 8.1 KB |
|
|
||||||
| [DEPLOYMENT_README.md](DEPLOYMENT_README.md) | Complete guide | 9.4 KB |
|
|
||||||
| [DOCKER_EXEC_COMMANDS.md](DOCKER_EXEC_COMMANDS.md) | Command reference | 7.6 KB |
|
|
||||||
| [DEPLOYMENT_COMMANDS.md](DEPLOYMENT_COMMANDS.md) | Detailed guide | 6.8 KB |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 Executable Scripts
|
|
||||||
|
|
||||||
| Script | Purpose | Time |
|
|
||||||
|--------|---------|------|
|
|
||||||
| [deploy.sh](deploy.sh) | Fully automated | 2-3 min |
|
|
||||||
| [setup_https.sh](setup_https.sh) | Semi-automated | 3-5 min |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 Common Commands
|
|
||||||
|
|
||||||
### Check Status
|
|
||||||
```bash
|
|
||||||
docker-compose ps
|
|
||||||
```
|
|
||||||
|
|
||||||
### View Logs
|
|
||||||
```bash
|
|
||||||
docker-compose logs -f digiserver-app
|
|
||||||
```
|
|
||||||
|
|
||||||
### Verify HTTPS Configuration
|
|
||||||
```bash
|
|
||||||
docker-compose exec -T digiserver-app python /app/https_manager.py status
|
|
||||||
```
|
|
||||||
|
|
||||||
### Access the Application
|
|
||||||
```
|
|
||||||
https://digiserver.sibiusb.harting.intra
|
|
||||||
https://10.76.152.164
|
|
||||||
https://digiserver
|
|
||||||
```
|
|
||||||
|
|
||||||
### Default Login
|
|
||||||
```
|
|
||||||
Username: admin
|
|
||||||
Password: admin123
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🆘 Troubleshooting
|
|
||||||
|
|
||||||
| Issue | Solution |
|
|
||||||
|-------|----------|
|
|
||||||
| Containers won't start | `docker-compose logs` |
|
|
||||||
| Migration fails | Check DB connection, see docs |
|
|
||||||
| HTTPS errors | Clear Caddy cache: `docker volume rm digiserver-v2_caddy-*` |
|
|
||||||
| Port conflict | `lsof -i :443` or change in docker-compose.yml |
|
|
||||||
|
|
||||||
See [DEPLOYMENT_README.md#-troubleshooting](DEPLOYMENT_README.md#-troubleshooting) for full guide.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🌍 Deploy on Different PC
|
|
||||||
|
|
||||||
1. Copy project files
|
|
||||||
2. Install Docker & Docker Compose
|
|
||||||
3. Run `./deploy.sh`
|
|
||||||
|
|
||||||
Done! 🎉
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔑 Customize Deployment
|
|
||||||
|
|
||||||
```bash
|
|
||||||
HOSTNAME=myserver \
|
|
||||||
DOMAIN=myserver.internal \
|
|
||||||
IP_ADDRESS=192.168.1.100 \
|
|
||||||
EMAIL=admin@example.com \
|
|
||||||
./deploy.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📚 Need More Help?
|
|
||||||
|
|
||||||
- **First time?** → [DEPLOYMENT_README.md](DEPLOYMENT_README.md)
|
|
||||||
- **Need a command?** → [DOCKER_EXEC_COMMANDS.md](DOCKER_EXEC_COMMANDS.md)
|
|
||||||
- **Lost?** → [DEPLOYMENT_INDEX.md](DEPLOYMENT_INDEX.md)
|
|
||||||
- **Troubleshooting?** → [DEPLOYMENT_README.md#-troubleshooting](DEPLOYMENT_README.md#-troubleshooting)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ What You Get
|
|
||||||
|
|
||||||
✅ Web application with admin dashboard
|
|
||||||
✅ HTTPS with self-signed certificates
|
|
||||||
✅ User management system
|
|
||||||
✅ Player & content management
|
|
||||||
✅ Fully configured & ready to use
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Ready to deploy?** `./deploy.sh` 🚀
|
|
||||||
5
app/app.py
Executable file → Normal file
@@ -4,6 +4,7 @@ Modern Flask application with blueprint architecture
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
from flask import Flask, render_template
|
from flask import Flask, render_template
|
||||||
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
from app.config import DevelopmentConfig, ProductionConfig, TestingConfig
|
from app.config import DevelopmentConfig, ProductionConfig, TestingConfig
|
||||||
@@ -37,6 +38,10 @@ def create_app(config_name=None):
|
|||||||
|
|
||||||
app.config.from_object(config)
|
app.config.from_object(config)
|
||||||
|
|
||||||
|
# Apply ProxyFix middleware for reverse proxy (Nginx/Caddy)
|
||||||
|
# This ensures proper handling of X-Forwarded-* headers
|
||||||
|
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1)
|
||||||
|
|
||||||
# Initialize extensions
|
# Initialize extensions
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
bcrypt.init_app(app)
|
bcrypt.init_app(app)
|
||||||
|
|||||||
0
app/blueprints/__init__.py
Executable file → Normal file
@@ -11,6 +11,7 @@ from app.extensions import db, bcrypt
|
|||||||
from app.models import User, Player, Group, Content, ServerLog, Playlist, HTTPSConfig
|
from app.models import User, Player, Group, Content, ServerLog, Playlist, HTTPSConfig
|
||||||
from app.utils.logger import log_action
|
from app.utils.logger import log_action
|
||||||
from app.utils.caddy_manager import CaddyConfigGenerator
|
from app.utils.caddy_manager import CaddyConfigGenerator
|
||||||
|
from app.utils.nginx_config_reader import get_nginx_status
|
||||||
|
|
||||||
admin_bp = Blueprint('admin', __name__, url_prefix='/admin')
|
admin_bp = Blueprint('admin', __name__, url_prefix='/admin')
|
||||||
|
|
||||||
@@ -870,10 +871,14 @@ def https_config():
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
log_action('info', f'HTTPS status auto-corrected to enabled (detected from request)')
|
log_action('info', f'HTTPS status auto-corrected to enabled (detected from request)')
|
||||||
|
|
||||||
|
# Get Nginx configuration status
|
||||||
|
nginx_status = get_nginx_status()
|
||||||
|
|
||||||
return render_template('admin/https_config.html',
|
return render_template('admin/https_config.html',
|
||||||
config=config,
|
config=config,
|
||||||
is_https_active=is_https_active,
|
is_https_active=is_https_active,
|
||||||
current_host=current_host)
|
current_host=current_host,
|
||||||
|
nginx_status=nginx_status)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_action('error', f'Error loading HTTPS config page: {str(e)}')
|
log_action('error', f'Error loading HTTPS config page: {str(e)}')
|
||||||
flash('Error loading HTTPS configuration page.', 'danger')
|
flash('Error loading HTTPS configuration page.', 'danger')
|
||||||
|
|||||||
0
app/blueprints/api.py
Executable file → Normal file
0
app/blueprints/auth.py
Executable file → Normal file
0
app/blueprints/content.py
Executable file → Normal file
0
app/blueprints/content_old.py
Executable file → Normal file
0
app/blueprints/groups.py
Executable file → Normal file
0
app/blueprints/main.py
Executable file → Normal file
0
app/blueprints/players.py
Executable file → Normal file
0
app/blueprints/playlist.py
Executable file → Normal file
5
app/config.py
Executable file → Normal file
@@ -29,6 +29,11 @@ class Config:
|
|||||||
SESSION_COOKIE_HTTPONLY = True
|
SESSION_COOKIE_HTTPONLY = True
|
||||||
SESSION_COOKIE_SAMESITE = 'Lax'
|
SESSION_COOKIE_SAMESITE = 'Lax'
|
||||||
|
|
||||||
|
# Reverse proxy trust (for Nginx/Caddy with ProxyFix middleware)
|
||||||
|
# These are set by werkzeug.middleware.proxy_fix
|
||||||
|
TRUSTED_PROXIES = os.getenv('TRUSTED_PROXIES', '127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16')
|
||||||
|
PREFERRED_URL_SCHEME = os.getenv('PREFERRED_URL_SCHEME', 'https')
|
||||||
|
|
||||||
# Cache
|
# Cache
|
||||||
SEND_FILE_MAX_AGE_DEFAULT = 300 # 5 minutes for static files
|
SEND_FILE_MAX_AGE_DEFAULT = 300 # 5 minutes for static files
|
||||||
|
|
||||||
|
|||||||
0
app/extensions.py
Executable file → Normal file
0
app/models/__init__.py
Executable file → Normal file
0
app/models/content.py
Executable file → Normal file
0
app/models/group.py
Executable file → Normal file
0
app/models/player.py
Executable file → Normal file
0
app/models/player_edit.py
Executable file → Normal file
0
app/models/player_feedback.py
Executable file → Normal file
0
app/models/player_user.py
Executable file → Normal file
0
app/models/playlist.py
Executable file → Normal file
0
app/models/server_log.py
Executable file → Normal file
0
app/models/user.py
Executable file → Normal file
0
app/static/icons/edit.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 294 B After Width: | Height: | Size: 294 B |
0
app/static/icons/home.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 311 B |
0
app/static/icons/info.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 329 B After Width: | Height: | Size: 329 B |
0
app/static/icons/monitor.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 301 B After Width: | Height: | Size: 301 B |
0
app/static/icons/moon.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 257 B After Width: | Height: | Size: 257 B |
0
app/static/icons/playlist.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 259 B After Width: | Height: | Size: 259 B |
0
app/static/icons/sun.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 651 B After Width: | Height: | Size: 651 B |
0
app/static/icons/trash.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 334 B After Width: | Height: | Size: 334 B |
0
app/static/icons/upload.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 345 B After Width: | Height: | Size: 345 B |
0
app/static/icons/warning.svg
Executable file → Normal file
|
Before Width: | Height: | Size: 396 B After Width: | Height: | Size: 396 B |
0
app/static/uploads/.gitkeep
Executable file → Normal file
0
app/templates/admin/admin.html
Executable file → Normal file
0
app/templates/admin/customize_logos.html
Executable file → Normal file
0
app/templates/admin/dependencies.html
Executable file → Normal file
0
app/templates/admin/editing_users.html
Executable file → Normal file
@@ -160,6 +160,95 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Nginx Status Card -->
|
||||||
|
<div class="card nginx-status-card">
|
||||||
|
<h2>🔧 Nginx Reverse Proxy Status</h2>
|
||||||
|
{% if nginx_status.available %}
|
||||||
|
<div class="nginx-status-content">
|
||||||
|
<div class="status-item">
|
||||||
|
<strong>Status:</strong>
|
||||||
|
<span class="badge badge-success">✅ Nginx Configured</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="status-item">
|
||||||
|
<strong>Configuration Path:</strong>
|
||||||
|
<code>{{ nginx_status.path }}</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if nginx_status.ssl_enabled %}
|
||||||
|
<div class="status-item">
|
||||||
|
<strong>SSL/TLS:</strong>
|
||||||
|
<span class="badge badge-success">🔒 Enabled</span>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="status-item">
|
||||||
|
<strong>SSL/TLS:</strong>
|
||||||
|
<span class="badge badge-warning">⚠️ Not Configured</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if nginx_status.http_ports %}
|
||||||
|
<div class="status-item">
|
||||||
|
<strong>HTTP Ports:</strong>
|
||||||
|
<code>{{ nginx_status.http_ports|join(', ') }}</code>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if nginx_status.https_ports %}
|
||||||
|
<div class="status-item">
|
||||||
|
<strong>HTTPS Ports:</strong>
|
||||||
|
<code>{{ nginx_status.https_ports|join(', ') }}</code>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if nginx_status.server_names %}
|
||||||
|
<div class="status-item">
|
||||||
|
<strong>Server Names:</strong>
|
||||||
|
{% for name in nginx_status.server_names %}
|
||||||
|
<code>{{ name }}</code>{% if not loop.last %}<br>{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if nginx_status.upstream_servers %}
|
||||||
|
<div class="status-item">
|
||||||
|
<strong>Upstream Servers:</strong>
|
||||||
|
{% for server in nginx_status.upstream_servers %}
|
||||||
|
<code>{{ server }}</code>{% if not loop.last %}<br>{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if nginx_status.ssl_protocols %}
|
||||||
|
<div class="status-item">
|
||||||
|
<strong>SSL Protocols:</strong>
|
||||||
|
<code>{{ nginx_status.ssl_protocols|join(', ') }}</code>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if nginx_status.client_max_body_size %}
|
||||||
|
<div class="status-item">
|
||||||
|
<strong>Max Body Size:</strong>
|
||||||
|
<code>{{ nginx_status.client_max_body_size }}</code>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if nginx_status.gzip_enabled %}
|
||||||
|
<div class="status-item">
|
||||||
|
<strong>Gzip Compression:</strong>
|
||||||
|
<span class="badge badge-success">✅ Enabled</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="status-disabled">
|
||||||
|
<p>⚠️ <strong>Nginx configuration not accessible</strong></p>
|
||||||
|
<p>Error: {{ nginx_status.error|default('Unknown error') }}</p>
|
||||||
|
<p style="font-size: 12px; color: #666;">Path checked: {{ nginx_status.path }}</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Information Section -->
|
<!-- Information Section -->
|
||||||
<div class="card info-card">
|
<div class="card info-card">
|
||||||
<h2>ℹ️ Important Information</h2>
|
<h2>ℹ️ Important Information</h2>
|
||||||
@@ -466,6 +555,45 @@
|
|||||||
color: #0066cc;
|
color: #0066cc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nginx-status-card {
|
||||||
|
background: linear-gradient(135deg, #f0f7ff 0%, #e7f3ff 100%);
|
||||||
|
border-left: 5px solid #0066cc;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nginx-status-card h2 {
|
||||||
|
color: #0066cc;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nginx-status-content {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-item {
|
||||||
|
padding: 12px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-left: 3px solid #0066cc;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-item strong {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 150px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-item code {
|
||||||
|
background: #f0f7ff;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
color: #0066cc;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
.info-sections {
|
.info-sections {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
|||||||
0
app/templates/admin/leftover_media.html
Executable file → Normal file
0
app/templates/admin/user_management.html
Executable file → Normal file
0
app/templates/auth/change_password.html
Executable file → Normal file
0
app/templates/auth/login.html
Executable file → Normal file
0
app/templates/auth/register.html
Executable file → Normal file
0
app/templates/base.html
Executable file → Normal file
0
app/templates/content/content_list.html
Executable file → Normal file
0
app/templates/content/content_list_new.html
Executable file → Normal file
0
app/templates/content/edit_content.html
Executable file → Normal file
0
app/templates/content/manage_playlist_content.html
Executable file → Normal file
0
app/templates/content/media_library.html
Executable file → Normal file
0
app/templates/content/upload_content.html
Executable file → Normal file
0
app/templates/content/upload_media.html
Executable file → Normal file
0
app/templates/dashboard.html
Executable file → Normal file
0
app/templates/errors/403.html
Executable file → Normal file
0
app/templates/errors/404.html
Executable file → Normal file
0
app/templates/errors/500.html
Executable file → Normal file
0
app/templates/groups/create_group.html
Executable file → Normal file
0
app/templates/groups/edit_group.html
Executable file → Normal file
0
app/templates/groups/group_fullscreen.html
Executable file → Normal file
0
app/templates/groups/groups_list.html
Executable file → Normal file
0
app/templates/groups/manage_group.html
Executable file → Normal file
0
app/templates/players/add_player.html
Executable file → Normal file
0
app/templates/players/edit_player.html
Executable file → Normal file
0
app/templates/players/edited_media.html
Executable file → Normal file
0
app/templates/players/manage_player.html
Executable file → Normal file
0
app/templates/players/player_fullscreen.html
Executable file → Normal file
0
app/templates/players/player_page.html
Executable file → Normal file
0
app/templates/players/players_list.html
Executable file → Normal file
0
app/templates/playlist/manage_playlist.html
Executable file → Normal file
0
app/utils/__init__.py
Executable file → Normal file
0
app/utils/group_player_management.py
Executable file → Normal file
0
app/utils/logger.py
Executable file → Normal file
120
app/utils/nginx_config_reader.py
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
"""Nginx configuration reader utility."""
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from typing import Dict, List, Optional, Any
|
||||||
|
|
||||||
|
|
||||||
|
class NginxConfigReader:
|
||||||
|
"""Read and parse Nginx configuration files."""
|
||||||
|
|
||||||
|
def __init__(self, config_path: str = '/etc/nginx/nginx.conf'):
|
||||||
|
"""Initialize Nginx config reader."""
|
||||||
|
self.config_path = config_path
|
||||||
|
self.config_content = None
|
||||||
|
self.is_available = os.path.exists(config_path)
|
||||||
|
|
||||||
|
if self.is_available:
|
||||||
|
try:
|
||||||
|
with open(config_path, 'r') as f:
|
||||||
|
self.config_content = f.read()
|
||||||
|
except Exception as e:
|
||||||
|
self.is_available = False
|
||||||
|
self.error = str(e)
|
||||||
|
|
||||||
|
def get_status(self) -> Dict[str, Any]:
|
||||||
|
"""Get Nginx configuration status."""
|
||||||
|
if not self.is_available:
|
||||||
|
return {
|
||||||
|
'available': False,
|
||||||
|
'error': 'Nginx configuration not found',
|
||||||
|
'path': self.config_path
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'available': True,
|
||||||
|
'path': self.config_path,
|
||||||
|
'file_exists': os.path.exists(self.config_path),
|
||||||
|
'ssl_enabled': self._check_ssl_enabled(),
|
||||||
|
'http_ports': self._extract_http_ports(),
|
||||||
|
'https_ports': self._extract_https_ports(),
|
||||||
|
'upstream_servers': self._extract_upstream_servers(),
|
||||||
|
'server_names': self._extract_server_names(),
|
||||||
|
'ssl_protocols': self._extract_ssl_protocols(),
|
||||||
|
'client_max_body_size': self._extract_client_max_body_size(),
|
||||||
|
'gzip_enabled': self._check_gzip_enabled(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def _check_ssl_enabled(self) -> bool:
|
||||||
|
"""Check if SSL is enabled."""
|
||||||
|
if not self.config_content:
|
||||||
|
return False
|
||||||
|
return 'ssl_certificate' in self.config_content
|
||||||
|
|
||||||
|
def _extract_http_ports(self) -> List[int]:
|
||||||
|
"""Extract HTTP listening ports."""
|
||||||
|
if not self.config_content:
|
||||||
|
return []
|
||||||
|
pattern = r'listen\s+(\d+)'
|
||||||
|
matches = re.findall(pattern, self.config_content)
|
||||||
|
return sorted(list(set(int(p) for p in matches if int(p) < 1000)))
|
||||||
|
|
||||||
|
def _extract_https_ports(self) -> List[int]:
|
||||||
|
"""Extract HTTPS listening ports."""
|
||||||
|
if not self.config_content:
|
||||||
|
return []
|
||||||
|
pattern = r'listen\s+(\d+).*ssl'
|
||||||
|
matches = re.findall(pattern, self.config_content)
|
||||||
|
return sorted(list(set(int(p) for p in matches)))
|
||||||
|
|
||||||
|
def _extract_upstream_servers(self) -> List[str]:
|
||||||
|
"""Extract upstream servers."""
|
||||||
|
if not self.config_content:
|
||||||
|
return []
|
||||||
|
upstream_match = re.search(r'upstream\s+\w+\s*{([^}]+)}', self.config_content)
|
||||||
|
if upstream_match:
|
||||||
|
upstream_content = upstream_match.group(1)
|
||||||
|
servers = re.findall(r'server\s+([^\s;]+)', upstream_content)
|
||||||
|
return servers
|
||||||
|
return []
|
||||||
|
|
||||||
|
def _extract_server_names(self) -> List[str]:
|
||||||
|
"""Extract server names."""
|
||||||
|
if not self.config_content:
|
||||||
|
return []
|
||||||
|
pattern = r'server_name\s+([^;]+);'
|
||||||
|
matches = re.findall(pattern, self.config_content)
|
||||||
|
result = []
|
||||||
|
for match in matches:
|
||||||
|
names = match.strip().split()
|
||||||
|
result.extend(names)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _extract_ssl_protocols(self) -> List[str]:
|
||||||
|
"""Extract SSL protocols."""
|
||||||
|
if not self.config_content:
|
||||||
|
return []
|
||||||
|
pattern = r'ssl_protocols\s+([^;]+);'
|
||||||
|
match = re.search(pattern, self.config_content)
|
||||||
|
if match:
|
||||||
|
return match.group(1).strip().split()
|
||||||
|
return []
|
||||||
|
|
||||||
|
def _extract_client_max_body_size(self) -> Optional[str]:
|
||||||
|
"""Extract client max body size."""
|
||||||
|
if not self.config_content:
|
||||||
|
return None
|
||||||
|
pattern = r'client_max_body_size\s+([^;]+);'
|
||||||
|
match = re.search(pattern, self.config_content)
|
||||||
|
return match.group(1).strip() if match else None
|
||||||
|
|
||||||
|
def _check_gzip_enabled(self) -> bool:
|
||||||
|
"""Check if gzip is enabled."""
|
||||||
|
if not self.config_content:
|
||||||
|
return False
|
||||||
|
return bool(re.search(r'gzip\s+on\s*;', self.config_content))
|
||||||
|
|
||||||
|
|
||||||
|
def get_nginx_status() -> Dict[str, Any]:
|
||||||
|
"""Get Nginx configuration status."""
|
||||||
|
reader = NginxConfigReader()
|
||||||
|
return reader.get_status()
|
||||||
0
app/utils/pptx_converter.py
Executable file → Normal file
0
app/utils/uploads.py
Executable file → Normal file
@@ -1,27 +0,0 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
|
||||||
digiserver:
|
|
||||||
build: .
|
|
||||||
container_name: digiserver-v2-http
|
|
||||||
ports:
|
|
||||||
- "80:5000" # Direct HTTP exposure on port 80
|
|
||||||
volumes:
|
|
||||||
- ./instance:/app/instance
|
|
||||||
- ./app/static/uploads:/app/app/static/uploads
|
|
||||||
environment:
|
|
||||||
- FLASK_ENV=production
|
|
||||||
- SECRET_KEY=${SECRET_KEY:-your-secret-key-change-this}
|
|
||||||
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
|
|
||||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin123}
|
|
||||||
restart: unless-stopped
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/').read()"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 40s
|
|
||||||
|
|
||||||
# Usage: docker-compose -f docker-compose.http.yml up -d
|
|
||||||
# Access at: http://localhost or http://your-server-ip
|
|
||||||
# Note: This is for development/testing only. Use docker-compose.yml for production HTTPS.
|
|
||||||
24
docker-compose.yml
Executable file → Normal file
@@ -26,19 +26,19 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- digiserver-network
|
- digiserver-network
|
||||||
|
|
||||||
# Caddy reverse proxy with automatic HTTPS
|
# Nginx reverse proxy with HTTPS support
|
||||||
caddy:
|
nginx:
|
||||||
image: caddy:2-alpine
|
image: nginx:alpine
|
||||||
container_name: digiserver-caddy
|
container_name: digiserver-nginx
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
- "443:443"
|
- "443:443"
|
||||||
- "443:443/udp" # HTTP/3 support
|
|
||||||
- "2019:2019" # Caddy admin API
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/Caddyfile:/etc/caddy/Caddyfile:ro
|
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
- ./data/caddy-data:/data
|
- ./nginx-custom-domains.conf:/etc/nginx/conf.d/custom-domains.conf:rw
|
||||||
- ./data/caddy-config:/config
|
- ./data/nginx-ssl:/etc/nginx/ssl:ro
|
||||||
|
- ./data/nginx-logs:/var/log/nginx
|
||||||
|
- ./data/certbot:/var/www/certbot:ro # For Let's Encrypt ACME challenges
|
||||||
environment:
|
environment:
|
||||||
- DOMAIN=${DOMAIN:-localhost}
|
- DOMAIN=${DOMAIN:-localhost}
|
||||||
- EMAIL=${EMAIL:-admin@localhost}
|
- EMAIL=${EMAIL:-admin@localhost}
|
||||||
@@ -46,6 +46,12 @@ services:
|
|||||||
digiserver-app:
|
digiserver-app:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80/"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
networks:
|
networks:
|
||||||
- digiserver-network
|
- digiserver-network
|
||||||
|
|
||||||
|
|||||||
0
fix_player_user_schema.py
Executable file → Normal file
30
generate_nginx_certs.sh
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Generate self-signed SSL certificates for Nginx
|
||||||
|
# Usage: ./generate_nginx_certs.sh [domain] [days]
|
||||||
|
|
||||||
|
DOMAIN=${1:-localhost}
|
||||||
|
DAYS=${2:-365}
|
||||||
|
CERT_DIR="./data/nginx-ssl"
|
||||||
|
|
||||||
|
echo "🔐 Generating self-signed SSL certificate for Nginx"
|
||||||
|
echo "Domain: $DOMAIN"
|
||||||
|
echo "Valid for: $DAYS days"
|
||||||
|
echo "Certificate directory: $CERT_DIR"
|
||||||
|
|
||||||
|
# Create directory if it doesnt exist
|
||||||
|
mkdir -p "$CERT_DIR"
|
||||||
|
|
||||||
|
# Generate private key and certificate
|
||||||
|
openssl req -x509 -nodes -days "$DAYS" \
|
||||||
|
-newkey rsa:2048 \
|
||||||
|
-keyout "$CERT_DIR/key.pem" \
|
||||||
|
-out "$CERT_DIR/cert.pem" \
|
||||||
|
-subj "/CN=$DOMAIN/O=DigiServer/C=US"
|
||||||
|
|
||||||
|
# Set proper permissions
|
||||||
|
chmod 644 "$CERT_DIR/cert.pem"
|
||||||
|
chmod 600 "$CERT_DIR/key.pem"
|
||||||
|
|
||||||
|
echo "✅ Certificates generated successfully!"
|
||||||
|
echo "Certificate: $CERT_DIR/cert.pem"
|
||||||
|
echo "Key: $CERT_DIR/key.pem"
|
||||||
157
https_manager.py
@@ -1,157 +0,0 @@
|
|||||||
"""Utility script for managing HTTPS configuration from command line."""
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
sys.path.insert(0, '/app')
|
|
||||||
|
|
||||||
from app.app import create_app
|
|
||||||
from app.models.https_config import HTTPSConfig
|
|
||||||
|
|
||||||
def show_help():
|
|
||||||
"""Display help information."""
|
|
||||||
print("""
|
|
||||||
HTTPS Configuration Management Utility
|
|
||||||
======================================
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
python https_manager.py <command> [arguments]
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
status Show current HTTPS configuration status
|
|
||||||
enable <hostname> <domain> <email> <ip> [port]
|
|
||||||
Enable HTTPS with specified settings
|
|
||||||
disable Disable HTTPS
|
|
||||||
show Show detailed configuration
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
# Show current status
|
|
||||||
python https_manager.py status
|
|
||||||
|
|
||||||
# Enable HTTPS
|
|
||||||
python https_manager.py enable digiserver digiserver.sibiusb.harting.intra admin@example.com 10.76.152.164 443
|
|
||||||
|
|
||||||
# Disable HTTPS
|
|
||||||
python https_manager.py disable
|
|
||||||
|
|
||||||
# Show detailed config
|
|
||||||
python https_manager.py show
|
|
||||||
""")
|
|
||||||
|
|
||||||
def show_status():
|
|
||||||
"""Show current HTTPS status."""
|
|
||||||
app = create_app()
|
|
||||||
with app.app_context():
|
|
||||||
config = HTTPSConfig.get_config()
|
|
||||||
if config:
|
|
||||||
print("\n" + "=" * 50)
|
|
||||||
print("HTTPS Configuration Status")
|
|
||||||
print("=" * 50)
|
|
||||||
print(f"Status: {'✅ ENABLED' if config.https_enabled else '⚠️ DISABLED'}")
|
|
||||||
print(f"Hostname: {config.hostname or 'N/A'}")
|
|
||||||
print(f"Domain: {config.domain or 'N/A'}")
|
|
||||||
print(f"IP Address: {config.ip_address or 'N/A'}")
|
|
||||||
print(f"Port: {config.port}")
|
|
||||||
print(f"Updated: {config.updated_at.strftime('%Y-%m-%d %H:%M:%S')} by {config.updated_by or 'N/A'}")
|
|
||||||
if config.https_enabled:
|
|
||||||
print(f"\nAccess URL: https://{config.domain}")
|
|
||||||
print(f"Fallback: http://{config.ip_address}")
|
|
||||||
print("=" * 50 + "\n")
|
|
||||||
else:
|
|
||||||
print("\n⚠️ No HTTPS configuration found. Use 'enable' command to create one.\n")
|
|
||||||
|
|
||||||
def enable_https(hostname: str, domain: str, ip_address: str, email: str, port: str = '443'):
|
|
||||||
"""Enable HTTPS with specified settings."""
|
|
||||||
app = create_app()
|
|
||||||
with app.app_context():
|
|
||||||
try:
|
|
||||||
port_num = int(port)
|
|
||||||
config = HTTPSConfig.create_or_update(
|
|
||||||
https_enabled=True,
|
|
||||||
hostname=hostname,
|
|
||||||
domain=domain,
|
|
||||||
ip_address=ip_address,
|
|
||||||
email=email,
|
|
||||||
port=port_num,
|
|
||||||
updated_by='cli_admin'
|
|
||||||
)
|
|
||||||
print("\n" + "=" * 50)
|
|
||||||
print("✅ HTTPS Configuration Updated")
|
|
||||||
print("=" * 50)
|
|
||||||
print(f"Hostname: {hostname}")
|
|
||||||
print(f"Domain: {domain}")
|
|
||||||
print(f"Email: {email}")
|
|
||||||
print(f"IP Address: {ip_address}")
|
|
||||||
print(f"Port: {port_num}")
|
|
||||||
print(f"\nAccess URL: https://{domain}")
|
|
||||||
print(f"Fallback: http://{ip_address}")
|
|
||||||
print("=" * 50 + "\n")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"\n❌ Error: {str(e)}\n")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def disable_https():
|
|
||||||
"""Disable HTTPS."""
|
|
||||||
app = create_app()
|
|
||||||
with app.app_context():
|
|
||||||
try:
|
|
||||||
config = HTTPSConfig.create_or_update(
|
|
||||||
https_enabled=False,
|
|
||||||
updated_by='cli_admin'
|
|
||||||
)
|
|
||||||
print("\n" + "=" * 50)
|
|
||||||
print("⚠️ HTTPS Disabled")
|
|
||||||
print("=" * 50)
|
|
||||||
print("The application is now running on HTTP only (port 80)")
|
|
||||||
print("=" * 50 + "\n")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"\n❌ Error: {str(e)}\n")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def show_config():
|
|
||||||
"""Show detailed configuration."""
|
|
||||||
app = create_app()
|
|
||||||
with app.app_context():
|
|
||||||
config = HTTPSConfig.get_config()
|
|
||||||
if config:
|
|
||||||
print("\n" + "=" * 50)
|
|
||||||
print("Detailed HTTPS Configuration")
|
|
||||||
print("=" * 50)
|
|
||||||
for key, value in config.to_dict().items():
|
|
||||||
print(f"{key:.<30} {value}")
|
|
||||||
print("=" * 50 + "\n")
|
|
||||||
else:
|
|
||||||
print("\n⚠️ No HTTPS configuration found.\n")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Main entry point."""
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
show_help()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
command = sys.argv[1].lower()
|
|
||||||
|
|
||||||
if command == 'status':
|
|
||||||
show_status()
|
|
||||||
elif command == 'enable':
|
|
||||||
if len(sys.argv) < 6:
|
|
||||||
print("\nError: 'enable' requires: hostname domain email ip_address [port]\n")
|
|
||||||
show_help()
|
|
||||||
sys.exit(1)
|
|
||||||
hostname = sys.argv[2]
|
|
||||||
domain = sys.argv[3]
|
|
||||||
email = sys.argv[4]
|
|
||||||
ip_address = sys.argv[5]
|
|
||||||
port = sys.argv[6] if len(sys.argv) > 6 else '443'
|
|
||||||
enable_https(hostname, domain, ip_address, email, port)
|
|
||||||
elif command == 'disable':
|
|
||||||
disable_https()
|
|
||||||
elif command == 'show':
|
|
||||||
show_config()
|
|
||||||
elif command in ['help', '-h', '--help']:
|
|
||||||
show_help()
|
|
||||||
else:
|
|
||||||
print(f"\nUnknown command: {command}\n")
|
|
||||||
show_help()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
10
init-data.sh
@@ -4,7 +4,7 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "🔧 Initializing data folder..."
|
echo "🔧 Initializing data folder..."
|
||||||
mkdir -p data/{app,instance,uploads,caddy-data,caddy-config}
|
mkdir -p data/{app,instance,uploads}
|
||||||
|
|
||||||
echo "📁 Copying app folder..."
|
echo "📁 Copying app folder..."
|
||||||
rm -rf data/app
|
rm -rf data/app
|
||||||
@@ -16,14 +16,10 @@ rm -rf data/migrations
|
|||||||
cp -r migrations data/
|
cp -r migrations data/
|
||||||
|
|
||||||
echo "🔧 Copying utility scripts..."
|
echo "🔧 Copying utility scripts..."
|
||||||
cp https_manager.py player_auth_module.py fix_player_user_schema.py data/
|
cp fix_player_user_schema.py data/
|
||||||
|
|
||||||
echo "📄 Copying Caddyfile..."
|
|
||||||
cp Caddyfile data/
|
|
||||||
|
|
||||||
echo "🔐 Setting permissions..."
|
echo "🔐 Setting permissions..."
|
||||||
chmod 755 data/{app,instance,uploads,caddy-data,caddy-config}
|
chmod 755 data/{app,instance,uploads}
|
||||||
chmod 644 data/Caddyfile
|
|
||||||
chmod -R 755 data/app/
|
chmod -R 755 data/app/
|
||||||
find data/app -type f \( -name "*.py" -o -name "*.html" -o -name "*.css" -o -name "*.js" \) -exec chmod 644 {} \;
|
find data/app -type f \( -name "*.py" -o -name "*.html" -o -name "*.css" -o -name "*.js" \) -exec chmod 644 {} \;
|
||||||
chmod 777 data/instance data/uploads
|
chmod 777 data/instance data/uploads
|
||||||
|
|||||||
0
migrations/add_player_user_table.py
Executable file → Normal file
0
migrations/migrate_player_user_global.py
Executable file → Normal file
21
nginx-custom-domains.conf
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Nginx configuration for custom HTTPS domains
|
||||||
|
# This file will be dynamically generated based on HTTPSConfig database entries
|
||||||
|
# Include this in your nginx.conf with: include /etc/nginx/conf.d/custom-domains.conf;
|
||||||
|
|
||||||
|
# Example entry for custom domain:
|
||||||
|
# server {
|
||||||
|
# listen 443 ssl http2;
|
||||||
|
# listen [::]:443 ssl http2;
|
||||||
|
# server_name digiserver.example.com;
|
||||||
|
#
|
||||||
|
# ssl_certificate /etc/nginx/ssl/custom/cert.pem;
|
||||||
|
# ssl_certificate_key /etc/nginx/ssl/custom/key.pem;
|
||||||
|
#
|
||||||
|
# location / {
|
||||||
|
# proxy_pass http://digiserver_app;
|
||||||
|
# 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;
|
||||||
|
# }
|
||||||
|
# }
|
||||||
117
nginx.conf
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
user nginx;
|
||||||
|
worker_processes auto;
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
pid /var/run/nginx.pid;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
use epoll;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
|
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log main;
|
||||||
|
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
tcp_nodelay on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
types_hash_max_size 2048;
|
||||||
|
client_max_body_size 2048M;
|
||||||
|
|
||||||
|
# Gzip compression
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml;
|
||||||
|
|
||||||
|
# Upstream to Flask application
|
||||||
|
upstream digiserver_app {
|
||||||
|
server digiserver-app:5000;
|
||||||
|
keepalive 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTP Server - redirect to HTTPS
|
||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# Allow ACME challenges for Let's Encrypt
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Redirect HTTP to HTTPS for non-ACME requests
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTPS Server (with self-signed cert by default)
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2 default_server;
|
||||||
|
listen [::]:443 ssl http2 default_server;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
# SSL certificate paths (will be volume-mounted)
|
||||||
|
ssl_certificate /etc/nginx/ssl/cert.pem;
|
||||||
|
ssl_certificate_key /etc/nginx/ssl/key.pem;
|
||||||
|
|
||||||
|
# SSL Configuration
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_session_timeout 10m;
|
||||||
|
|
||||||
|
# Security Headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||||
|
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
|
||||||
|
|
||||||
|
# Proxy settings
|
||||||
|
location / {
|
||||||
|
proxy_pass http://digiserver_app;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
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_set_header X-Forwarded-Host $server_name;
|
||||||
|
|
||||||
|
# Timeouts for large uploads
|
||||||
|
proxy_connect_timeout 300s;
|
||||||
|
proxy_send_timeout 300s;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
|
||||||
|
# Buffering
|
||||||
|
proxy_buffering on;
|
||||||
|
proxy_buffer_size 128k;
|
||||||
|
proxy_buffers 4 256k;
|
||||||
|
proxy_busy_buffers_size 256k;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Static files caching
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
proxy_pass http://digiserver_app;
|
||||||
|
proxy_cache_valid 200 60d;
|
||||||
|
expires 60d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Additional server blocks for custom domains can be included here
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
}
|
||||||
0
old_code_documentation/.env.example
Executable file → Normal file
0
old_code_documentation/DOCKER.md
Executable file → Normal file
0
old_code_documentation/HTTPS_SETUP.md
Executable file → Normal file
0
old_code_documentation/IMPLEMENTATION_OPTIONAL_LIBREOFFICE.md
Executable file → Normal file
84
old_code_documentation/NGINX_SETUP_QUICK.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# Quick Start: Nginx Setup for DigiServer v2
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
- SSL certificates in `./data/nginx-ssl/cert.pem` and `./data/nginx-ssl/key.pem`
|
||||||
|
- Docker and Docker Compose installed
|
||||||
|
- Port 80 and 443 available
|
||||||
|
|
||||||
|
## Quick Setup (3 steps)
|
||||||
|
|
||||||
|
### 1. Generate Self-Signed Certificates
|
||||||
|
```bash
|
||||||
|
./generate_nginx_certs.sh localhost 365
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Update Nginx Configuration
|
||||||
|
- Edit `nginx.conf` to set your domain:
|
||||||
|
```nginx
|
||||||
|
server_name localhost; # Change to your domain
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Start Docker Compose
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
### Check if Nginx is running
|
||||||
|
```bash
|
||||||
|
docker ps | grep nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test HTTP → HTTPS redirect
|
||||||
|
```bash
|
||||||
|
curl -L http://localhost
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test HTTPS (with self-signed cert)
|
||||||
|
```bash
|
||||||
|
curl -k https://localhost
|
||||||
|
```
|
||||||
|
|
||||||
|
### View logs
|
||||||
|
```bash
|
||||||
|
docker logs digiserver-nginx
|
||||||
|
docker exec digiserver-nginx tail -f /var/log/nginx/access.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using Production Certificates
|
||||||
|
|
||||||
|
### Option A: Let's Encrypt (Free)
|
||||||
|
1. Install certbot: `apt-get install certbot`
|
||||||
|
2. Generate cert: `certbot certonly --standalone -d your-domain.com`
|
||||||
|
3. Copy cert: `cp /etc/letsencrypt/live/your-domain.com/fullchain.pem ./data/nginx-ssl/cert.pem`
|
||||||
|
4. Copy key: `cp /etc/letsencrypt/live/your-domain.com/privkey.pem ./data/nginx-ssl/key.pem`
|
||||||
|
5. Fix permissions: `sudo chown 101:101 ./data/nginx-ssl/*`
|
||||||
|
6. Reload: `docker exec digiserver-nginx nginx -s reload`
|
||||||
|
|
||||||
|
### Option B: Commercial Certificate
|
||||||
|
1. Place your certificate files in `./data/nginx-ssl/cert.pem` and `./data/nginx-ssl/key.pem`
|
||||||
|
2. Fix permissions: `sudo chown 101:101 ./data/nginx-ssl/*`
|
||||||
|
3. Reload: `docker exec digiserver-nginx nginx -s reload`
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Issue | Solution |
|
||||||
|
|-------|----------|
|
||||||
|
| Port 80/443 in use | `sudo netstat -tlnp \| grep :80` or `:443` |
|
||||||
|
| Certificate permission denied | `sudo chown 101:101 ./data/nginx-ssl/*` |
|
||||||
|
| Nginx won't start | `docker logs digiserver-nginx` |
|
||||||
|
| Connection refused | Check firewall: `sudo ufw allow 80/tcp && sudo ufw allow 443/tcp` |
|
||||||
|
|
||||||
|
## File Locations
|
||||||
|
- Main config: `./nginx.conf`
|
||||||
|
- SSL certs: `./data/nginx-ssl/`
|
||||||
|
- Logs: `./data/nginx-logs/`
|
||||||
|
- Custom domains: `./nginx-custom-domains.conf` (auto-generated)
|
||||||
|
|
||||||
|
## Next: Production Setup
|
||||||
|
1. Update `.env` with your DOMAIN and EMAIL
|
||||||
|
2. Configure HTTPS settings in admin panel
|
||||||
|
3. Run: `python nginx_manager.py generate`
|
||||||
|
4. Test: `docker exec digiserver-nginx nginx -t`
|
||||||
|
5. Reload: `docker exec digiserver-nginx nginx -s reload`
|
||||||