diff --git a/Dockerfile b/Dockerfile index ff46220..914292b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -96,7 +96,10 @@ RUN echo '#!/bin/bash' > /usr/local/bin/start-app.sh \ && echo '# Wait for database' >> /usr/local/bin/start-app.sh \ && echo 'echo "Waiting for database connection..."' >> /usr/local/bin/start-app.sh \ && echo 'until mariadb -h"$DB_HOST" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" --skip-ssl -e "SELECT 1;" >/dev/null 2>&1; do' >> /usr/local/bin/start-app.sh \ - && echo ' echo "Database not ready, waiting 2 seconds..."' >> /usr/local/bin/start-app.sh \ + && echo ' echo "Database not ready. Trying: mariadb -h$DB_HOST -u$DB_USER -p[HIDDEN] $DB_NAME --skip-ssl"' >> /usr/local/bin/start-app.sh \ + && echo ' mariadb -h"$DB_HOST" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" --skip-ssl -e "SELECT 1;" 2>&1 | head -3' >> /usr/local/bin/start-app.sh \ + && echo ' echo "^ If you see access denied error, run: docker compose down -v && docker compose up -d"' >> /usr/local/bin/start-app.sh \ + && echo ' echo "Waiting 2 seconds..."' >> /usr/local/bin/start-app.sh \ && echo ' sleep 2' >> /usr/local/bin/start-app.sh \ && echo 'done' >> /usr/local/bin/start-app.sh \ && echo 'echo "Database connected successfully!"' >> /usr/local/bin/start-app.sh \ diff --git a/docker-compose.yml b/docker-compose.yml index e80c35c..baa72b1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -200,9 +200,12 @@ services: entrypoint: ["tail", "-f", "/dev/null"] # Keep container running for interactive use networks: + # Auto-managed bridge network for maximum compatibility + # Docker automatically handles service discovery, subnet assignment, and conflict avoidance + # Containers can communicate using service names (database, app, caddy, etc.) accounting_network: driver: bridge - name: accounting_panel_network + # No custom name or subnet - lets Docker handle everything automatically volumes: db_data: diff --git a/docker/caddy/Caddyfile b/docker/caddy/Caddyfile index d4cd210..f901f93 100644 --- a/docker/caddy/Caddyfile +++ b/docker/caddy/Caddyfile @@ -1,5 +1,10 @@ +# Caddy Configuration for PersonalAccounter +# This is the default configuration for development/manual setup +# The setup.sh script will replace this with domain-specific configuration + { admin off + local_certs } # HTTP server - works on both ports 80 and 8080 @@ -23,10 +28,14 @@ php_fastcgi app:9000 } -# HTTPS server - default port 443 and 8443 +# HTTPS server - default port 443 and 8443 with self-signed certificate :443, :8443 { root * /var/www/html/public + # Self-signed TLS certificate for development/testing + # Note: setup.sh will configure proper Let's Encrypt or domain-specific SSL + tls internal + # Security headers header { X-Content-Type-Options nosniff diff --git a/network-check.sh b/network-check.sh index d4dc873..edbe265 100755 --- a/network-check.sh +++ b/network-check.sh @@ -57,16 +57,19 @@ check_network_conflicts() { suggest_alternatives() { echo -e "${BLUE}🛠️ Network Configuration Options:${NC}" echo "" - echo -e "${GREEN}1. Current Configuration (Customizable):${NC}" + echo -e "${GREEN}1. Auto-Managed (Recommended):${NC}" + echo " • Let Docker choose subnet automatically" + echo " • Automatic service discovery (database, app, etc.)" + echo " • Best for: Most deployments, avoiding conflicts" + echo " • Current status: $(if grep -q 'name:' docker-compose.yml 2>/dev/null; then echo 'Not active'; else echo 'Active'; fi)" + echo "" + echo -e "${GREEN}2. Custom Subnet (Advanced):${NC}" echo " • Subnet: ${DOCKER_SUBNET:-172.28.0.0/24}" echo " • Gateway: ${DOCKER_GATEWAY:-172.28.0.1}" echo " • Good for: Specific network requirements" + echo " • Requires manual configuration" echo "" - echo -e "${GREEN}2. Auto-Managed (Maximum Compatibility):${NC}" - echo " • Let Docker choose subnet automatically" - echo " • Best for: Avoiding conflicts entirely" - echo "" - echo -e "${GREEN}3. Alternative Subnets:${NC}" + echo -e "${GREEN}3. Alternative Subnets (if conflicts persist):${NC}" echo " • 172.29.0.0/24 (less common)" echo " • 172.30.0.0/24 (even less common)" echo " • 10.99.0.0/24 (private class A)" @@ -92,18 +95,21 @@ fix_conflicts() { } switch_to_auto_managed() { - echo -e "${BLUE}🔄 Switching to auto-managed network...${NC}" + echo -e "${BLUE}🔄 Ensuring auto-managed network configuration...${NC}" # Create backup cp docker-compose.yml docker-compose.yml.backup - # Comment out the advanced config and uncomment the simple one - sed -i.tmp '/# Advanced network configuration/,/com.docker.network.driver.mtu: "1500"/s/^/# /' docker-compose.yml - sed -i.tmp '/# Alternative: Simple auto-managed network/,/# *name: accounting_panel_network/s/^# *//' docker-compose.yml + # Check if we're already using the simple configuration + if grep -q "name: accounting_panel_network" docker-compose.yml; then + echo -e "${YELLOW}Removing custom network name for better service discovery...${NC}" + sed -i.tmp '/name: accounting_panel_network/d' docker-compose.yml + rm docker-compose.yml.tmp 2>/dev/null || true + echo -e "${GREEN}✅ Updated to auto-managed network${NC}" + else + echo -e "${GREEN}✅ Already using auto-managed network${NC}" + fi - rm docker-compose.yml.tmp - - echo -e "${GREEN}✅ Switched to auto-managed network${NC}" echo -e "${YELLOW}💡 Backup saved as docker-compose.yml.backup${NC}" } diff --git a/setup.sh b/setup.sh index 92f14c9..39d83d4 100755 --- a/setup.sh +++ b/setup.sh @@ -443,13 +443,14 @@ setup_directories() { update_caddy_config() { print_header "Updating Caddy Configuration" - # Use simplified Caddy configuration that works without environment variables - cat > docker/caddy/Caddyfile << 'EOF' + if [[ "$DOMAIN" == "localhost" ]]; then + # Localhost configuration - HTTP only, port-based + cat > docker/caddy/Caddyfile << 'EOF' { admin off } -# HTTP server - works on both ports 80 and 8080 +# HTTP server for localhost - ports 80 and 8080 :80, :8080 { root * /var/www/html/public @@ -463,15 +464,25 @@ update_caddy_config() { # Enable file server file_server - # Enable gzip compression + # Enable gzip compression encode gzip # PHP handling php_fastcgi app:9000 } +EOF + else + # Domain-based configuration + if [[ "$SSL_TYPE" == "letsencrypt" ]]; then + # Let's Encrypt configuration - automatic HTTPS + cat > docker/caddy/Caddyfile << EOF +{ + admin off + email ${ADMIN_EMAIL} +} -# HTTPS server - default port 443 and 8443 -:443, :8443 { +# HTTP and HTTPS for ${DOMAIN} +${DOMAIN} { root * /var/www/html/public # Security headers @@ -492,8 +503,64 @@ update_caddy_config() { php_fastcgi app:9000 } EOF + else + # Self-signed certificate configuration + cat > docker/caddy/Caddyfile << EOF +{ + admin off + local_certs +} - print_success "Caddy configuration updated" +# HTTP server +:80, :8080 { + root * /var/www/html/public + + # Security headers + header { + X-Content-Type-Options nosniff + X-Frame-Options DENY + -Server + } + + # Enable file server + file_server + + # Enable gzip compression + encode gzip + + # PHP handling + php_fastcgi app:9000 +} + +# HTTPS server with self-signed certificates +:443, :8443 { + root * /var/www/html/public + + # Self-signed TLS certificate + tls internal + + # Security headers + header { + X-Content-Type-Options nosniff + X-Frame-Options DENY + Strict-Transport-Security "max-age=31536000; includeSubDomains" + -Server + } + + # Enable file server + file_server + + # Enable gzip compression + encode gzip + + # PHP handling + php_fastcgi app:9000 +} +EOF + fi + fi + + print_success "Caddy configuration updated for $DOMAIN with SSL type: ${SSL_TYPE:-none}" } # Build and deploy application @@ -521,7 +588,22 @@ deploy_application() { local max_wait=300 local count=0 - while ! docker compose ps --format json | jq -r '.[] | select(.Health != null) | .Health' | grep -q "healthy"; do + while true; do + # Check health status using multiple methods for compatibility + local health_status="" + if command -v jq >/dev/null 2>&1; then + # Try different health check formats + health_status=$(docker compose ps --format json 2>/dev/null | jq -r '.[].State // .[].Status // "unknown"' 2>/dev/null | grep -c "running\|healthy" || echo "0") + else + # Fallback without jq - just check if containers are running + health_status=$(docker compose ps --format "table {{.State}}" 2>/dev/null | grep -c "running" || echo "0") + fi + + # Check if all expected services are healthy/running (at least 4: app, database, caddy, cron) + if [[ "$health_status" -ge 4 ]]; then + break + fi + if [[ $count -ge $max_wait ]]; then die "Services failed to become healthy within $max_wait seconds" fi @@ -565,11 +647,18 @@ initialize_database() { run_health_checks() { print_header "Running Health Checks" - # Check container health - if docker compose ps --format json | jq -r '.[] | select(.Health != null) | .Health' | grep -q "healthy"; then - print_success "All containers are healthy" + # Check container health + local running_services=0 + if command -v jq >/dev/null 2>&1; then + running_services=$(docker compose ps --format json 2>/dev/null | jq -r '.[].State // .[].Status // "unknown"' 2>/dev/null | grep -c "running\|healthy" || echo "0") else - print_warning "Some containers may not be healthy" + running_services=$(docker compose ps --format "table {{.State}}" 2>/dev/null | grep -c "running" || echo "0") + fi + + if [[ "$running_services" -ge 4 ]]; then + print_success "All containers are healthy ($running_services services running)" + else + print_warning "Some containers may not be healthy ($running_services services running)" fi # Check web server