feat: Docker Compose setup untuk development & production

- docker/php/Dockerfile: PHP 8.4-FPM + GD + imagick (PECL) + semua extension Laravel
- docker/php/php.ini: upload 20MB, memory 512MB, opcache, Asia/Kuala_Lumpur
- docker/php/php-dev.ini: validate_timestamps=1, display_errors=On (dev)
- docker/nginx/default.conf: gzip, security headers, static asset caching
- docker/entrypoint.sh: tunggu MySQL → migrate → seed AdminSeeder → cache (prod)
- docker-compose.yml: dev stack — port 8003, DB host 33060, queue worker
- docker-compose.prod.yml: production overrides — storage volume, no DB port exposed
- .env.docker: template env untuk Docker (DB_HOST=db)
- .dockerignore: exclude node_modules, vendor, .env, logs

fix: testGenerate try/catch kembalikan JSON error (bukan HTML 500)
fix: loadPreview() semak r.ok, tunjuk error alert, loading spinner

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Saufi
2026-05-18 15:36:47 +08:00
parent c9b50ccc5e
commit 576c71c960
11 changed files with 613 additions and 5 deletions

70
docker/php/Dockerfile Normal file
View File

@@ -0,0 +1,70 @@
###############################################################################
# eCert MBIP — PHP-FPM Runtime Image
# PHP 8.4 + GD + imagick + semua extension Laravel
###############################################################################
FROM php:8.4-fpm
LABEL org.opencontainers.image.title="eCert MBIP" \
org.opencontainers.image.description="Sistem Pengurusan Sijil Digital MBIP"
# ── System libraries ──────────────────────────────────────────────────────────
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
zip \
unzip \
# zip extension
libzip-dev \
# GD: PNG, JPEG, WebP, FreeType
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
libwebp-dev \
# mbstring
libonig-dev \
# xml / intl
libxml2-dev \
libicu-dev \
# imagick
libmagickwand-dev \
# mysql client (wait-for-db in entrypoint)
default-mysql-client \
&& rm -rf /var/lib/apt/lists/*
# ── GD (untuk Intervention Image — GD driver) ─────────────────────────────────
RUN docker-php-ext-configure gd \
--with-freetype \
--with-jpeg \
--with-webp \
&& docker-php-ext-install -j$(nproc) gd
# ── Core PHP extensions ───────────────────────────────────────────────────────
RUN docker-php-ext-install -j$(nproc) \
pdo_mysql \
mbstring \
exif \
pcntl \
bcmath \
zip \
intl \
opcache
# ── imagick (PECL) — tersedia sebagai driver alternatif ───────────────────────
RUN pecl install imagick \
&& docker-php-ext-enable imagick \
&& rm -rf /tmp/pear
# ── Composer ──────────────────────────────────────────────────────────────────
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
# ── Working directory ─────────────────────────────────────────────────────────
WORKDIR /var/www
# ── Entrypoint ────────────────────────────────────────────────────────────────
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
EXPOSE 9000
ENTRYPOINT ["entrypoint.sh"]
CMD ["php-fpm"]

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

@@ -0,0 +1,13 @@
; ──────────────────────────────────────────────────────────────────────────────
; Development overrides — dilengkap di atas 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 dalam browser semasa dev
display_errors = On
display_startup_errors = On
error_reporting = E_ALL

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

@@ -0,0 +1,44 @@
; ──────────────────────────────────────────────────────────────────────────────
; eCert MBIP — PHP Runtime Configuration
; Letakkan dalam /usr/local/etc/php/conf.d/99-ecert.ini
; ──────────────────────────────────────────────────────────────────────────────
; Timezone Malaysia
date.timezone = Asia/Kuala_Lumpur
; ── Upload (template sijil hingga 10MB + buffer) ──────────────────────────────
upload_max_filesize = 20M
post_max_size = 25M
max_file_uploads = 10
; ── Execution (image generation + queue workers) ──────────────────────────────
max_execution_time = 120
max_input_time = 120
; ── Memory (Intervention Image untuk sijil resolusi tinggi) ───────────────────
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), 0 untuk prod (prestasi)
opcache.validate_timestamps = 1
opcache.revalidate_freq = 2
opcache.fast_shutdown = 1
; ── Error (override per environment via APP_ENV) ──────────────────────────────
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
; ── imagick ───────────────────────────────────────────────────────────────────
[imagick]
imagick.skip_version_check = 1