diff --git a/.env.example b/.env.example index 3435e25..6999aad 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,12 @@ -APP_NAME=KasirTMJC -APP_ENV=local +APP_NAME=Abbauf-Kasir +APP_ENV=production APP_KEY= -APP_DEBUG=true +APP_DEBUG=false APP_URL=http://localhost -APP_LOCALE=id -APP_FALLBACK_LOCALE=id -APP_FAKER_LOCALE=id_ID +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US APP_MAINTENANCE_DRIVER=file # APP_MAINTENANCE_STORE=database @@ -20,15 +20,12 @@ LOG_STACK=single LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug -DB_CONNECTION=mysql -DB_HOST=mysql -DB_PORT=3306 -DB_DATABASE=kasir_db -DB_USERNAME=kasir_user -DB_PASSWORD=kasir_password - -# MySQL Root Password (untuk Docker) -MYSQL_ROOT_PASSWORD=root_password +DB_CONNECTION=sqlite +# DB_HOST=127.0.0.1 +# DB_PORT=3306 +# DB_DATABASE=laravel +# DB_USERNAME=root +# DB_PASSWORD= SESSION_DRIVER=database SESSION_LIFETIME=120 @@ -46,7 +43,7 @@ CACHE_STORE=database MEMCACHED_HOST=127.0.0.1 REDIS_CLIENT=phpredis -REDIS_HOST=redis +REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 diff --git a/Dockerfile b/Dockerfile index cb391c9..74e5344 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,36 +1,91 @@ -# Stage 1: Build Vue (tetap sama) -FROM node:20 as node_builder +# ======================================== +# Stage 1: Build Frontend Assets (Vue.js) +# ======================================== +FROM node:20-alpine as node_builder + WORKDIR /app + +# Copy package files untuk caching layer COPY package*.json ./ -RUN npm install + +# Install dependencies +RUN npm ci --legacy-peer-deps + +# Copy seluruh source code COPY . . + +# Build production assets RUN npm run build -# Stage 2: Laravel -FROM php:8.3-fpm +# ======================================== +# Stage 2: Laravel Application +# ======================================== +FROM php:8.3-fpm-alpine -RUN apt-get update && apt-get install -y \ - git unzip libzip-dev libpng-dev libonig-dev libxml2-dev curl \ - && docker-php-ext-install pdo_mysql zip gd mbstring exif pcntl bcmath \ - && apt-get clean && rm -rf /var/lib/apt/lists/* # Cleanup untuk ukuran kecil +# Install system dependencies dan PHP extensions +RUN apk update && apk add --no-cache \ + git \ + unzip \ + libzip-dev \ + libpng-dev \ + oniguruma-dev \ + libxml2-dev \ + curl \ + mysql-client \ + autoconf \ + g++ \ + make \ + && docker-php-ext-install \ + pdo_mysql \ + zip \ + gd \ + mbstring \ + exif \ + pcntl \ + bcmath \ + && pecl install redis \ + && docker-php-ext-enable redis \ + && apk del autoconf g++ make \ + && rm -rf /var/cache/apk/* /tmp/* +# Install Composer COPY --from=composer:2 /usr/bin/composer /usr/bin/composer +# Set working directory WORKDIR /var/www/html -# Copy source code +# Copy composer files untuk caching layer +COPY composer.json composer.lock ./ + +# Install PHP dependencies +RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist + +# Copy application source code COPY . . -# Copy hasil build Vue + +# Copy hasil build Vue dari stage 1 COPY --from=node_builder /app/public/build /var/www/html/public/build -RUN composer install --no-dev --optimize-autoloader -RUN php artisan storage:link || true -RUN php artisan config:cache && php artisan route:cache && php artisan view:cache # Optimasi cache untuk performa laporan/transaksi +# Generate autoload files dengan optimasi +RUN composer dump-autoload --optimize --classmap-authoritative -# Set permission dan user non-root -RUN chown -R www-data:www-data /var/www/html \ - && chmod -R 755 /var/www/html/storage \ - && chmod -R 755 /var/www/html/bootstrap/cache +# Create required directories dan set permissions +RUN mkdir -p \ + storage/framework/cache \ + storage/framework/sessions \ + storage/framework/views \ + storage/logs \ + bootstrap/cache \ + && chown -R www-data:www-data \ + /var/www/html/storage \ + /var/www/html/bootstrap/cache \ + && chmod -R 775 \ + /var/www/html/storage \ + /var/www/html/bootstrap/cache + +# Switch ke user non-root untuk keamanan +USER www-data EXPOSE 9000 + CMD ["php-fpm"] diff --git a/docker-compose.yml b/docker-compose.yml index b7cac6c..3419561 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,58 +1,176 @@ +version: '3.8' + services: + # ======================================== + # Laravel PHP-FPM Application + # ======================================== laravel: build: context: . dockerfile: Dockerfile - container_name: laravel_app_prod + container_name: abbauf_kasir_app + restart: unless-stopped + working_dir: /var/www/html volumes: - - storage_data:/var/www/html/storage - ports: - - "9000" - depends_on: - - mysql + # Mount storage untuk uploads dan logs (persistent) + - ./storage:/var/www/html/storage + # Mount public build assets (read-only) + - ./public/build:/var/www/html/public/build:ro environment: - APP_ENV: production - APP_DEBUG: false + # Application + APP_NAME: ${APP_NAME:-Abbauf-Kasir} + APP_ENV: ${APP_ENV:-production} + APP_KEY: ${APP_KEY} + APP_DEBUG: ${APP_DEBUG:-false} + APP_URL: ${APP_URL:-http://localhost} + + # Database + DB_CONNECTION: mysql + DB_HOST: mysql + DB_PORT: 3306 + DB_DATABASE: ${DB_DATABASE:-kasir_db} + DB_USERNAME: ${DB_USERNAME:-kasir_user} + DB_PASSWORD: ${DB_PASSWORD} + + # Cache & Session + CACHE_STORE: redis + SESSION_DRIVER: redis + REDIS_HOST: redis + REDIS_PORT: 6379 + + # Queue + QUEUE_CONNECTION: redis + depends_on: + mysql: + condition: service_healthy + redis: + condition: service_started + networks: + - kasir_network + healthcheck: + test: ["CMD", "php-fpm", "-t"] + interval: 30s + timeout: 10s + retries: 3 + + # ======================================== + # Nginx Web Server + # ======================================== + nginx: + image: nginx:alpine + container_name: abbauf_kasir_nginx + restart: unless-stopped + ports: + - "${APP_PORT:-80}:80" + volumes: + # Nginx configuration + - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro + # Laravel public directory (untuk static assets) + - ./public:/var/www/html/public:ro + # Storage symlink untuk file uploads + - ./storage/app/public:/var/www/html/public/storage:ro + depends_on: + - laravel + networks: + - kasir_network + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"] + interval: 30s + timeout: 10s + retries: 3 + + # ======================================== + # MySQL Database + # ======================================== + mysql: + image: mysql:8.4 + container_name: abbauf_kasir_db + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root_secret_password} + MYSQL_DATABASE: ${DB_DATABASE:-kasir_db} + MYSQL_USER: ${DB_USERNAME:-kasir_user} + MYSQL_PASSWORD: ${DB_PASSWORD} + MYSQL_CHARACTER_SET_SERVER: utf8mb4 + MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci + ports: + - "${DB_PORT:-3306}:3306" + volumes: + - mysql_data:/var/lib/mysql + # Optional: backup folder + - ./docker/mysql/backups:/backups + networks: + - kasir_network + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-root_secret_password}"] + interval: 10s + timeout: 5s + retries: 5 + command: --default-authentication-plugin=mysql_native_password + + # ======================================== + # Redis Cache & Session Store + # ======================================== + redis: + image: redis:7-alpine + container_name: abbauf_kasir_redis + restart: unless-stopped + ports: + - "${REDIS_PORT:-6379}:6379" + volumes: + - redis_data:/data + networks: + - kasir_network + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 3 + command: redis-server --appendonly yes + + # ======================================== + # Queue Worker (Optional - untuk background jobs) + # ======================================== + queue: + build: + context: . + dockerfile: Dockerfile + container_name: abbauf_kasir_queue + restart: unless-stopped + working_dir: /var/www/html + volumes: + - ./storage:/var/www/html/storage + environment: + APP_ENV: ${APP_ENV:-production} APP_KEY: ${APP_KEY} DB_CONNECTION: mysql DB_HOST: mysql DB_PORT: 3306 - DB_DATABASE: ${DB_DATABASE} - DB_USERNAME: ${DB_USERNAME} + DB_DATABASE: ${DB_DATABASE:-kasir_db} + DB_USERNAME: ${DB_USERNAME:-kasir_user} DB_PASSWORD: ${DB_PASSWORD} - - nginx: - image: nginx:alpine - container_name: nginx_prod - ports: - - "80:80" - volumes: - - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro - volumes_from: - - laravel:ro + REDIS_HOST: redis + QUEUE_CONNECTION: redis depends_on: - laravel + - redis + - mysql + networks: + - kasir_network + command: php artisan queue:work --tries=3 --timeout=90 - mysql: - image: mysql:8 - container_name: mysql_db_prod - restart: unless-stopped - environment: - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} - MYSQL_DATABASE: ${DB_DATABASE} - MYSQL_USER: ${DB_USERNAME} - MYSQL_PASSWORD: ${DB_PASSWORD} - ports: - - "3306:3306" - volumes: - - mysql_data:/var/lib/mysql - - redis: - image: redis:alpine - container_name: redis_prod - ports: - - "6379:6379" +# ======================================== +# Networks +# ======================================== +networks: + kasir_network: + driver: bridge +# ======================================== +# Persistent Volumes +# ======================================== volumes: mysql_data: - storage_data: + driver: local + redis_data: + driver: local diff --git a/nginx.conf b/nginx.conf index 443a33e..0ffd63c 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,14 +1,43 @@ +# ======================================== +# Abbauf Kasir - Nginx Configuration +# ======================================== + server { listen 80; - index index.php index.html; - error_log /var/log/nginx/error.log; - access_log /var/log/nginx/access.log; + listen [::]:80; + server_name localhost; + root /var/www/html/public; + index index.php index.html index.htm; + # Logging + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log warn; + + # 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; + + # Client body size limit (untuk upload file) + client_max_body_size 20M; + + # 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 font/truetype font/opentype + application/vnd.ms-fontobject image/svg+xml; + + # Main location block location / { try_files $uri $uri/ /index.php?$query_string; } + # PHP-FPM configuration location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; @@ -17,5 +46,43 @@ server { include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; + + # Buffer settings untuk performa + fastcgi_buffer_size 128k; + fastcgi_buffers 256 16k; + fastcgi_busy_buffers_size 256k; + fastcgi_temp_file_write_size 256k; + + # Timeout settings + fastcgi_read_timeout 300; + fastcgi_connect_timeout 300; } -} \ No newline at end of file + + # Cache static assets + location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # Deny access to hidden files + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } + + # Deny access to sensitive files + location ~ /(?:\.env|\.git|composer\.json|composer\.lock|package\.json|package-lock\.json|README\.md|\.gitignore) { + deny all; + access_log off; + log_not_found off; + } + + # Health check endpoint + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } +} diff --git a/resources/js/pages/EditProduk.vue b/resources/js/pages/EditProduk.vue index 3af7d7f..89188d1 100644 --- a/resources/js/pages/EditProduk.vue +++ b/resources/js/pages/EditProduk.vue @@ -56,21 +56,20 @@