From 21eb63659a1288f318824a1a79ab2f84e561815b Mon Sep 17 00:00:00 2001 From: root Date: Thu, 15 Jan 2026 22:15:11 +0200 Subject: [PATCH 1/4] feat: complete nginx migration from caddy - Replace Caddy reverse proxy with Nginx (nginx:alpine) - Add nginx.conf with HTTP/HTTPS, gzip, and proxy settings - Add nginx-custom-domains.conf template for custom domains - Update docker-compose.yml to use Nginx service - Add ProxyFix middleware to Flask app for proper header handling - Create nginx_config_reader.py utility to read Nginx configuration - Update admin blueprint to display Nginx status in https_config page - Add Nginx configuration display to https_config.html template - Generate self-signed SSL certificates for localhost - Add utility scripts: generate_nginx_certs.sh - Add documentation: NGINX_SETUP_QUICK.md, PROXY_FIX_SETUP.md - All containers now running, HTTPS working, HTTP redirects to HTTPS - Session cookies marked as Secure - Security headers properly configured --- NGINX_SETUP_QUICK.md | 84 +++++++++++++++++ PROXY_FIX_SETUP.md | 56 +++++++++++ app/app.py | 5 + app/blueprints/admin.py | 7 +- app/config.py | 5 + app/templates/admin/https_config.html | 128 ++++++++++++++++++++++++++ app/utils/nginx_config_reader.py | 120 ++++++++++++++++++++++++ docker-compose.yml | 24 +++-- generate_nginx_certs.sh | 30 ++++++ nginx-custom-domains.conf | 21 +++++ nginx.conf | 117 +++++++++++++++++++++++ 11 files changed, 587 insertions(+), 10 deletions(-) create mode 100644 NGINX_SETUP_QUICK.md create mode 100644 PROXY_FIX_SETUP.md create mode 100644 app/utils/nginx_config_reader.py create mode 100755 generate_nginx_certs.sh create mode 100644 nginx-custom-domains.conf create mode 100644 nginx.conf diff --git a/NGINX_SETUP_QUICK.md b/NGINX_SETUP_QUICK.md new file mode 100644 index 0000000..22e626f --- /dev/null +++ b/NGINX_SETUP_QUICK.md @@ -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` diff --git a/PROXY_FIX_SETUP.md b/PROXY_FIX_SETUP.md new file mode 100644 index 0000000..659f200 --- /dev/null +++ b/PROXY_FIX_SETUP.md @@ -0,0 +1,56 @@ +# ProxyFix Middleware Setup - DigiServer v2 + +## Overview +ProxyFix middleware is now properly configured in the Flask app to handle reverse proxy headers from Nginx (or Caddy). This ensures correct handling of: +- **X-Real-IP**: Client's real IP address +- **X-Forwarded-For**: List of IPs in the proxy chain +- **X-Forwarded-Proto**: Original protocol (http/https) +- **X-Forwarded-Host**: Original hostname + +## Configuration Details + +### Flask App (app/app.py) +```python +from werkzeug.middleware.proxy_fix import ProxyFix + +app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1) +``` + +**Parameters:** +- `x_for=1`: Trust one proxy for X-Forwarded-For header +- `x_proto=1`: Trust proxy for X-Forwarded-Proto header +- `x_host=1`: Trust proxy for X-Forwarded-Host header +- `x_port=1`: Trust proxy for X-Forwarded-Port header + +### Config Settings (app/config.py) + +```python +# Reverse proxy trust (for Nginx/Caddy with ProxyFix middleware) +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') +``` + +## Testing ProxyFix + +### 1. Test Real Client IP +```bash +docker exec digiserver-app flask shell +>>> from flask import request +>>> request.remote_addr # Should show client IP +``` + +### 2. Test URL Scheme +```bash +docker exec digiserver-app flask shell +>>> from flask import url_for +>>> url_for('auth.login', _external=True) # Should use https:// +``` + +## Verification Checklist + +- [x] ProxyFix imported in app.py +- [x] app.wsgi_app wrapped with ProxyFix +- [x] TRUSTED_PROXIES configured +- [x] PREFERRED_URL_SCHEME set to 'https' +- [x] SESSION_COOKIE_SECURE=True in ProductionConfig +- [x] Nginx headers configured correctly diff --git a/app/app.py b/app/app.py index 42e3e51..6c6120c 100755 --- a/app/app.py +++ b/app/app.py @@ -4,6 +4,7 @@ Modern Flask application with blueprint architecture """ import os from flask import Flask, render_template +from werkzeug.middleware.proxy_fix import ProxyFix from dotenv import load_dotenv from app.config import DevelopmentConfig, ProductionConfig, TestingConfig @@ -37,6 +38,10 @@ def create_app(config_name=None): 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 db.init_app(app) bcrypt.init_app(app) diff --git a/app/blueprints/admin.py b/app/blueprints/admin.py index 62fe38a..3898fab 100644 --- a/app/blueprints/admin.py +++ b/app/blueprints/admin.py @@ -11,6 +11,7 @@ from app.extensions import db, bcrypt 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 +from app.utils.nginx_config_reader import get_nginx_status admin_bp = Blueprint('admin', __name__, url_prefix='/admin') @@ -870,10 +871,14 @@ def https_config(): db.session.commit() 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', config=config, is_https_active=is_https_active, - current_host=current_host) + current_host=current_host, + nginx_status=nginx_status) except Exception as e: log_action('error', f'Error loading HTTPS config page: {str(e)}') flash('Error loading HTTPS configuration page.', 'danger') diff --git a/app/config.py b/app/config.py index bcda914..16c162d 100755 --- a/app/config.py +++ b/app/config.py @@ -29,6 +29,11 @@ class Config: SESSION_COOKIE_HTTPONLY = True 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 SEND_FILE_MAX_AGE_DEFAULT = 300 # 5 minutes for static files diff --git a/app/templates/admin/https_config.html b/app/templates/admin/https_config.html index a3012f1..7eb2b7a 100644 --- a/app/templates/admin/https_config.html +++ b/app/templates/admin/https_config.html @@ -160,6 +160,95 @@ + +
+

šŸ”§ Nginx Reverse Proxy Status

+ {% if nginx_status.available %} +
+
+ Status: + āœ… Nginx Configured +
+ +
+ Configuration Path: + {{ nginx_status.path }} +
+ + {% if nginx_status.ssl_enabled %} +
+ SSL/TLS: + šŸ”’ Enabled +
+ {% else %} +
+ SSL/TLS: + āš ļø Not Configured +
+ {% endif %} + + {% if nginx_status.http_ports %} +
+ HTTP Ports: + {{ nginx_status.http_ports|join(', ') }} +
+ {% endif %} + + {% if nginx_status.https_ports %} +
+ HTTPS Ports: + {{ nginx_status.https_ports|join(', ') }} +
+ {% endif %} + + {% if nginx_status.server_names %} +
+ Server Names: + {% for name in nginx_status.server_names %} + {{ name }}{% if not loop.last %}
{% endif %} + {% endfor %} +
+ {% endif %} + + {% if nginx_status.upstream_servers %} +
+ Upstream Servers: + {% for server in nginx_status.upstream_servers %} + {{ server }}{% if not loop.last %}
{% endif %} + {% endfor %} +
+ {% endif %} + + {% if nginx_status.ssl_protocols %} +
+ SSL Protocols: + {{ nginx_status.ssl_protocols|join(', ') }} +
+ {% endif %} + + {% if nginx_status.client_max_body_size %} +
+ Max Body Size: + {{ nginx_status.client_max_body_size }} +
+ {% endif %} + + {% if nginx_status.gzip_enabled %} +
+ Gzip Compression: + āœ… Enabled +
+ {% endif %} +
+ {% else %} +
+

āš ļø Nginx configuration not accessible

+

Error: {{ nginx_status.error|default('Unknown error') }}

+

Path checked: {{ nginx_status.path }}

+
+ {% endif %} +
+

ā„¹ļø Important Information

@@ -466,6 +555,45 @@ 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 { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); diff --git a/app/utils/nginx_config_reader.py b/app/utils/nginx_config_reader.py new file mode 100644 index 0000000..b8f4d6c --- /dev/null +++ b/app/utils/nginx_config_reader.py @@ -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() diff --git a/docker-compose.yml b/docker-compose.yml index c87082a..73d1e4f 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,19 +26,19 @@ services: networks: - digiserver-network - # Caddy reverse proxy with automatic HTTPS - caddy: - image: caddy:2-alpine - container_name: digiserver-caddy + # Nginx reverse proxy with HTTPS support + nginx: + image: nginx:alpine + container_name: digiserver-nginx ports: - "80:80" - "443:443" - - "443:443/udp" # HTTP/3 support - - "2019:2019" # Caddy admin API volumes: - - ./data/Caddyfile:/etc/caddy/Caddyfile:ro - - ./data/caddy-data:/data - - ./data/caddy-config:/config + - ./nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx-custom-domains.conf:/etc/nginx/conf.d/custom-domains.conf:rw + - ./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: - DOMAIN=${DOMAIN:-localhost} - EMAIL=${EMAIL:-admin@localhost} @@ -46,6 +46,12 @@ services: digiserver-app: condition: service_started restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s networks: - digiserver-network diff --git a/generate_nginx_certs.sh b/generate_nginx_certs.sh new file mode 100755 index 0000000..d081f1e --- /dev/null +++ b/generate_nginx_certs.sh @@ -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" diff --git a/nginx-custom-domains.conf b/nginx-custom-domains.conf new file mode 100644 index 0000000..32fc0da --- /dev/null +++ b/nginx-custom-domains.conf @@ -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; +# } +# } diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..c2323bc --- /dev/null +++ b/nginx.conf @@ -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; +} From d17ed79e29d631b552ae857f2c72576dc70a26c9 Mon Sep 17 00:00:00 2001 From: Quality App Developer Date: Thu, 15 Jan 2026 22:25:13 +0200 Subject: [PATCH 2/4] chore: remove caddy-related and obsolete files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed: - Caddyfile: Caddy reverse proxy config (replaced by nginx.conf) - setup_https.sh: Caddy HTTPS setup script - https_manager.py: Caddy HTTPS management utility - HTTPS_STATUS.txt: Old HTTPS documentation - docker-compose.http.yml: HTTP-only Caddy compose file - player_auth_module.py: Old authentication module (unused) - player_config_template.ini: Old player config template (unused) - test connection.txr: Test file Updated: - init-data.sh: Removed references to deleted caddy/obsolete files - .dockerignore: Removed obsolete ignore entries This completes the Caddy → Nginx migration cleanup. --- .dockerignore | 4 +- Caddyfile | 73 ------- HTTPS_STATUS.txt | 413 ------------------------------------- docker-compose.http.yml | 27 --- https_manager.py | 157 -------------- init-data.sh | 10 +- player_auth_module.py | 254 ----------------------- player_config_template.ini | 51 ----- setup_https.sh | 146 ------------- test connection.txr | 26 --- 10 files changed, 4 insertions(+), 1157 deletions(-) delete mode 100755 Caddyfile delete mode 100644 HTTPS_STATUS.txt delete mode 100644 docker-compose.http.yml delete mode 100644 https_manager.py delete mode 100755 player_auth_module.py delete mode 100755 player_config_template.ini delete mode 100755 setup_https.sh delete mode 100644 test connection.txr diff --git a/.dockerignore b/.dockerignore index 50677da..412633d 100755 --- a/.dockerignore +++ b/.dockerignore @@ -52,6 +52,4 @@ PLAYER_AUTH.md PROGRESS.md README.md -# Config templates -player_config_template.ini -player_auth_module.py + diff --git a/Caddyfile b/Caddyfile deleted file mode 100755 index a9a54a7..0000000 --- a/Caddyfile +++ /dev/null @@ -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 -} diff --git a/HTTPS_STATUS.txt b/HTTPS_STATUS.txt deleted file mode 100644 index b2b65e3..0000000 --- a/HTTPS_STATUS.txt +++ /dev/null @@ -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 - -═══════════════════════════════════════════════════════════════════════════════ diff --git a/docker-compose.http.yml b/docker-compose.http.yml deleted file mode 100644 index 797cfc2..0000000 --- a/docker-compose.http.yml +++ /dev/null @@ -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. diff --git a/https_manager.py b/https_manager.py deleted file mode 100644 index 97a3423..0000000 --- a/https_manager.py +++ /dev/null @@ -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 [arguments] - -Commands: - status Show current HTTPS configuration status - enable [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() diff --git a/init-data.sh b/init-data.sh index d5f6873..be2e5e3 100755 --- a/init-data.sh +++ b/init-data.sh @@ -4,7 +4,7 @@ set -e echo "šŸ”§ Initializing data folder..." -mkdir -p data/{app,instance,uploads,caddy-data,caddy-config} +mkdir -p data/{app,instance,uploads} echo "šŸ“ Copying app folder..." rm -rf data/app @@ -16,14 +16,10 @@ rm -rf data/migrations cp -r migrations data/ echo "šŸ”§ Copying utility scripts..." -cp https_manager.py player_auth_module.py fix_player_user_schema.py data/ - -echo "šŸ“„ Copying Caddyfile..." -cp Caddyfile data/ +cp fix_player_user_schema.py data/ echo "šŸ” Setting permissions..." -chmod 755 data/{app,instance,uploads,caddy-data,caddy-config} -chmod 644 data/Caddyfile +chmod 755 data/{app,instance,uploads} chmod -R 755 data/app/ 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 diff --git a/player_auth_module.py b/player_auth_module.py deleted file mode 100755 index 41d719d..0000000 --- a/player_auth_module.py +++ /dev/null @@ -1,254 +0,0 @@ -""" -Player Authentication Module for Kiwy-Signage -Handles authentication with DigiServer v2 and secure config storage -""" -import configparser -import os -import requests -from typing import Optional, Dict, Tuple -import json - - -class PlayerAuth: - """Handle player authentication and configuration management.""" - - def __init__(self, config_path: str = 'player_config.ini'): - """Initialize player authentication. - - Args: - config_path: Path to configuration file - """ - self.config_path = config_path - self.config = configparser.ConfigParser() - self.load_config() - - def load_config(self) -> None: - """Load configuration from file.""" - if os.path.exists(self.config_path): - self.config.read(self.config_path) - else: - # Create default config - self._create_default_config() - - def _create_default_config(self) -> None: - """Create default configuration file.""" - self.config['server'] = { - 'server_url': 'http://localhost:5000' - } - self.config['player'] = { - 'hostname': '', - 'auth_code': '', - 'player_id': '', - 'group_id': '' - } - self.config['display'] = { - 'orientation': 'Landscape', - 'resolution': '1920x1080' - } - self.config['security'] = { - 'verify_ssl': 'true', - 'timeout': '30' - } - self.config['cache'] = { - 'cache_dir': './cache', - 'max_cache_size': '1024' - } - self.config['logging'] = { - 'enabled': 'true', - 'log_level': 'INFO', - 'log_file': './player.log' - } - self.save_config() - - def save_config(self) -> None: - """Save configuration to file.""" - with open(self.config_path, 'w') as f: - self.config.write(f) - - def get_server_url(self) -> str: - """Get server URL from config.""" - return self.config.get('server', 'server_url', fallback='http://localhost:5000') - - def get_hostname(self) -> str: - """Get player hostname from config.""" - return self.config.get('player', 'hostname', fallback='') - - def get_auth_code(self) -> str: - """Get saved auth code from config.""" - return self.config.get('player', 'auth_code', fallback='') - - def is_authenticated(self) -> bool: - """Check if player has valid authentication.""" - return bool(self.get_hostname() and self.get_auth_code()) - - def authenticate(self, hostname: str, password: str = None, - quickconnect_code: str = None) -> Tuple[bool, Optional[str]]: - """Authenticate with server and save credentials. - - Args: - hostname: Player hostname/identifier - password: Player password (optional if using quickconnect) - quickconnect_code: Quick connect code (optional if using password) - - Returns: - Tuple of (success: bool, error_message: Optional[str]) - """ - if not password and not quickconnect_code: - return False, "Password or quick connect code required" - - server_url = self.get_server_url() - - try: - # Make authentication request - response = requests.post( - f"{server_url}/api/auth/player", - json={ - 'hostname': hostname, - 'password': password, - 'quickconnect_code': quickconnect_code - }, - timeout=int(self.config.get('security', 'timeout', fallback='30')), - verify=self.config.getboolean('security', 'verify_ssl', fallback=True) - ) - - if response.status_code == 200: - data = response.json() - - # Save authentication data - self.config['player']['hostname'] = hostname - self.config['player']['auth_code'] = data.get('auth_code', '') - self.config['player']['player_id'] = str(data.get('player_id', '')) - self.config['player']['group_id'] = str(data.get('group_id', '')) - self.config['display']['orientation'] = data.get('orientation', 'Landscape') - - self.save_config() - - return True, None - - else: - error_data = response.json() - return False, error_data.get('error', 'Authentication failed') - - except requests.exceptions.ConnectionError: - return False, "Cannot connect to server" - except requests.exceptions.Timeout: - return False, "Connection timeout" - except Exception as e: - return False, f"Error: {str(e)}" - - def verify_auth(self) -> Tuple[bool, Optional[Dict]]: - """Verify current auth code with server. - - Returns: - Tuple of (valid: bool, player_info: Optional[Dict]) - """ - auth_code = self.get_auth_code() - - if not auth_code: - return False, None - - server_url = self.get_server_url() - - try: - response = requests.post( - f"{server_url}/api/auth/verify", - json={'auth_code': auth_code}, - timeout=int(self.config.get('security', 'timeout', fallback='30')), - verify=self.config.getboolean('security', 'verify_ssl', fallback=True) - ) - - if response.status_code == 200: - data = response.json() - return data.get('valid', False), data - - return False, None - - except Exception: - return False, None - - def get_playlist(self) -> Optional[Dict]: - """Get playlist for this player from server. - - Returns: - Playlist data or None if failed - """ - auth_code = self.get_auth_code() - player_id = self.config.get('player', 'player_id', fallback='') - - if not auth_code or not player_id: - return None - - server_url = self.get_server_url() - - try: - response = requests.get( - f"{server_url}/api/playlists/{player_id}", - headers={'Authorization': f'Bearer {auth_code}'}, - timeout=int(self.config.get('security', 'timeout', fallback='30')), - verify=self.config.getboolean('security', 'verify_ssl', fallback=True) - ) - - if response.status_code == 200: - return response.json() - - return None - - except Exception: - return None - - def send_heartbeat(self, status: str = 'online') -> bool: - """Send heartbeat to server. - - Args: - status: Player status - - Returns: - True if successful, False otherwise - """ - auth_code = self.get_auth_code() - player_id = self.config.get('player', 'player_id', fallback='') - - if not auth_code or not player_id: - return False - - server_url = self.get_server_url() - - try: - response = requests.post( - f"{server_url}/api/players/{player_id}/heartbeat", - headers={'Authorization': f'Bearer {auth_code}'}, - json={'status': status}, - timeout=int(self.config.get('security', 'timeout', fallback='30')), - verify=self.config.getboolean('security', 'verify_ssl', fallback=True) - ) - - return response.status_code == 200 - - except Exception: - return False - - def clear_auth(self) -> None: - """Clear saved authentication data.""" - self.config['player']['auth_code'] = '' - self.config['player']['player_id'] = '' - self.config['player']['group_id'] = '' - self.save_config() - - -# Example usage -if __name__ == '__main__': - auth = PlayerAuth() - - # Check if already authenticated - if auth.is_authenticated(): - print(f"Already authenticated as: {auth.get_hostname()}") - - # Verify authentication - valid, info = auth.verify_auth() - if valid: - print(f"Authentication valid: {info}") - else: - print("Authentication expired or invalid") - else: - print("Not authenticated. Please run authentication:") - print("auth.authenticate(hostname='player-001', password='your_password')") diff --git a/player_config_template.ini b/player_config_template.ini deleted file mode 100755 index c436568..0000000 --- a/player_config_template.ini +++ /dev/null @@ -1,51 +0,0 @@ -# Player Configuration File -# This file is automatically generated and updated by the signage player -# DO NOT EDIT MANUALLY unless you know what you're doing - -[server] -# DigiServer URL (without trailing slash) -server_url = http://localhost:5000 - -[player] -# Player hostname/identifier (must be unique) -hostname = - -# Player authentication code (obtained after first authentication) -auth_code = - -# Player ID (assigned by server) -player_id = - -# Group ID (assigned by server) -group_id = - -[display] -# Display orientation: Landscape or Portrait -orientation = Landscape - -# Screen resolution (width x height) -resolution = 1920x1080 - -[security] -# Enable SSL certificate verification -verify_ssl = true - -# Connection timeout in seconds -timeout = 30 - -[cache] -# Local cache directory for downloaded content -cache_dir = ./cache - -# Maximum cache size in MB -max_cache_size = 1024 - -[logging] -# Enable logging -enabled = true - -# Log level: DEBUG, INFO, WARNING, ERROR -log_level = INFO - -# Log file path -log_file = ./player.log diff --git a/setup_https.sh b/setup_https.sh deleted file mode 100755 index 49ea46e..0000000 --- a/setup_https.sh +++ /dev/null @@ -1,146 +0,0 @@ -#!/bin/bash -# Setup script for HTTPS Configuration Management and complete system initialization -# This script can be run locally or on a new deployment PC - -set -e # Exit on any error - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Determine if we're running in Docker or on host -IN_DOCKER=false -if [ -f /.dockerenv ]; then - IN_DOCKER=true -fi - -echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}" -echo -e "${BLUE}ā•‘ DigiServer HTTPS Configuration Setup ā•‘${NC}" -echo -e "${BLUE}ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•${NC}" -echo "" - -# ============================================================================ -# OPTION 1: Running inside Docker container -# ============================================================================ -if [ "$IN_DOCKER" = true ]; then - echo -e "${YELLOW}šŸ“ Running inside Docker container${NC}" - echo "" - - echo -e "${BLUE}Step 1/4:${NC} ${YELLOW}Creating https_config database table...${NC}" - python /app/migrations/add_https_config_table.py - echo -e "${GREEN}āœ… https_config table created${NC}" - echo "" - - echo -e "${BLUE}Step 2/4:${NC} ${YELLOW}Creating player_user database table...${NC}" - python /app/migrations/add_player_user_table.py - echo -e "${GREEN}āœ… player_user table created${NC}" - echo "" - - echo -e "${BLUE}Step 3/4:${NC} ${YELLOW}Adding email column to https_config...${NC}" - python /app/migrations/add_email_to_https_config.py - echo -e "${GREEN}āœ… Email column added${NC}" - echo "" - - echo -e "${BLUE}Step 4/4:${NC} ${YELLOW}Migrating player_user global settings...${NC}" - python /app/migrations/migrate_player_user_global.py - echo -e "${GREEN}āœ… Player user migration completed${NC}" - echo "" - - echo -e "${GREEN}āœ… HTTPS Configuration Management is now ready!${NC}" - echo "" - echo -e "${BLUE}ā„¹ļø Next steps:${NC}" - echo "1. Log in to the admin panel" - echo "2. Navigate to: Admin Panel → šŸ”’ HTTPS Configuration" - echo "3. Configure your HTTPS settings:" - echo " - Enable HTTPS" - echo " - Set hostname: digiserver" - echo " - Set domain: digiserver.sibiusb.harting.intra" - echo " - Set IP: 10.76.152.164" - echo "4. Save the configuration" - echo "" - exit 0 -fi - -# ============================================================================ -# OPTION 2: Running on host machine with docker-compose -# ============================================================================ -echo -e "${YELLOW}šŸ“ Running on host machine${NC}" -echo "" - -# Check if docker-compose is available -if ! command -v docker-compose &> /dev/null; then - echo -e "${RED}āŒ docker-compose not found!${NC}" - exit 1 -fi - -# Check if we're in the project directory -if [ ! -f "docker-compose.yml" ]; then - echo -e "${RED}āŒ docker-compose.yml not found!${NC}" - echo "Please run this script from the digiserver-v2 directory" - exit 1 -fi - -echo -e "${BLUE}Checking container status...${NC}" -RUNNING=$(docker-compose ps -q digiserver-app 2>/dev/null || echo "") -if [ -z "$RUNNING" ]; then - echo -e "${YELLOW}šŸ“¦ Starting containers...${NC}" - docker-compose up -d - echo -e "${YELLOW}ā³ Waiting for containers to be ready...${NC}" - sleep 5 -fi - -echo -e "${GREEN}āœ… Containers are running${NC}" -echo "" - -# Run all migrations -echo -e "${BLUE}Step 1/5:${NC} ${YELLOW}Creating https_config database table...${NC}" -docker-compose exec -T digiserver-app python /app/migrations/add_https_config_table.py -echo -e "${GREEN}āœ… https_config table created${NC}" -echo "" - -echo -e "${BLUE}Step 2/5:${NC} ${YELLOW}Creating player_user database table...${NC}" -docker-compose exec -T digiserver-app python /app/migrations/add_player_user_table.py -echo -e "${GREEN}āœ… player_user table created${NC}" -echo "" - -echo -e "${BLUE}Step 3/5:${NC} ${YELLOW}Adding email column to https_config...${NC}" -docker-compose exec -T digiserver-app python /app/migrations/add_email_to_https_config.py -echo -e "${GREEN}āœ… Email column added${NC}" -echo "" - -echo -e "${BLUE}Step 4/5:${NC} ${YELLOW}Migrating player_user global settings...${NC}" -docker-compose exec -T digiserver-app python /app/migrations/migrate_player_user_global.py -echo -e "${GREEN}āœ… Player user migration completed${NC}" -echo "" - -# Configure HTTPS automatically -echo -e "${BLUE}Step 5/5:${NC} ${YELLOW}Configuring HTTPS settings...${NC}" -docker-compose exec -T digiserver-app python /app/https_manager.py enable \ - digiserver \ - digiserver.sibiusb.harting.intra \ - admin@example.com \ - 10.76.152.164 \ - 443 - -echo -e "${GREEN}āœ… HTTPS configured successfully${NC}" -echo "" - -# Display configuration status -echo -e "${BLUE}Configuration Status:${NC}" -docker-compose exec -T digiserver-app python /app/https_manager.py status -echo "" - -echo -e "${GREEN}╔════════════════════════════════════════════════════════════════╗${NC}" -echo -e "${GREEN}ā•‘ šŸŽ‰ Deployment Complete! ā•‘${NC}" -echo -e "${GREEN}ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•${NC}" -echo "" -echo -e "${BLUE}Access your application at:${NC}" -echo " šŸ”’ https://digiserver" -echo " šŸ”’ https://10.76.152.164" -echo " šŸ”’ https://digiserver.sibiusb.harting.intra" -echo "" -echo -e "${BLUE}šŸ“– For more deployment commands, see: DEPLOYMENT_COMMANDS.md${NC}" -echo "" diff --git a/test connection.txr b/test connection.txr deleted file mode 100644 index 7670378..0000000 --- a/test connection.txr +++ /dev/null @@ -1,26 +0,0 @@ -# 1. Add virtual IP -sudo ip addr add 10.76.152.164/24 dev wlp0s20f3 - -# 2. Wait a moment for the interface to be ready -sleep 2 - -# 3. Test HTTP (should redirect to HTTPS) -echo "Testing HTTP:" -curl -i http://10.76.152.164 - -# 4. Test HTTPS -echo "Testing HTTPS:" -curl -k https://10.76.152.164 | head -20 - -# 5. When done, remove the alias -sudo ip addr del 10.76.152.164/24 dev wlp0s20f3 - - - -echo "šŸ“‹ Step 1: Add virtual IP alias 10.76.152.164 to wlp0s20f3" && \ -sudo ip addr add 10.76.152.164/24 dev wlp0s20f3 && \ -echo "āœ… Virtual IP added" && \ -sleep 2 && \ -echo "" && \ -echo "šŸ“‹ Step 2: Verify the virtual IP is active" && \ -ip addr show wlp0s20f3 | grep "inet " \ No newline at end of file From 024430754ce9e62a4e0f7a016eeb47afbd140f8b Mon Sep 17 00:00:00 2001 From: Quality App Developer Date: Thu, 15 Jan 2026 22:31:37 +0200 Subject: [PATCH 3/4] docs: add comprehensive quick deployment guide - Complete deployment workflow documentation - Container architecture explanation - Step-by-step deployment process - Common commands reference - Troubleshooting section - SSL certificate management - Performance tuning guide - Security checklist - Health checks information - Database backup/restore procedures --- QUICK_DEPLOYMENT.md | 487 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 487 insertions(+) create mode 100644 QUICK_DEPLOYMENT.md diff --git a/QUICK_DEPLOYMENT.md b/QUICK_DEPLOYMENT.md new file mode 100644 index 0000000..cfea467 --- /dev/null +++ b/QUICK_DEPLOYMENT.md @@ -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://` (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 +``` + +--- + +## šŸ“Š 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 From a4262da7c93f4708d64ea769978ac561b6d3f845 Mon Sep 17 00:00:00 2001 From: Quality App Developer Date: Thu, 15 Jan 2026 22:39:51 +0200 Subject: [PATCH 4/4] chore: fix file permissions and ownership across project - Changed ownership of all files to scheianu:scheianu - Set directories to 755 permissions (rwxr-xr-x) - Set files to 644 permissions (rw-r--r--) - Made shell scripts executable (755) - Allows development without requiring sudo for file modifications - Improves development workflow and security --- .dockerignore | 0 .gitignore | 0 Dockerfile | 0 QUICK_START.md | 120 ------------------ app/app.py | 0 app/blueprints/__init__.py | 0 app/blueprints/api.py | 0 app/blueprints/auth.py | 0 app/blueprints/content.py | 0 app/blueprints/content_old.py | 0 app/blueprints/groups.py | 0 app/blueprints/main.py | 0 app/blueprints/players.py | 0 app/blueprints/playlist.py | 0 app/config.py | 0 app/extensions.py | 0 app/models/__init__.py | 0 app/models/content.py | 0 app/models/group.py | 0 app/models/player.py | 0 app/models/player_edit.py | 0 app/models/player_feedback.py | 0 app/models/player_user.py | 0 app/models/playlist.py | 0 app/models/server_log.py | 0 app/models/user.py | 0 app/static/icons/edit.svg | 0 app/static/icons/home.svg | 0 app/static/icons/info.svg | 0 app/static/icons/monitor.svg | 0 app/static/icons/moon.svg | 0 app/static/icons/playlist.svg | 0 app/static/icons/sun.svg | 0 app/static/icons/trash.svg | 0 app/static/icons/upload.svg | 0 app/static/icons/warning.svg | 0 app/static/uploads/.gitkeep | 0 app/templates/admin/admin.html | 0 app/templates/admin/customize_logos.html | 0 app/templates/admin/dependencies.html | 0 app/templates/admin/editing_users.html | 0 app/templates/admin/leftover_media.html | 0 app/templates/admin/user_management.html | 0 app/templates/auth/change_password.html | 0 app/templates/auth/login.html | 0 app/templates/auth/register.html | 0 app/templates/base.html | 0 app/templates/content/content_list.html | 0 app/templates/content/content_list_new.html | 0 app/templates/content/edit_content.html | 0 .../content/manage_playlist_content.html | 0 app/templates/content/media_library.html | 0 app/templates/content/upload_content.html | 0 app/templates/content/upload_media.html | 0 app/templates/dashboard.html | 0 app/templates/errors/403.html | 0 app/templates/errors/404.html | 0 app/templates/errors/500.html | 0 app/templates/groups/create_group.html | 0 app/templates/groups/edit_group.html | 0 app/templates/groups/group_fullscreen.html | 0 app/templates/groups/groups_list.html | 0 app/templates/groups/manage_group.html | 0 app/templates/players/add_player.html | 0 app/templates/players/edit_player.html | 0 app/templates/players/edited_media.html | 0 app/templates/players/manage_player.html | 0 app/templates/players/player_fullscreen.html | 0 app/templates/players/player_page.html | 0 app/templates/players/players_list.html | 0 app/templates/playlist/manage_playlist.html | 0 app/utils/__init__.py | 0 app/utils/group_player_management.py | 0 app/utils/logger.py | 0 app/utils/pptx_converter.py | 0 app/utils/uploads.py | 0 docker-compose.yml | 0 fix_player_user_schema.py | 0 migrations/add_player_user_table.py | 0 migrations/migrate_player_user_global.py | 0 old_code_documentation/.env.example | 0 .../DATA_DEPLOYMENT.md | 0 old_code_documentation/DOCKER.md | 0 old_code_documentation/HTTPS_SETUP.md | 0 .../IMPLEMENTATION_OPTIONAL_LIBREOFFICE.md | 0 .../NGINX_SETUP_QUICK.md | 0 .../OPTIONAL_DEPENDENCIES.md | 0 .../PLAYER_EDIT_MEDIA_API.md | 0 .../PROXY_FIX_SETUP.md | 0 old_code_documentation/README.md | 0 old_code_documentation/add_muted_column.py | 0 old_code_documentation/check_fix_player.py | 0 .../migrate_add_edit_enabled.py | 0 requirements.txt | 0 94 files changed, 120 deletions(-) mode change 100755 => 100644 .dockerignore mode change 100755 => 100644 .gitignore mode change 100755 => 100644 Dockerfile delete mode 100644 QUICK_START.md mode change 100755 => 100644 app/app.py mode change 100755 => 100644 app/blueprints/__init__.py mode change 100755 => 100644 app/blueprints/api.py mode change 100755 => 100644 app/blueprints/auth.py mode change 100755 => 100644 app/blueprints/content.py mode change 100755 => 100644 app/blueprints/content_old.py mode change 100755 => 100644 app/blueprints/groups.py mode change 100755 => 100644 app/blueprints/main.py mode change 100755 => 100644 app/blueprints/players.py mode change 100755 => 100644 app/blueprints/playlist.py mode change 100755 => 100644 app/config.py mode change 100755 => 100644 app/extensions.py mode change 100755 => 100644 app/models/__init__.py mode change 100755 => 100644 app/models/content.py mode change 100755 => 100644 app/models/group.py mode change 100755 => 100644 app/models/player.py mode change 100755 => 100644 app/models/player_edit.py mode change 100755 => 100644 app/models/player_feedback.py mode change 100755 => 100644 app/models/player_user.py mode change 100755 => 100644 app/models/playlist.py mode change 100755 => 100644 app/models/server_log.py mode change 100755 => 100644 app/models/user.py mode change 100755 => 100644 app/static/icons/edit.svg mode change 100755 => 100644 app/static/icons/home.svg mode change 100755 => 100644 app/static/icons/info.svg mode change 100755 => 100644 app/static/icons/monitor.svg mode change 100755 => 100644 app/static/icons/moon.svg mode change 100755 => 100644 app/static/icons/playlist.svg mode change 100755 => 100644 app/static/icons/sun.svg mode change 100755 => 100644 app/static/icons/trash.svg mode change 100755 => 100644 app/static/icons/upload.svg mode change 100755 => 100644 app/static/icons/warning.svg mode change 100755 => 100644 app/static/uploads/.gitkeep mode change 100755 => 100644 app/templates/admin/admin.html mode change 100755 => 100644 app/templates/admin/customize_logos.html mode change 100755 => 100644 app/templates/admin/dependencies.html mode change 100755 => 100644 app/templates/admin/editing_users.html mode change 100755 => 100644 app/templates/admin/leftover_media.html mode change 100755 => 100644 app/templates/admin/user_management.html mode change 100755 => 100644 app/templates/auth/change_password.html mode change 100755 => 100644 app/templates/auth/login.html mode change 100755 => 100644 app/templates/auth/register.html mode change 100755 => 100644 app/templates/base.html mode change 100755 => 100644 app/templates/content/content_list.html mode change 100755 => 100644 app/templates/content/content_list_new.html mode change 100755 => 100644 app/templates/content/edit_content.html mode change 100755 => 100644 app/templates/content/manage_playlist_content.html mode change 100755 => 100644 app/templates/content/media_library.html mode change 100755 => 100644 app/templates/content/upload_content.html mode change 100755 => 100644 app/templates/content/upload_media.html mode change 100755 => 100644 app/templates/dashboard.html mode change 100755 => 100644 app/templates/errors/403.html mode change 100755 => 100644 app/templates/errors/404.html mode change 100755 => 100644 app/templates/errors/500.html mode change 100755 => 100644 app/templates/groups/create_group.html mode change 100755 => 100644 app/templates/groups/edit_group.html mode change 100755 => 100644 app/templates/groups/group_fullscreen.html mode change 100755 => 100644 app/templates/groups/groups_list.html mode change 100755 => 100644 app/templates/groups/manage_group.html mode change 100755 => 100644 app/templates/players/add_player.html mode change 100755 => 100644 app/templates/players/edit_player.html mode change 100755 => 100644 app/templates/players/edited_media.html mode change 100755 => 100644 app/templates/players/manage_player.html mode change 100755 => 100644 app/templates/players/player_fullscreen.html mode change 100755 => 100644 app/templates/players/player_page.html mode change 100755 => 100644 app/templates/players/players_list.html mode change 100755 => 100644 app/templates/playlist/manage_playlist.html mode change 100755 => 100644 app/utils/__init__.py mode change 100755 => 100644 app/utils/group_player_management.py mode change 100755 => 100644 app/utils/logger.py mode change 100755 => 100644 app/utils/pptx_converter.py mode change 100755 => 100644 app/utils/uploads.py mode change 100755 => 100644 docker-compose.yml mode change 100755 => 100644 fix_player_user_schema.py mode change 100755 => 100644 migrations/add_player_user_table.py mode change 100755 => 100644 migrations/migrate_player_user_global.py mode change 100755 => 100644 old_code_documentation/.env.example rename DATA_DEPLOYMENT.md => old_code_documentation/DATA_DEPLOYMENT.md (100%) mode change 100755 => 100644 old_code_documentation/DOCKER.md mode change 100755 => 100644 old_code_documentation/HTTPS_SETUP.md mode change 100755 => 100644 old_code_documentation/IMPLEMENTATION_OPTIONAL_LIBREOFFICE.md rename NGINX_SETUP_QUICK.md => old_code_documentation/NGINX_SETUP_QUICK.md (100%) mode change 100755 => 100644 old_code_documentation/OPTIONAL_DEPENDENCIES.md mode change 100755 => 100644 old_code_documentation/PLAYER_EDIT_MEDIA_API.md rename PROXY_FIX_SETUP.md => old_code_documentation/PROXY_FIX_SETUP.md (100%) mode change 100755 => 100644 old_code_documentation/README.md mode change 100755 => 100644 old_code_documentation/add_muted_column.py mode change 100755 => 100644 old_code_documentation/check_fix_player.py mode change 100755 => 100644 old_code_documentation/migrate_add_edit_enabled.py mode change 100755 => 100644 requirements.txt diff --git a/.dockerignore b/.dockerignore old mode 100755 new mode 100644 diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 diff --git a/Dockerfile b/Dockerfile old mode 100755 new mode 100644 diff --git a/QUICK_START.md b/QUICK_START.md deleted file mode 100644 index 32a8ca1..0000000 --- a/QUICK_START.md +++ /dev/null @@ -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` šŸš€ diff --git a/app/app.py b/app/app.py old mode 100755 new mode 100644 diff --git a/app/blueprints/__init__.py b/app/blueprints/__init__.py old mode 100755 new mode 100644 diff --git a/app/blueprints/api.py b/app/blueprints/api.py old mode 100755 new mode 100644 diff --git a/app/blueprints/auth.py b/app/blueprints/auth.py old mode 100755 new mode 100644 diff --git a/app/blueprints/content.py b/app/blueprints/content.py old mode 100755 new mode 100644 diff --git a/app/blueprints/content_old.py b/app/blueprints/content_old.py old mode 100755 new mode 100644 diff --git a/app/blueprints/groups.py b/app/blueprints/groups.py old mode 100755 new mode 100644 diff --git a/app/blueprints/main.py b/app/blueprints/main.py old mode 100755 new mode 100644 diff --git a/app/blueprints/players.py b/app/blueprints/players.py old mode 100755 new mode 100644 diff --git a/app/blueprints/playlist.py b/app/blueprints/playlist.py old mode 100755 new mode 100644 diff --git a/app/config.py b/app/config.py old mode 100755 new mode 100644 diff --git a/app/extensions.py b/app/extensions.py old mode 100755 new mode 100644 diff --git a/app/models/__init__.py b/app/models/__init__.py old mode 100755 new mode 100644 diff --git a/app/models/content.py b/app/models/content.py old mode 100755 new mode 100644 diff --git a/app/models/group.py b/app/models/group.py old mode 100755 new mode 100644 diff --git a/app/models/player.py b/app/models/player.py old mode 100755 new mode 100644 diff --git a/app/models/player_edit.py b/app/models/player_edit.py old mode 100755 new mode 100644 diff --git a/app/models/player_feedback.py b/app/models/player_feedback.py old mode 100755 new mode 100644 diff --git a/app/models/player_user.py b/app/models/player_user.py old mode 100755 new mode 100644 diff --git a/app/models/playlist.py b/app/models/playlist.py old mode 100755 new mode 100644 diff --git a/app/models/server_log.py b/app/models/server_log.py old mode 100755 new mode 100644 diff --git a/app/models/user.py b/app/models/user.py old mode 100755 new mode 100644 diff --git a/app/static/icons/edit.svg b/app/static/icons/edit.svg old mode 100755 new mode 100644 diff --git a/app/static/icons/home.svg b/app/static/icons/home.svg old mode 100755 new mode 100644 diff --git a/app/static/icons/info.svg b/app/static/icons/info.svg old mode 100755 new mode 100644 diff --git a/app/static/icons/monitor.svg b/app/static/icons/monitor.svg old mode 100755 new mode 100644 diff --git a/app/static/icons/moon.svg b/app/static/icons/moon.svg old mode 100755 new mode 100644 diff --git a/app/static/icons/playlist.svg b/app/static/icons/playlist.svg old mode 100755 new mode 100644 diff --git a/app/static/icons/sun.svg b/app/static/icons/sun.svg old mode 100755 new mode 100644 diff --git a/app/static/icons/trash.svg b/app/static/icons/trash.svg old mode 100755 new mode 100644 diff --git a/app/static/icons/upload.svg b/app/static/icons/upload.svg old mode 100755 new mode 100644 diff --git a/app/static/icons/warning.svg b/app/static/icons/warning.svg old mode 100755 new mode 100644 diff --git a/app/static/uploads/.gitkeep b/app/static/uploads/.gitkeep old mode 100755 new mode 100644 diff --git a/app/templates/admin/admin.html b/app/templates/admin/admin.html old mode 100755 new mode 100644 diff --git a/app/templates/admin/customize_logos.html b/app/templates/admin/customize_logos.html old mode 100755 new mode 100644 diff --git a/app/templates/admin/dependencies.html b/app/templates/admin/dependencies.html old mode 100755 new mode 100644 diff --git a/app/templates/admin/editing_users.html b/app/templates/admin/editing_users.html old mode 100755 new mode 100644 diff --git a/app/templates/admin/leftover_media.html b/app/templates/admin/leftover_media.html old mode 100755 new mode 100644 diff --git a/app/templates/admin/user_management.html b/app/templates/admin/user_management.html old mode 100755 new mode 100644 diff --git a/app/templates/auth/change_password.html b/app/templates/auth/change_password.html old mode 100755 new mode 100644 diff --git a/app/templates/auth/login.html b/app/templates/auth/login.html old mode 100755 new mode 100644 diff --git a/app/templates/auth/register.html b/app/templates/auth/register.html old mode 100755 new mode 100644 diff --git a/app/templates/base.html b/app/templates/base.html old mode 100755 new mode 100644 diff --git a/app/templates/content/content_list.html b/app/templates/content/content_list.html old mode 100755 new mode 100644 diff --git a/app/templates/content/content_list_new.html b/app/templates/content/content_list_new.html old mode 100755 new mode 100644 diff --git a/app/templates/content/edit_content.html b/app/templates/content/edit_content.html old mode 100755 new mode 100644 diff --git a/app/templates/content/manage_playlist_content.html b/app/templates/content/manage_playlist_content.html old mode 100755 new mode 100644 diff --git a/app/templates/content/media_library.html b/app/templates/content/media_library.html old mode 100755 new mode 100644 diff --git a/app/templates/content/upload_content.html b/app/templates/content/upload_content.html old mode 100755 new mode 100644 diff --git a/app/templates/content/upload_media.html b/app/templates/content/upload_media.html old mode 100755 new mode 100644 diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html old mode 100755 new mode 100644 diff --git a/app/templates/errors/403.html b/app/templates/errors/403.html old mode 100755 new mode 100644 diff --git a/app/templates/errors/404.html b/app/templates/errors/404.html old mode 100755 new mode 100644 diff --git a/app/templates/errors/500.html b/app/templates/errors/500.html old mode 100755 new mode 100644 diff --git a/app/templates/groups/create_group.html b/app/templates/groups/create_group.html old mode 100755 new mode 100644 diff --git a/app/templates/groups/edit_group.html b/app/templates/groups/edit_group.html old mode 100755 new mode 100644 diff --git a/app/templates/groups/group_fullscreen.html b/app/templates/groups/group_fullscreen.html old mode 100755 new mode 100644 diff --git a/app/templates/groups/groups_list.html b/app/templates/groups/groups_list.html old mode 100755 new mode 100644 diff --git a/app/templates/groups/manage_group.html b/app/templates/groups/manage_group.html old mode 100755 new mode 100644 diff --git a/app/templates/players/add_player.html b/app/templates/players/add_player.html old mode 100755 new mode 100644 diff --git a/app/templates/players/edit_player.html b/app/templates/players/edit_player.html old mode 100755 new mode 100644 diff --git a/app/templates/players/edited_media.html b/app/templates/players/edited_media.html old mode 100755 new mode 100644 diff --git a/app/templates/players/manage_player.html b/app/templates/players/manage_player.html old mode 100755 new mode 100644 diff --git a/app/templates/players/player_fullscreen.html b/app/templates/players/player_fullscreen.html old mode 100755 new mode 100644 diff --git a/app/templates/players/player_page.html b/app/templates/players/player_page.html old mode 100755 new mode 100644 diff --git a/app/templates/players/players_list.html b/app/templates/players/players_list.html old mode 100755 new mode 100644 diff --git a/app/templates/playlist/manage_playlist.html b/app/templates/playlist/manage_playlist.html old mode 100755 new mode 100644 diff --git a/app/utils/__init__.py b/app/utils/__init__.py old mode 100755 new mode 100644 diff --git a/app/utils/group_player_management.py b/app/utils/group_player_management.py old mode 100755 new mode 100644 diff --git a/app/utils/logger.py b/app/utils/logger.py old mode 100755 new mode 100644 diff --git a/app/utils/pptx_converter.py b/app/utils/pptx_converter.py old mode 100755 new mode 100644 diff --git a/app/utils/uploads.py b/app/utils/uploads.py old mode 100755 new mode 100644 diff --git a/docker-compose.yml b/docker-compose.yml old mode 100755 new mode 100644 diff --git a/fix_player_user_schema.py b/fix_player_user_schema.py old mode 100755 new mode 100644 diff --git a/migrations/add_player_user_table.py b/migrations/add_player_user_table.py old mode 100755 new mode 100644 diff --git a/migrations/migrate_player_user_global.py b/migrations/migrate_player_user_global.py old mode 100755 new mode 100644 diff --git a/old_code_documentation/.env.example b/old_code_documentation/.env.example old mode 100755 new mode 100644 diff --git a/DATA_DEPLOYMENT.md b/old_code_documentation/DATA_DEPLOYMENT.md similarity index 100% rename from DATA_DEPLOYMENT.md rename to old_code_documentation/DATA_DEPLOYMENT.md diff --git a/old_code_documentation/DOCKER.md b/old_code_documentation/DOCKER.md old mode 100755 new mode 100644 diff --git a/old_code_documentation/HTTPS_SETUP.md b/old_code_documentation/HTTPS_SETUP.md old mode 100755 new mode 100644 diff --git a/old_code_documentation/IMPLEMENTATION_OPTIONAL_LIBREOFFICE.md b/old_code_documentation/IMPLEMENTATION_OPTIONAL_LIBREOFFICE.md old mode 100755 new mode 100644 diff --git a/NGINX_SETUP_QUICK.md b/old_code_documentation/NGINX_SETUP_QUICK.md similarity index 100% rename from NGINX_SETUP_QUICK.md rename to old_code_documentation/NGINX_SETUP_QUICK.md diff --git a/old_code_documentation/OPTIONAL_DEPENDENCIES.md b/old_code_documentation/OPTIONAL_DEPENDENCIES.md old mode 100755 new mode 100644 diff --git a/old_code_documentation/PLAYER_EDIT_MEDIA_API.md b/old_code_documentation/PLAYER_EDIT_MEDIA_API.md old mode 100755 new mode 100644 diff --git a/PROXY_FIX_SETUP.md b/old_code_documentation/PROXY_FIX_SETUP.md similarity index 100% rename from PROXY_FIX_SETUP.md rename to old_code_documentation/PROXY_FIX_SETUP.md diff --git a/old_code_documentation/README.md b/old_code_documentation/README.md old mode 100755 new mode 100644 diff --git a/old_code_documentation/add_muted_column.py b/old_code_documentation/add_muted_column.py old mode 100755 new mode 100644 diff --git a/old_code_documentation/check_fix_player.py b/old_code_documentation/check_fix_player.py old mode 100755 new mode 100644 diff --git a/old_code_documentation/migrate_add_edit_enabled.py b/old_code_documentation/migrate_add_edit_enabled.py old mode 100755 new mode 100644 diff --git a/requirements.txt b/requirements.txt old mode 100755 new mode 100644