diff --git a/tastyigniter/.env.example b/tastyigniter/.env.example new file mode 100644 index 0000000..f750300 --- /dev/null +++ b/tastyigniter/.env.example @@ -0,0 +1,26 @@ +# Application +APP_NAME=TastyIgniter +APP_ENV=production +APP_KEY= +APP_DEBUG=false +APP_URL=https://your-domain.com + +# TastyIgniter specific +IGNITER_LOCATION_MODE=multiple +IGNITER_CARTE_KEY= + +# Database +DB_DATABASE=tastyigniter +DB_USERNAME=tastyigniter +DB_PASSWORD=your_secure_password +DB_PREFIX=ti_ +MYSQL_ROOT_PASSWORD=your_secure_root_password + +# Mail +MAIL_MAILER=log +MAIL_FROM_ADDRESS=noreply@your-domain.com +MAIL_FROM_NAME=TastyIgniter + +# Deployment +PORT=80 +STACK_NAME=tastyigniter \ No newline at end of file diff --git a/tastyigniter/Dockerfile b/tastyigniter/Dockerfile index 1eacc8e..5f98fa9 100644 --- a/tastyigniter/Dockerfile +++ b/tastyigniter/Dockerfile @@ -12,11 +12,16 @@ RUN apt-get update && apt-get install -y \ zip \ unzip \ default-mysql-client \ + libfreetype6-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libwebp-dev \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Install PHP extensions -RUN docker-php-ext-install \ +RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ + && docker-php-ext-install \ pdo_mysql \ mbstring \ exif \ @@ -27,7 +32,8 @@ RUN docker-php-ext-install \ xml \ intl \ ctype \ - tokenizer + tokenizer \ + opcache # Install Composer COPY --from=composer:latest /usr/bin/composer /usr/bin/composer @@ -47,6 +53,10 @@ RUN mkdir -p /var/www/html/storage && \ chown -R www-data:www-data /var/www/html && \ chmod -R 755 /var/www/html +# Configure PHP +COPY docker/php/php.ini /usr/local/etc/php/conf.d/custom.ini +COPY docker/php/www.conf /usr/local/etc/php-fpm.d/www.conf + # Entrypoint script ENTRYPOINT ["entrypoint.sh"] diff --git a/tastyigniter/README.md b/tastyigniter/README.md index 0772457..807c429 100644 --- a/tastyigniter/README.md +++ b/tastyigniter/README.md @@ -1,104 +1,130 @@ -# TastyIgniter Docker Setup +# TastyIgniter Docker Compose -This repository contains Docker configuration files to quickly set up a TastyIgniter v4.0+ environment. The setup includes: +This repository contains a Docker Compose setup for TastyIgniter, making it easy to deploy on Coolify or any other Docker-compatible platform. -- PHP 8.3 with all required extensions -- MySQL 8 database +## Features + +- PHP 8.3 with FPM - Nginx web server -- Automated installation process -- Properly configured scheduler and queue workers +- MySQL 8 database +- Queue worker for background jobs +- Cron job for scheduled tasks +- Persistent volumes for data storage +- Health checks for all services +- Environment variable configuration ## Prerequisites -- Docker and Docker Compose installed on your system -- Basic understanding of Docker concepts +- Docker +- Docker Compose +- Coolify (for deployment) -## Directory Structure - -``` -tastyigniter-docker/ -├── docker/ -│ ├── entrypoint.sh -│ └── nginx/ -│ └── default.conf -├── docker-compose.yml -├── Dockerfile -└── README.md -``` - -## Setup Instructions - -1. Create the directory structure as shown above: +## Quick Start +1. Clone this repository: ```bash -mkdir -p tastyigniter-docker/docker/nginx +git clone https://github.com/yourusername/tastyigniter-docker-compose.git +cd tastyigniter-docker-compose ``` -2. Copy all the configuration files into their respective directories. +2. Copy the example environment file: +```bash +cp .env.example .env +``` -3. Start the Docker environment: +3. Edit the `.env` file with your configuration: +```bash +nano .env +``` +4. Start the containers: ```bash -cd tastyigniter-docker docker-compose up -d ``` -4. The setup will: - - Create a new TastyIgniter project - - Set up the database connection - - Run the non-interactive installation - - Configure the proper permissions - - Set up scheduled tasks and queue processing +## Deployment on Coolify -5. Access TastyIgniter: - - Frontend: http://localhost - - Admin panel: http://localhost/admin - - Default admin credentials: - - Username: admin - - Email: admin@example.com - - Password: admin123 +1. Push this repository to your Git provider (GitHub, GitLab, etc.) -## Configuration Options +2. In Coolify: + - Create a new service + - Select "Docker Compose" as the deployment method + - Connect your Git repository + - Set the following environment variables from the `.env.example` file + - Deploy the service -You can customize the installation by modifying the environment variables in the `docker-compose.yml` file: +### Required Environment Variables -- `APP_URL`: Your application URL -- `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD`: Database connection details -- `ADMIN_USER`, `ADMIN_EMAIL`, `ADMIN_PASSWORD`: Administrator account details +Make sure to set these environment variables in Coolify: -## Persistent Storage +- `APP_KEY`: Generate a random 32-character string +- `APP_URL`: Your domain name +- `DB_PASSWORD`: Secure database password +- `MYSQL_ROOT_PASSWORD`: Secure root password +- `IGNITER_CARTE_KEY`: Your TastyIgniter Carte key (if using) -The setup uses Docker volumes for: -- MySQL data: Stored in the `dbdata` volume -- TastyIgniter storage: Mapped to the `./storage` directory on your host +### Volume Management -## Production Deployment +The following volumes are automatically managed by Coolify: +- `dbdata`: MySQL database data +- `tastyigniter`: Application files +- `tastyigniter-storage`: Application storage -Before deploying to production, make sure to: +### Health Checks -1. Change all default passwords -2. Set `APP_DEBUG=false` -3. Generate a unique `APP_KEY` -4. Configure proper SSL/TLS in the Nginx configuration for HTTPS +The deployment includes health checks for: +- Application container (PHP-FPM) +- Nginx web server +- MySQL database + +## Development + +To run in development mode: + +1. Set `APP_ENV=local` in your `.env` file +2. Set `APP_DEBUG=true` in your `.env` file +3. Run `docker-compose up -d` + +## Maintenance + +### Database Backup + +To backup the database: +```bash +docker-compose exec db mysqldump -u root -p tastyigniter > backup.sql +``` + +### Logs + +View logs for all services: +```bash +docker-compose logs -f +``` + +View logs for a specific service: +```bash +docker-compose logs -f app +docker-compose logs -f nginx +docker-compose logs -f db +``` ## Troubleshooting -If you encounter issues: +1. If the application fails to start: + - Check the logs: `docker-compose logs -f app` + - Verify environment variables are set correctly + - Ensure all required ports are available -1. Check the logs: `docker-compose logs -f app` -2. Verify database connectivity: `docker-compose exec app php artisan db:monitor` -3. Check container status: `docker-compose ps` -4. Ensure proper permissions on the storage directory +2. If the database connection fails: + - Check the database logs: `docker-compose logs -f db` + - Verify database credentials in `.env` + - Ensure the database container is running: `docker-compose ps` -### Common Issues +3. If the web server is not accessible: + - Check nginx logs: `docker-compose logs -f nginx` + - Verify port configuration in `.env` + - Check if the domain is properly configured -**Missing PHP extensions** +## License -If you see errors about missing PHP extensions like `ext-intl`, the Dockerfile has been updated to include this. However, if you encounter other missing extensions, you can add them to the Dockerfile by: - -1. Adding the required dev packages to the `apt-get install` command -2. Adding the extension to the `docker-php-ext-install` command - -**Composer installation failures** - -If the composer installation fails, you can modify the entrypoint script to use the `--ignore-platform-req` flag. This is included as a fallback in the updated entrypoint script. +This project is licensed under the MIT License - see the LICENSE file for details. diff --git a/tastyigniter/docker-compose.yml b/tastyigniter/docker-compose.yml index 24a532b..1d8a7c1 100644 --- a/tastyigniter/docker-compose.yml +++ b/tastyigniter/docker-compose.yml @@ -8,22 +8,22 @@ services: container_name: tastyigniter-app restart: unless-stopped environment: - - APP_NAME=TastyIgniter - - APP_ENV=production - - APP_KEY= - - APP_DEBUG=false - - APP_URL=http://localhost - - IGNITER_LOCATION_MODE=multiple - - IGNITER_CARTE_KEY= + - APP_NAME=${APP_NAME:-TastyIgniter} + - APP_ENV=${APP_ENV:-production} + - APP_KEY=${APP_KEY} + - APP_DEBUG=${APP_DEBUG:-false} + - APP_URL=${APP_URL} + - IGNITER_LOCATION_MODE=${IGNITER_LOCATION_MODE:-multiple} + - IGNITER_CARTE_KEY=${IGNITER_CARTE_KEY} # Database configuration - DB_CONNECTION=mysql - DB_HOST=db - DB_PORT=3306 - - DB_DATABASE=tastyigniter - - DB_USERNAME=tastyigniter - - DB_PASSWORD=secret_password - - DB_PREFIX=ti_ + - DB_DATABASE=${DB_DATABASE:-tastyigniter} + - DB_USERNAME=${DB_USERNAME:-tastyigniter} + - DB_PASSWORD=${DB_PASSWORD} + - DB_PREFIX=${DB_PREFIX:-ti_} # Cache and Session - BROADCAST_DRIVER=log @@ -33,44 +33,61 @@ services: - SESSION_LIFETIME=120 # Mail Configuration - - MAIL_MAILER=log - - MAIL_FROM_ADDRESS=noreply@tastyigniter.tld - - MAIL_FROM_NAME=TastyIgniter + - MAIL_MAILER=${MAIL_MAILER:-log} + - MAIL_FROM_ADDRESS=${MAIL_FROM_ADDRESS:-noreply@tastyigniter.tld} + - MAIL_FROM_NAME=${MAIL_FROM_NAME:-TastyIgniter} volumes: - tastyigniter:/var/www/html - tastyigniter-storage:/var/www/html/storage depends_on: - - db + db: + condition: service_healthy networks: - tastyigniter-network + healthcheck: + test: ["CMD", "php", "artisan", "--version"] + interval: 30s + timeout: 10s + retries: 3 nginx: image: nginx:alpine container_name: tastyigniter-nginx restart: unless-stopped ports: - - "80:80" + - "${PORT:-80}:80" volumes: - tastyigniter:/var/www/html - ./docker/nginx:/etc/nginx/conf.d depends_on: - - app + app: + condition: service_healthy networks: - tastyigniter-network + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:80"] + interval: 30s + timeout: 10s + retries: 3 db: image: mysql:8 container_name: tastyigniter-db restart: unless-stopped environment: - - MYSQL_DATABASE=tastyigniter - - MYSQL_USER=tastyigniter - - MYSQL_PASSWORD=secret_password - - MYSQL_ROOT_PASSWORD=root_password + - MYSQL_DATABASE=${DB_DATABASE:-tastyigniter} + - MYSQL_USER=${DB_USERNAME:-tastyigniter} + - MYSQL_PASSWORD=${DB_PASSWORD} + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} volumes: - dbdata:/var/lib/mysql networks: - tastyigniter-network + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"] + interval: 30s + timeout: 10s + retries: 3 cron: build: @@ -82,7 +99,8 @@ services: volumes: - tastyigniter:/var/www/html depends_on: - - app + app: + condition: service_healthy networks: - tastyigniter-network @@ -96,15 +114,20 @@ services: volumes: - tastyigniter:/var/www/html depends_on: - - app + app: + condition: service_healthy networks: - tastyigniter-network volumes: dbdata: + name: ${STACK_NAME:-tastyigniter}_dbdata tastyigniter: + name: ${STACK_NAME:-tastyigniter}_app tastyigniter-storage: + name: ${STACK_NAME:-tastyigniter}_storage networks: tastyigniter-network: + name: ${STACK_NAME:-tastyigniter}_network driver: bridge diff --git a/tastyigniter/docker/entrypoint.sh b/tastyigniter/docker/entrypoint.sh index f94bb81..be50996 100644 --- a/tastyigniter/docker/entrypoint.sh +++ b/tastyigniter/docker/entrypoint.sh @@ -1,9 +1,43 @@ #!/bin/bash set -e +# Function to log messages +log() { + echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" +} + +# Function to check if a command exists +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +# Function to validate environment variables +validate_env() { + local required_vars=("DB_HOST" "DB_DATABASE" "DB_USERNAME" "DB_PASSWORD") + local missing_vars=() + + for var in "${required_vars[@]}"; do + if [ -z "${!var}" ]; then + missing_vars+=("$var") + fi + done + + if [ ${#missing_vars[@]} -ne 0 ]; then + log "ERROR: Missing required environment variables: ${missing_vars[*]}" + exit 1 + fi +} + +# Create log directory if it doesn't exist +mkdir -p /var/log/php +chown -R www-data:www-data /var/log/php + +# Validate environment variables +validate_env + # Check if TastyIgniter is already installed if [ ! -f /var/www/html/composer.json ] || [ ! -d /var/www/html/vendor ]; then - echo "Installing TastyIgniter..." + log "Installing TastyIgniter..." # Create new TastyIgniter project composer create-project tastyigniter/tastyigniter:^v4.0 /tmp/tastyigniter --prefer-dist --no-interaction --no-progress @@ -11,40 +45,71 @@ if [ ! -f /var/www/html/composer.json ] || [ ! -d /var/www/html/vendor ]; then cp -a /tmp/tastyigniter/. /var/www/html/ rm -rf /tmp/tastyigniter - echo "TastyIgniter files installed" + log "TastyIgniter files installed" fi # Set proper permissions +log "Setting proper permissions..." chown -R www-data:www-data /var/www/html chmod -R 755 /var/www/html chmod -R 775 /var/www/html/storage +chmod -R 775 /var/www/html/bootstrap/cache # Generate app key if not set if [ -z "$APP_KEY" ]; then + log "Generating application key..." php artisan key:generate fi # Wait for database to be ready -echo "Waiting for database to be ready..." +log "Waiting for database to be ready..." +max_tries=30 +counter=1 while ! mysql -h "$DB_HOST" -u "$DB_USERNAME" -p"$DB_PASSWORD" -e "SELECT 1" >/dev/null 2>&1; do - sleep 1 + if [ $counter -ge $max_tries ]; then + log "ERROR: Database connection failed after $max_tries attempts" + exit 1 + fi + log "Database connection attempt $counter/$max_tries failed, retrying in 2 seconds..." + sleep 2 + counter=$((counter + 1)) done -echo "Database connection established" +log "Database connection established" # Run TastyIgniter installer in non-interactive mode if not installed if [ ! -f .env ] || ! grep -q "IGNITER_INSTALLED=true" .env; then - echo "Running TastyIgniter installer..." + log "Running TastyIgniter installer..." php artisan igniter:install --no-interaction # Mark as installed in .env echo "IGNITER_INSTALLED=true" >> .env - echo "TastyIgniter installed successfully" + log "TastyIgniter installed successfully" else - echo "TastyIgniter already installed" + log "TastyIgniter already installed" fi # Run database migrations if needed +log "Running database migrations..." php artisan migrate --force +# Clear and cache configuration +log "Clearing and caching configuration..." +php artisan config:clear +php artisan config:cache +php artisan route:clear +php artisan route:cache +php artisan view:clear +php artisan view:cache + +# Set proper permissions again after all operations +log "Setting final permissions..." +chown -R www-data:www-data /var/www/html +chmod -R 755 /var/www/html +chmod -R 775 /var/www/html/storage +chmod -R 775 /var/www/html/bootstrap/cache + +log "Entrypoint script completed successfully" + +# Execute the main command exec "$@" diff --git a/tastyigniter/docker/nginx/site.conf b/tastyigniter/docker/nginx/site.conf index c410e70..9c19a9e 100644 --- a/tastyigniter/docker/nginx/site.conf +++ b/tastyigniter/docker/nginx/site.conf @@ -1,40 +1,49 @@ server { listen 80; - server_name localhost; - + server_name ${APP_URL:-localhost}; root /var/www/html/public; index index.php; # Security headers - add_header X-Frame-Options "SAMEORIGIN"; - add_header X-Content-Type-Options "nosniff"; - add_header X-XSS-Protection "1; mode=block"; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" 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; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # Gzip compression - gzip on; - gzip_comp_level 5; - gzip_min_length 256; - gzip_proxied expired no-cache no-store private auth; - gzip_types application/javascript application/x-javascript application/json - text/css text/plain text/xml application/xml - image/svg+xml image/x-icon - application/vnd.ms-fontobject application/x-font-ttf font/opentype; + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml; charset utf-8; client_max_body_size 100M; fastcgi_read_timeout 1800; + # Logs + access_log /var/log/nginx/access.log combined buffer=512k flush=1m; + error_log /var/log/nginx/error.log warn; + location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass app:9000; fastcgi_index index.php; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; + fastcgi_read_timeout 1800; + fastcgi_send_timeout 1800; + fastcgi_connect_timeout 1800; } # Deny access to . files @@ -55,18 +64,33 @@ server { } # Deny access to sensitive files - location ~* \.(env|log|git|key|md|yml|yaml|conf)$ { + location ~* \.(env|log|git|key|md|yml|yaml|conf|ini|lock|json|sql)$ { deny all; } # Assets caching - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires max; log_not_found off; access_log off; add_header Cache-Control "public, no-transform"; + try_files $uri =404; } - access_log off; - error_log /var/log/nginx/error.log error; + # Deny access to hidden files + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } + + # Deny access to composer files + location ~ composer\.(json|lock)$ { + deny all; + } + + # Deny access to storage files + location ~ /storage/.*\.(php|php5|sh|pl|py|jsp|asp|htm|html|shtml|js)$ { + deny all; + } } diff --git a/tastyigniter/docker/php/php.ini b/tastyigniter/docker/php/php.ini new file mode 100644 index 0000000..98a8078 --- /dev/null +++ b/tastyigniter/docker/php/php.ini @@ -0,0 +1,31 @@ +[PHP] +; Production settings +display_errors = Off +display_startup_errors = Off +error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED +log_errors = On +error_log = /var/log/php/error.log +memory_limit = 256M +max_execution_time = 60 +max_input_time = 60 +post_max_size = 100M +upload_max_filesize = 100M +max_input_vars = 3000 + +; Session settings +session.gc_maxlifetime = 1440 +session.gc_probability = 1 +session.gc_divisor = 100 + +; OpCache settings +opcache.enable = 1 +opcache.enable_cli = 0 +opcache.memory_consumption = 128 +opcache.interned_strings_buffer = 8 +opcache.max_accelerated_files = 4000 +opcache.revalidate_freq = 60 +opcache.fast_shutdown = 1 +opcache.enable_file_override = 0 + +; Date settings +date.timezone = UTC \ No newline at end of file diff --git a/tastyigniter/docker/php/www.conf b/tastyigniter/docker/php/www.conf new file mode 100644 index 0000000..be443d9 --- /dev/null +++ b/tastyigniter/docker/php/www.conf @@ -0,0 +1,21 @@ +[www] +user = www-data +group = www-data +listen = 9000 +listen.owner = www-data +listen.group = www-data +listen.mode = 0660 +pm = dynamic +pm.max_children = 50 +pm.start_servers = 5 +pm.min_spare_servers = 5 +pm.max_spare_servers = 35 +pm.max_requests = 500 +php_admin_value[error_log] = /var/log/php/error.log +php_admin_flag[log_errors] = on +php_admin_value[memory_limit] = 256M +php_admin_value[max_execution_time] = 60 +php_admin_value[max_input_time] = 60 +php_admin_value[post_max_size] = 100M +php_admin_value[upload_max_filesize] = 100M +php_admin_value[max_input_vars] = 3000 \ No newline at end of file