diff --git a/.env.production b/.env.production index 87617b6..2919b12 100644 --- a/.env.production +++ b/.env.production @@ -1,9 +1,9 @@ -APP_NAME="SUTERA 3.0" +APP_NAME="MyKOPKB 1.0" APP_ENV=production APP_KEY=base64:XdMAy5IbQMHsTrp1N7Tn/LIJC4SKenN8CfhYLt1s3tk= APP_DEBUG=false APP_TIMEZONE=Asia/Kuala_Lumpur -APP_URL=http://20.11.32.50 +APP_URL=https://anggota.koppkb.com APP_LOCALE=en APP_FALLBACK_LOCALE=en @@ -22,11 +22,11 @@ LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=error DB_CONNECTION=pgsql -DB_HOST=20.11.32.56 -DB_PORT=5432 -DB_DATABASE=sutera -DB_USERNAME=suterauser -DB_PASSWORD=Aretus@Production_2025 +DB_HOST=172.16.5.12 +DB_PORT=5433 +DB_DATABASE=mykopkb +DB_USERNAME=mykopkbuser +DB_PASSWORD=MyKOPKB@Production_2026 SESSION_DRIVER=redis SESSION_LIFETIME=60 @@ -42,7 +42,7 @@ HORIZON_WORKER_MEMORY=512 HORIZON_WORKER_TIMEOUT=3600 CACHE_STORE=redis -CACHE_PREFIX=sutera_cache +CACHE_PREFIX=mykopkb_cache CACHE_DRIVER=redis MEMCACHED_HOST=memcached @@ -50,7 +50,7 @@ MEMCACHED_HOST=memcached REDIS_CLIENT=phpredis REDIS_HOST=redis REDIS_PORT=6379 -REDIS_PASSWORD=sutera_redis@2025 +REDIS_PASSWORD=mykopkb_redis@2026 REDIS_DB=0 # only change this to smtp if is deployed to public server @@ -76,22 +76,22 @@ VITE_APP_NAME="${APP_NAME}" # VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" # VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" -EXTERNAL_API_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsibWJzcC1yZXNvdXJjZUlkIl0sInRpbWVvdXRQZXJpb2QiOjkwMCwidXNlcl9uYW1lIjoic3BhLXVzZXIiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNzI4NTgzNjEyLCJqdGkiOiJjMmU2OTkxOS0yZDU3LTQ5YmYtYTVmZS1lZjVmMzIyNDZkZDIiLCJjbGllbnRfaWQiOiJzcGEtY2xpZW50In0.LE2byaRzsYkchzI4ox9Gz2u6jNmTqZQa-_ttR2PY8e8" -EXTERNAL_API_BASE_URL=http://20.11.32.134/rest/td/gasset/asset +EXTERNAL_API_TOKEN="" +EXTERNAL_API_BASE_URL= -SSO_SECRET="Y6g#Rb@Km=Nz]o^X+fEC~ [], diff --git a/docker/common/unified/Dockerfile b/docker/common/unified/Dockerfile index 8a506eb..7bd634a 100644 --- a/docker/common/unified/Dockerfile +++ b/docker/common/unified/Dockerfile @@ -1,33 +1,3 @@ -# Stage 0: Build Vue frontend assets with Node.js -FROM node:22 AS frontend-builder - -WORKDIR /app/frontend - -# Copy frontend package files -COPY fe/package*.json ./ -COPY fe/pnpm-lock.yaml* ./ - -# Install pnpm and frontend dependencies (including dev dependencies for build) -RUN npm install -g pnpm -RUN pnpm install --frozen-lockfile - -# Copy frontend source code -COPY fe/ ./ - -# Build TWO frontend bundles (path-based): -# - Production: served at / -# - Training: served at /training/ -# -# This avoids runtime JS injection and allows training/prod UI differences -# while keeping a single backend image. -RUN pnpm run typecheck \ - && pnpm exec vite build --mode=production --base=/ --outDir dist-prod \ - && pnpm exec vite build --mode=training --base=/training/ --outDir dist-training - -# Clean up dev dependencies to reduce image size -ENV CI=true -RUN pnpm prune --prod - # Stage 1: Build environment and Composer dependencies FROM php:8.4-fpm AS builder @@ -150,12 +120,6 @@ RUN echo '[www]' > /usr/local/etc/php-fpm.d/zzz-status.conf && \ # Copy the application code and dependencies from the build stage first COPY --from=builder /var/www /var/www -# Copy the built Vue frontends from frontend-builder stage -# - Production frontend at / -COPY --from=frontend-builder /app/frontend/dist-prod /var/www/public/ -# - Training frontend at /training/ -COPY --from=frontend-builder /app/frontend/dist-training /var/www/public/training/ - # Set working directory WORKDIR /var/www @@ -173,3 +137,8 @@ EXPOSE 80 9000 # Start supervisor which manages both Nginx and PHP-FPM CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"] + +# CI: install dev dependencies and run PHPUnit (build fails if tests fail) +FROM builder AS backend-test +RUN composer install --no-interaction --prefer-dist --no-scripts \ + && ./vendor/bin/phpunit --no-coverage diff --git a/docker/production/docker-compose.production.yml b/docker/production/docker-compose.production.yml index 242719d..9174a8c 100644 --- a/docker/production/docker-compose.production.yml +++ b/docker/production/docker-compose.production.yml @@ -1,16 +1,10 @@ services: - app: - build: - context: . - dockerfile: ./be/docker/common/unified/Dockerfile - platforms: - - linux/amd64 - - linux/arm64 - image: git.ismailmasseran.com/topaz/sutera:v3.0.1b - container_name: sutera-app-production + backend: + image: git.koppkb.com/KoPKB/My-KOPKB-be:${IMAGE_TAG:-latest} + container_name: mykopkb-be-production restart: unless-stopped ports: - - "${APP_PORT:-80}:80" + - "${API_PORT:-8080}:80" environment: - APP_ENV=production - APP_DEBUG=true @@ -23,9 +17,9 @@ services: - app-storage-production:/var/www/storage - app-logs-production:/var/www/storage/logs - ./.env.production:/var/www/.env:ro - - ./be/docker/production/nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ${NGINX_CONF:-./be/docker/production/nginx/nginx.conf}:/etc/nginx/nginx.conf:ro networks: - - sutera-production-network + - mykopkb-production-network depends_on: redis: condition: service_healthy @@ -41,17 +35,36 @@ services: max-size: "10m" max-file: "3" - # Redis for caching and sessions + frontend: + image: git.koppkb.com/KoPKB/My-KOPKB-fe:${IMAGE_TAG:-latest} + container_name: mykopkb-fe-production + restart: unless-stopped + ports: + - "${FE_PORT:-8081}:80" + networks: + - mykopkb-production-network + healthcheck: + test: ["CMD", "wget", "-q", "--spider", "http://localhost/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + redis: image: redis:8 - container_name: sutera-redis-production + container_name: mykopkb-redis-production restart: unless-stopped volumes: - redis-data-production:/data - ./redis-config/redis.conf:/usr/local/etc/redis/redis.conf:ro command: redis-server /usr/local/etc/redis/redis.conf networks: - - sutera-production-network + - mykopkb-production-network healthcheck: test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] interval: 10s @@ -65,7 +78,7 @@ services: max-file: "3" networks: - sutera-production-network: + mykopkb-production-network: driver: bridge volumes: diff --git a/docker/production/nginx/nginx.conf b/docker/production/nginx/nginx.conf index ac3c643..0e5aa46 100644 --- a/docker/production/nginx/nginx.conf +++ b/docker/production/nginx/nginx.conf @@ -9,74 +9,40 @@ http { sendfile on; keepalive_timeout 65; - # Logging access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; - # Production HTTP server + # API-only server (api.koppkb.com via host reverse proxy) server { listen 80; server_name _; root /var/www/public; - index index.php index.html; + index index.php; - # Security headers 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; - # API routes - pass directly to Laravel with original REQUEST_URI location / { - fastcgi_pass 127.0.0.1:9000; - fastcgi_index index.php; - fastcgi_param SCRIPT_FILENAME $document_root/index.php; - include fastcgi_params; - fastcgi_param HTTP_PROXY ""; - fastcgi_param HTTPS $https if_not_empty; - fastcgi_read_timeout 300; - fastcgi_send_timeout 300; + try_files $uri $uri/ /index.php?$query_string; } - # PRODUCTION: /training/ path is disabled - return 404 - location ^~ /training/ { - return 404; - } - - # Serve frontend assets - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; - try_files $uri =404; - } - - # Handle all other routes - serve Vue app or Laravel - location / { - add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; - try_files $uri $uri/ /index.html /index.php?$query_string; - } - - # Handle PHP files - connect to localhost PHP-FPM location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; - - # Additional FastCGI parameters fastcgi_param HTTP_PROXY ""; fastcgi_param HTTPS $https if_not_empty; fastcgi_read_timeout 300; fastcgi_send_timeout 300; } - # Deny access to hidden files location ~ /\. { deny all; } - # Health check endpoint for Docker location = /health { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; diff --git a/docker/staging/docker-compose.staging.yml b/docker/staging/docker-compose.staging.yml index 3217647..4ae6487 100644 --- a/docker/staging/docker-compose.staging.yml +++ b/docker/staging/docker-compose.staging.yml @@ -30,8 +30,8 @@ services: volumes: - app-storage-staging:/var/www/storage - app-logs-staging:/var/www/storage/logs - - /etc/letsencrypt/live/sutera.ismailmasseran.com/fullchain.pem:/etc/nginx/ssl/fullchain.pem:ro - - /etc/letsencrypt/live/sutera.ismailmasseran.com/privkey.pem:/etc/nginx/ssl/privkey.pem:ro + - /etc/letsencrypt/live/sutera.koppkb.com/fullchain.pem:/etc/nginx/ssl/fullchain.pem:ro + - /etc/letsencrypt/live/sutera.koppkb.com/privkey.pem:/etc/nginx/ssl/privkey.pem:ro networks: - sutera-staging-network depends_on: diff --git a/docker/training/docker-compose.training.yml b/docker/training/docker-compose.training.yml index 030aae7..7333049 100644 --- a/docker/training/docker-compose.training.yml +++ b/docker/training/docker-compose.training.yml @@ -6,7 +6,7 @@ services: platforms: - linux/amd64 - linux/arm64 - image: git.ismailmasseran.com/topaz/sutera:33a53e08a641152f255f5c3fa27765200add4f45 + image: git.koppkb.com/topaz/sutera:33a53e08a641152f255f5c3fa27765200add4f45 container_name: sutera-app-training restart: unless-stopped ports: