diff --git a/debian/.env b/debian/.env new file mode 100644 index 0000000..a321d43 --- /dev/null +++ b/debian/.env @@ -0,0 +1,49 @@ +# IN application vars +APP_URL=http://in.localhost:8003 +APP_KEY= +APP_DEBUG=true +REQUIRE_HTTPS=false +PHANTOMJS_PDF_GENERATION=false +PDF_GENERATOR=snappdf +TRUSTED_PROXIES='*' + +QUEUE_CONNECTION=database + +# DB connection +DB_HOST=db +DB_PORT=3306 +DB_DATABASE=ninja +DB_USERNAME=ninja +DB_PASSWORD=ninja +DB_ROOT_PASSWORD=ninjaAdm1nPassword + +# Create initial user +# Default to these values if empty +# IN_USER_EMAIL=admin@example.com +# IN_PASSWORD=changeme! +IN_USER_EMAIL= +IN_PASSWORD= + +# Mail options +MAIL_MAILER=log +MAIL_HOST=smtp.mailtrap.io +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS='user@example.com' +MAIL_FROM_NAME='Self Hosted User' + +# MySQL +MYSQL_ROOT_PASSWORD=ninjaAdm1nPassword +MYSQL_USER=ninja +MYSQL_PASSWORD=ninja +MYSQL_DATABASE=ninja + +# GoCardless/Nordigen API key for banking integration +NORDIGEN_SECRET_ID= +NORDIGEN_SECRET_KEY= + +# V4 env vars +# DB_STRICT=false +# APP_CIPHER=AES-256-CBC diff --git a/debian/Dockerfile b/debian/Dockerfile new file mode 100644 index 0000000..03ac2c8 --- /dev/null +++ b/debian/Dockerfile @@ -0,0 +1,93 @@ +FROM php:8.2-fpm as base + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + git \ + curl \ + libpng-dev \ + libonig-dev \ + libxml2-dev \ + zip \ + unzip \ + supervisor \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Install PHP extensions +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ +RUN install-php-extensions \ + pdo_mysql \ + mbstring \ + exif \ + pcntl \ + bcmath \ + gd \ + opcache \ + redis \ + zip \ + @composer + +# Configure PHP +COPY php/php.ini /usr/local/etc/php/conf.d/app.ini +COPY php/php-fpm.conf /usr/local/etc/php-fpm.d/www.conf + +# Set working directory +WORKDIR /var/www/html + +# Download and extract application in a single layer +RUN set -eux; \ + DOWNLOAD_URL=$(curl -s "https://api.github.com/repos/invoiceninja/invoiceninja/releases/latest" | \ + grep -o '"browser_download_url": "[^"]*invoiceninja.tar"' | cut -d '"' -f 4) && \ + curl -L "$DOWNLOAD_URL" | tar -xvz -C /var/www/html && \ + rm -rf /var/www/html/ui && \ + chown -R www-data:www-data /var/www/html + +# Install dependencies +RUN composer install --no-dev --no-scripts --no-autoloader + +# Generate optimized autoloader and clear cache +RUN composer dump-autoload --optimize \ + && php artisan optimize \ + && php artisan view:cache \ + && php artisan config:cache \ + && php artisan route:cache + +# Setup supervisor +COPY supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf + +# Add initialization script +COPY scripts/init.sh /usr/local/bin/init.sh +RUN chmod +x /usr/local/bin/init.sh + +# Create volume directories +RUN mkdir -p \ + /var/www/html/storage/app/public \ + /var/www/html/storage/framework/cache \ + /var/www/html/storage/framework/sessions \ + /var/www/html/storage/framework/views \ + /var/www/html/storage/logs \ + /var/www/html/public/uploads \ + /var/log/supervisor + +# Set permissions +RUN chown -R www-data:www-data \ + /var/www/html/storage \ + /var/www/html/bootstrap/cache \ + /var/www/html/public/uploads \ + /var/log/supervisor \ + && chmod -R 775 \ + /var/www/html/public/uploads \ + /var/www/html/storage \ + /var/www/html/bootstrap/cache \ + /var/log/supervisor + +USER www-data + +# Health check +HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ + CMD php -v || exit 1 + +EXPOSE 9000 + +ENTRYPOINT ["/usr/local/bin/init.sh"] +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] \ No newline at end of file diff --git a/debian/docker-compose.yml b/debian/docker-compose.yml new file mode 100644 index 0000000..6977639 --- /dev/null +++ b/debian/docker-compose.yml @@ -0,0 +1,109 @@ +version: '3.8' + +x-logging: &default-logging + options: + max-size: "10m" + max-file: "3" + driver: json-file + +services: + app: + image: deb6:latest + user: "www-data:www-data" # Set user explicitly + restart: unless-stopped + env_file: + - .env + volumes: + - app_storage:/var/www/html/storage:rw + - app_public:/var/www/html/public/uploads:rw + - app_cache:/var/www/html/bootstrap/cache:rw + networks: + - app-network + depends_on: + mysql: + condition: service_healthy + redis: + condition: service_healthy + deploy: + resources: + limits: + memory: 512M + logging: *default-logging + + nginx: + image: nginx:alpine + restart: unless-stopped + ports: + - "8012:80" + volumes: + - app_public:/var/www/html/public/uploads:ro + - ./nginx/conf.d:/etc/nginx/conf.d:ro + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + networks: + - app-network + depends_on: + - app + deploy: + resources: + limits: + memory: 128M + logging: *default-logging + + mysql: + image: mysql:8.0 + restart: unless-stopped + env_file: + - .env + environment: + MYSQL_DATABASE: ${DB_DATABASE} + MYSQL_USER: ${DB_USERNAME} + MYSQL_PASSWORD: ${DB_PASSWORD} + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + volumes: + - mysql_data:/var/lib/mysql + networks: + - app-network + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"] + interval: 10s + timeout: 5s + retries: 5 + deploy: + resources: + limits: + memory: 1G + logging: *default-logging + + redis: + image: redis:alpine + restart: unless-stopped + volumes: + - redis_data:/data + networks: + - app-network + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + deploy: + resources: + limits: + memory: 256M + logging: *default-logging + +networks: + app-network: + driver: bridge + +volumes: + app_storage: + driver: local + app_public: + driver: local + app_cache: + driver: local + mysql_data: + driver: local + redis_data: + driver: local \ No newline at end of file diff --git a/debian/nginx/conf.d/default.conf b/debian/nginx/conf.d/default.conf new file mode 100644 index 0000000..70b45fb --- /dev/null +++ b/debian/nginx/conf.d/default.conf @@ -0,0 +1,34 @@ +server { + listen 80; + server_name localhost; + root /var/www/html/public; + index index.php; + + charset utf-8; + client_max_body_size 100M; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location = /favicon.ico { access_log off; log_not_found off; } + location = /robots.txt { access_log off; log_not_found off; } + + error_page 404 /index.php; + + location ~ \.php$ { + fastcgi_pass app:9000; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + include fastcgi_params; + } + + location ~ /\.(?!well-known).* { + deny all; + } + + # Enable browser caching for static assets + location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { + expires 30d; + add_header Cache-Control "public, no-transform"; + } +} \ No newline at end of file diff --git a/debian/nginx/nginx.conf b/debian/nginx/nginx.conf new file mode 100644 index 0000000..919f25c --- /dev/null +++ b/debian/nginx/nginx.conf @@ -0,0 +1,26 @@ +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +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; + error_log /var/log/nginx/error.log; + + sendfile on; + keepalive_timeout 65; + gzip on; + + include /etc/nginx/conf.d/*.conf; +} \ No newline at end of file diff --git a/debian/php/php-fpm.conf b/debian/php/php-fpm.conf new file mode 100644 index 0000000..48ba99a --- /dev/null +++ b/debian/php/php-fpm.conf @@ -0,0 +1,9 @@ +[www] +user = www-data +group = www-data +listen = 9000 +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 \ No newline at end of file diff --git a/debian/php/php.ini b/debian/php/php.ini new file mode 100644 index 0000000..e69de29 diff --git a/debian/scripts/init.sh b/debian/scripts/init.sh new file mode 100755 index 0000000..e96b124 --- /dev/null +++ b/debian/scripts/init.sh @@ -0,0 +1,27 @@ +#!/bin/sh +set -e + +# Create directories if they don't exist +mkdir -p \ + /var/www/html/storage/app/public \ + /var/www/html/storage/framework/cache \ + /var/www/html/storage/framework/sessions \ + /var/www/html/storage/framework/views \ + /var/www/html/storage/logs \ + /var/www/html/public/uploads + +# Set directory permissions without changing ownership +chmod -R 775 \ + /var/www/html/storage \ + /var/www/html/bootstrap/cache \ + /var/www/html/public/uploads + +# Clear and cache config in production +if [ "$APP_ENV" = "production" ]; then + php artisan config:cache + php artisan route:cache + php artisan view:cache +fi + +# Start supervisord +exec "$@" \ No newline at end of file diff --git a/debian/supervisor/supervisord.conf b/debian/supervisor/supervisord.conf new file mode 100644 index 0000000..ac43a85 --- /dev/null +++ b/debian/supervisor/supervisord.conf @@ -0,0 +1,33 @@ +[supervisord] +nodaemon=true +user=www-data +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:php-fpm] +command=php-fpm +autostart=true +autorestart=true +stderr_logfile=/var/log/supervisor/php-fpm.err.log +stdout_logfile=/var/log/supervisor/php-fpm.out.log + +[program:laravel-worker] +process_name=%(program_name)s_%(process_num)02d +command=php /var/www/html/artisan queue:work --sleep=3 --tries=3 --max-time=3600 +autostart=true +autorestart=true +stopasgroup=true +killasgroup=true +user=www-data +numprocs=2 +redirect_stderr=true +stdout_logfile=/var/log/supervisor/worker.log +stopwaitsecs=3600 + +[program:laravel-scheduler] +command=php /var/www/html/artisan schedule:work +autostart=true +autorestart=true +user=www-data +redirect_stderr=true +stdout_logfile=/var/log/supervisor/scheduler.log \ No newline at end of file