Add HTTPS configuration management system
- Add HTTPSConfig model for managing HTTPS settings - Add admin routes for HTTPS configuration management - Add beautiful admin template for HTTPS configuration - Add database migration for https_config table - Add CLI utility for HTTPS management - Add setup script for automated configuration - Add Caddy configuration generator and manager - Add comprehensive documentation (3 guides) - Add HTTPS Configuration card to admin dashboard - Implement input validation and security features - Add admin-only access control with audit trail - Add real-time configuration preview - Integrate with existing Caddy reverse proxy Features: - Enable/disable HTTPS from web interface - Configure domain, hostname, IP address, port - Automatic SSL certificate management via Let's Encrypt - Real-time Caddyfile generation and reload - Full audit trail with admin username and timestamps - Support for HTTPS and HTTP fallback access points - Beautiful, mobile-responsive UI Modified files: - app/models/__init__.py (added HTTPSConfig import) - app/blueprints/admin.py (added HTTPS routes) - app/templates/admin/admin.html (added HTTPS card) - docker-compose.yml (added Caddyfile mount and admin port) New files: - app/models/https_config.py - app/blueprints/https_config.html - app/utils/caddy_manager.py - https_manager.py - setup_https.sh - migrations/add_https_config_table.py - migrations/add_email_to_https_config.py - HTTPS_STATUS.txt - Documentation files (3 markdown guides)
This commit is contained in:
0
app/blueprints/__init__.py
Normal file → Executable file
0
app/blueprints/__init__.py
Normal file → Executable file
155
app/blueprints/admin.py
Normal file → Executable file
155
app/blueprints/admin.py
Normal file → Executable file
@@ -8,8 +8,9 @@ from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from app.extensions import db, bcrypt
|
||||
from app.models import User, Player, Group, Content, ServerLog, Playlist
|
||||
from app.models import User, Player, Group, Content, ServerLog, Playlist, HTTPSConfig
|
||||
from app.utils.logger import log_action
|
||||
from app.utils.caddy_manager import CaddyConfigGenerator
|
||||
|
||||
admin_bp = Blueprint('admin', __name__, url_prefix='/admin')
|
||||
|
||||
@@ -843,3 +844,155 @@ def delete_editing_user(user_id: int):
|
||||
flash(f'Error deleting user: {str(e)}', 'danger')
|
||||
|
||||
return redirect(url_for('admin.manage_editing_users'))
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# HTTPS Configuration Management Routes
|
||||
# ============================================================================
|
||||
|
||||
@admin_bp.route('/https-config', methods=['GET'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def https_config():
|
||||
"""Display HTTPS configuration management page."""
|
||||
try:
|
||||
config = HTTPSConfig.get_config()
|
||||
|
||||
return render_template('admin/https_config.html',
|
||||
config=config)
|
||||
except Exception as e:
|
||||
log_action('error', f'Error loading HTTPS config page: {str(e)}')
|
||||
flash('Error loading HTTPS configuration page.', 'danger')
|
||||
return redirect(url_for('admin.admin_panel'))
|
||||
|
||||
|
||||
@admin_bp.route('/https-config/update', methods=['POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def update_https_config():
|
||||
"""Update HTTPS configuration."""
|
||||
try:
|
||||
https_enabled = request.form.get('https_enabled') == 'on'
|
||||
hostname = request.form.get('hostname', '').strip()
|
||||
domain = request.form.get('domain', '').strip()
|
||||
ip_address = request.form.get('ip_address', '').strip()
|
||||
email = request.form.get('email', '').strip()
|
||||
port = request.form.get('port', '443').strip()
|
||||
|
||||
# Validation
|
||||
errors = []
|
||||
|
||||
if https_enabled:
|
||||
if not hostname:
|
||||
errors.append('Hostname is required when HTTPS is enabled.')
|
||||
if not domain:
|
||||
errors.append('Domain name is required when HTTPS is enabled.')
|
||||
if not ip_address:
|
||||
errors.append('IP address is required when HTTPS is enabled.')
|
||||
if not email:
|
||||
errors.append('Email address is required when HTTPS is enabled.')
|
||||
|
||||
# Validate domain format (basic)
|
||||
if domain and '.' not in domain:
|
||||
errors.append('Please enter a valid domain name (e.g., example.com).')
|
||||
|
||||
# Validate IP format (basic)
|
||||
if ip_address:
|
||||
ip_parts = ip_address.split('.')
|
||||
if len(ip_parts) != 4:
|
||||
errors.append('Please enter a valid IPv4 address (e.g., 10.76.152.164).')
|
||||
else:
|
||||
try:
|
||||
for part in ip_parts:
|
||||
num = int(part)
|
||||
if num < 0 or num > 255:
|
||||
raise ValueError()
|
||||
except ValueError:
|
||||
errors.append('Please enter a valid IPv4 address.')
|
||||
|
||||
# Validate email format (basic)
|
||||
if email and '@' not in email:
|
||||
errors.append('Please enter a valid email address.')
|
||||
|
||||
# Validate port
|
||||
try:
|
||||
port_num = int(port)
|
||||
if port_num < 1 or port_num > 65535:
|
||||
errors.append('Port must be between 1 and 65535.')
|
||||
port = port_num
|
||||
except ValueError:
|
||||
errors.append('Port must be a valid number.')
|
||||
else:
|
||||
port = 443
|
||||
|
||||
if errors:
|
||||
for error in errors:
|
||||
flash(error, 'warning')
|
||||
return redirect(url_for('admin.https_config'))
|
||||
|
||||
# Update configuration
|
||||
config = HTTPSConfig.create_or_update(
|
||||
https_enabled=https_enabled,
|
||||
hostname=hostname if https_enabled else None,
|
||||
domain=domain if https_enabled else None,
|
||||
ip_address=ip_address if https_enabled else None,
|
||||
email=email if https_enabled else None,
|
||||
port=port if https_enabled else 443,
|
||||
updated_by=current_user.username
|
||||
)
|
||||
|
||||
# Generate and update Caddyfile
|
||||
try:
|
||||
caddyfile_content = CaddyConfigGenerator.generate_caddyfile(config)
|
||||
if CaddyConfigGenerator.write_caddyfile(caddyfile_content):
|
||||
# Reload Caddy configuration
|
||||
if CaddyConfigGenerator.reload_caddy():
|
||||
caddy_status = '✅ Caddy configuration updated successfully!'
|
||||
log_action('info', f'Caddy configuration reloaded by {current_user.username}')
|
||||
else:
|
||||
caddy_status = '⚠️ Caddyfile updated but reload failed. Please restart containers.'
|
||||
log_action('warning', f'Caddy reload failed for {current_user.username}')
|
||||
else:
|
||||
caddy_status = '⚠️ Configuration saved but Caddyfile update failed.'
|
||||
log_action('warning', f'Caddyfile write failed for {current_user.username}')
|
||||
except Exception as caddy_error:
|
||||
caddy_status = f'⚠️ Configuration saved but Caddy update failed: {str(caddy_error)}'
|
||||
log_action('error', f'Caddy update error: {str(caddy_error)}')
|
||||
|
||||
if https_enabled:
|
||||
log_action('info', f'HTTPS enabled by {current_user.username}: domain={domain}, hostname={hostname}, ip={ip_address}, email={email}')
|
||||
flash(f'✅ HTTPS configuration saved successfully!\n{caddy_status}\nServer available at https://{domain}', 'success')
|
||||
else:
|
||||
log_action('info', f'HTTPS disabled by {current_user.username}')
|
||||
flash(f'✅ HTTPS has been disabled. Server running on HTTP only.\n{caddy_status}', 'success')
|
||||
|
||||
return redirect(url_for('admin.https_config'))
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
log_action('error', f'Error updating HTTPS config: {str(e)}')
|
||||
flash(f'Error updating HTTPS configuration: {str(e)}', 'danger')
|
||||
return redirect(url_for('admin.https_config'))
|
||||
|
||||
|
||||
@admin_bp.route('/https-config/status')
|
||||
@login_required
|
||||
@admin_required
|
||||
def https_config_status():
|
||||
"""Get current HTTPS configuration status as JSON."""
|
||||
try:
|
||||
config = HTTPSConfig.get_config()
|
||||
|
||||
if config:
|
||||
return jsonify(config.to_dict())
|
||||
else:
|
||||
return jsonify({
|
||||
'https_enabled': False,
|
||||
'hostname': None,
|
||||
'domain': None,
|
||||
'ip_address': None,
|
||||
'port': 443,
|
||||
})
|
||||
except Exception as e:
|
||||
log_action('error', f'Error getting HTTPS status: {str(e)}')
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
0
app/blueprints/api.py
Normal file → Executable file
0
app/blueprints/api.py
Normal file → Executable file
0
app/blueprints/auth.py
Normal file → Executable file
0
app/blueprints/auth.py
Normal file → Executable file
0
app/blueprints/content.py
Normal file → Executable file
0
app/blueprints/content.py
Normal file → Executable file
0
app/blueprints/content_old.py
Normal file → Executable file
0
app/blueprints/content_old.py
Normal file → Executable file
0
app/blueprints/groups.py
Normal file → Executable file
0
app/blueprints/groups.py
Normal file → Executable file
0
app/blueprints/main.py
Normal file → Executable file
0
app/blueprints/main.py
Normal file → Executable file
0
app/blueprints/players.py
Normal file → Executable file
0
app/blueprints/players.py
Normal file → Executable file
0
app/blueprints/playlist.py
Normal file → Executable file
0
app/blueprints/playlist.py
Normal file → Executable file
Reference in New Issue
Block a user