This commit is contained in:
Saufi
2026-06-24 18:30:00 +08:00
commit c0c3d7c09c
122 changed files with 16321 additions and 0 deletions

105
docker/entrypoint.sh Normal file
View File

@@ -0,0 +1,105 @@
#!/bin/sh
# ──────────────────────────────────────────────────────────────────────────────
# KOIPB MAT — Container Entrypoint
# Jalankan sebelum php-fpm bermula:
# 1. Install Composer deps (jika vendor tiada)
# 2. Tunggu MySQL
# 3. Generate APP_KEY jika tiada
# 4. Migrate + seed peranan/pengguna (idempotent)
# 5. Storage link + permissions
# 6. Cache (prod sahaja)
# ──────────────────────────────────────────────────────────────────────────────
set -e
echo ""
echo "╔════════════════════════════════════════╗"
echo "║ KOIPB MAT — Container Start ║"
echo "╚════════════════════════════════════════╝"
echo ""
# ── 1. Pasang Composer dependencies ───────────────────────────────────────────
if [ ! -d /var/www/html/vendor ]; then
echo "📦 Memasang Composer dependencies..."
if [ "${APP_ENV}" = "production" ]; then
composer install --no-interaction --no-dev --no-progress --prefer-dist --optimize-autoloader
else
composer install --no-interaction --no-progress --prefer-dist
fi
fi
# ── 2. Tunggu MySQL bersedia ──────────────────────────────────────────────────
DB_HOST="${DB_HOST:-mysql}"
DB_PORT="${DB_PORT:-3306}"
DB_DATABASE="${DB_DATABASE:-koipb_mat}"
DB_USERNAME="${DB_USERNAME:-koipb}"
DB_PASSWORD="${DB_PASSWORD:-secret}"
echo "⏳ Menunggu MySQL di ${DB_HOST}:${DB_PORT}..."
until php -r "
try {
new PDO('mysql:host=${DB_HOST};port=${DB_PORT};dbname=${DB_DATABASE}', '${DB_USERNAME}', '${DB_PASSWORD}');
exit(0);
} catch (Exception \$e) { exit(1); }
" 2>/dev/null; do
printf "."
sleep 2
done
echo ""
echo "✓ MySQL bersedia."
# ── 2b. Fix storage permissions ───────────────────────────────────────────────
mkdir -p /var/www/html/storage/framework/views \
/var/www/html/storage/framework/cache/data \
/var/www/html/storage/framework/sessions \
/var/www/html/storage/logs \
/var/www/html/storage/app/public \
/var/www/html/bootstrap/cache
chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache 2>/dev/null || true
chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache 2>/dev/null || true
# ── 3. Generate APP_KEY jika kosong ───────────────────────────────────────────
if [ -z "${APP_KEY}" ]; then
echo "🔑 Menjana APP_KEY..."
php artisan key:generate --force
fi
# Penting: env_file mungkin hantar APP_KEY="" (kosong) sebagai env var sebenar
# yang mengatasi nilai dalam .env. Baca semula dari .env & EXPORT supaya
# proses anak (migrate, config:cache, php-fpm) mewarisi kunci yang betul.
if [ -f /var/www/html/.env ]; then
FILE_KEY=$(grep -E '^APP_KEY=' /var/www/html/.env | head -1 | cut -d= -f2-)
if [ -n "${FILE_KEY}" ]; then
export APP_KEY="${FILE_KEY}"
fi
fi
# ── 4. Migration + seed peranan & pengguna (idempotent) ───────────────────────
echo "🗄 Menjalankan migration..."
php artisan migrate --force
echo "👤 Seeding peranan & pengguna (RoleUserSeeder)..."
php artisan db:seed --class=RoleUserSeeder --force
# Seed data demo (200 anggota + 21 hadiah) hanya bila SEED_DEMO=true
if [ "${SEED_DEMO}" = "true" ]; then
echo "🎁 Seeding data demo penuh (DatabaseSeeder)..."
php artisan db:seed --force
fi
# ── 5. Storage symbolic link ──────────────────────────────────────────────────
php artisan storage:link 2>/dev/null || true
# ── 6. Cache (production sahaja) ──────────────────────────────────────────────
if [ "${APP_ENV}" = "production" ]; then
echo "⚡ Caching config, routes, views..."
php artisan config:cache
php artisan route:cache
php artisan view:cache
else
php artisan optimize:clear 2>/dev/null || true
fi
echo ""
echo "✅ Aplikasi bersedia."
echo ""
exec "$@"

75
docker/nginx/default.conf Normal file
View File

@@ -0,0 +1,75 @@
# ──────────────────────────────────────────────────────────────────────────────
# KOIPB MAT — Nginx Server Block
# Document root: /var/www/html/public | PHP-FPM upstream: app:9000
# ──────────────────────────────────────────────────────────────────────────────
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
gzip_types
text/plain text/css text/javascript application/javascript
application/json application/xml image/svg+xml font/woff2;
server {
listen 80;
server_name _;
root /var/www/html/public;
index index.php;
# Had muat naik (kena sama dengan php.ini: post_max_size)
client_max_body_size 25M;
charset utf-8;
# ── Security headers ──────────────────────────────────────────────────────
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# ── Laravel routes ────────────────────────────────────────────────────────
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# ── PHP-FPM ───────────────────────────────────────────────────────────────
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_read_timeout 120s;
fastcgi_connect_timeout 10s;
fastcgi_buffer_size 16k;
fastcgi_buffers 8 16k;
}
# ── Static assets — cache 1 tahun ─────────────────────────────────────────
location ~* \.(jpg|jpeg|png|gif|ico|svg|css|js|woff2?|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
try_files $uri =404;
}
# ── Halang akses fail tersembunyi ─────────────────────────────────────────
location ~ /\. {
deny all;
}
# ── Halang akses terus ke fail sensitif ───────────────────────────────────
location ~* \.(env|log|htaccess|htpasswd|ini|sh|sql|bak)$ {
deny all;
}
access_log /var/log/nginx/koipb-access.log;
error_log /var/log/nginx/koipb-error.log warn;
}

13
docker/php/php-dev.ini Normal file
View File

@@ -0,0 +1,13 @@
; ──────────────────────────────────────────────────────────────────────────────
; KOIPB MAT — Development overrides (dimuat selepas php.ini)
; Hanya dimuat dalam docker-compose.yml (dev), tidak dalam prod
; ──────────────────────────────────────────────────────────────────────────────
; Reload fail PHP tanpa restart container
opcache.validate_timestamps = 1
opcache.revalidate_freq = 0
; Tunjuk ralat semasa pembangunan
display_errors = On
display_startup_errors = On
error_reporting = E_ALL

11
docker/php/php-prod.ini Normal file
View File

@@ -0,0 +1,11 @@
; ──────────────────────────────────────────────────────────────────────────────
; KOIPB MAT — Production overrides (dimuat selepas php.ini)
; Hanya dimuat dalam docker-compose.prod.yml
; ──────────────────────────────────────────────────────────────────────────────
; Prestasi: jangan semak timestamp fail (kosongkan cache bila deploy)
opcache.validate_timestamps = 0
; Sembunyikan ralat dari pengguna
display_errors = Off
display_startup_errors = Off

45
docker/php/php.ini Normal file
View File

@@ -0,0 +1,45 @@
; ──────────────────────────────────────────────────────────────────────────────
; KOIPB MAT — PHP Runtime Configuration
; Letakkan dalam /usr/local/etc/php/conf.d/99-koipb.ini
; ──────────────────────────────────────────────────────────────────────────────
; Timezone Malaysia
date.timezone = Asia/Kuala_Lumpur
; ── Muat naik (import CSV/XLSX anggota & hadiah) ──────────────────────────────
upload_max_filesize = 20M
post_max_size = 25M
max_file_uploads = 10
; ── Pelaksanaan ───────────────────────────────────────────────────────────────
max_execution_time = 120
max_input_time = 120
; ── Memori (eksport PDF/XLSX) ─────────────────────────────────────────────────
memory_limit = 512M
; ── Session ───────────────────────────────────────────────────────────────────
session.cookie_httponly = 1
session.cookie_samesite = Lax
session.gc_maxlifetime = 7200
; ── OPcache ───────────────────────────────────────────────────────────────────
opcache.enable = 1
opcache.memory_consumption = 192
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 10000
; validate_timestamps: 1 untuk dev (auto-reload), override 0 untuk prod
opcache.validate_timestamps = 1
opcache.revalidate_freq = 2
opcache.fast_shutdown = 1
; ── Ralat (default selamat — override per environment) ────────────────────────
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
output_buffering = 4096
sys_temp_dir = /tmp
upload_tmp_dir = /tmp