Sistem untuk admin DUN merekod waktu KTM (Ketua Tempat Mengundi) hadir mengambil peti undi. Data KTM dibaca daripada DB sppm (spr2026) melalui connection kedua — hanya penempatan berjawatan KTM yang dipaparkan. - Dashboard: senarai KTM ikut pusat mengundi (asc) & saluran (asc), status BELUM HADIR (merah) / HADIR (hijau) + tarikh & masa - Carian nama/no. KP, tapisan pusat mengundi & status - Tanda hadir / batal dengan kawalan DUN (admin terhad ke DUN sendiri) - Eksport Excel (worksheet Hadir & Belum Hadir) guna SimpleXlsx - Peranan: superadmin (urus pengguna, semua DUN) & admin DUN - Docker: nginx + php-fpm + queue + scheduler + webhook deploy, port 127.0.0.1:8009, TZ Asia/Kuala_Lumpur di semua container - Zon waktu aplikasi: Asia/Kuala_Lumpur (APP_TIMEZONE) Berasaskan scaffold spr2026_taklimat. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
162 lines
5.0 KiB
Markdown
162 lines
5.0 KiB
Markdown
# Running with Docker
|
|
|
|
This app is packaged as a small Docker stack and is designed to run **behind
|
|
the host's existing Nginx**, connecting to the **host's existing MySQL** (where
|
|
both `sppm_ktm` and the external `sppm` database live).
|
|
|
|
```
|
|
Internet
|
|
│
|
|
▼
|
|
host nginx (ktmspr.apps.mbip.my, port 80) ← reverse proxy
|
|
│ proxy_pass 127.0.0.1:8009
|
|
▼
|
|
web container (nginx) ──fastcgi──► app container (php-fpm 8.4)
|
|
│
|
|
▼ host.docker.internal:3306
|
|
host MySQL (sppm_ktm, sppm)
|
|
```
|
|
|
|
Containers:
|
|
|
|
| Service | Image | Role |
|
|
|-------------|--------------------|-------------------------------------------------|
|
|
| `web` | `ktmspr-web` | nginx, serves static assets, proxies PHP. Binds `127.0.0.1:8009`. |
|
|
| `app` | `ktmspr-app` | PHP 8.4-FPM. Runs migrations on boot. |
|
|
| `queue` | `ktmspr-app` | `queue:work` (database queue). |
|
|
| `scheduler` | `ktmspr-app` | `schedule:work` (Laravel scheduler). |
|
|
|
|
---
|
|
|
|
## Prerequisites on the host (Ubuntu 24.04)
|
|
|
|
- Docker Engine + Compose plugin:
|
|
```bash
|
|
sudo apt-get update
|
|
sudo apt-get install -y docker.io docker-compose-v2
|
|
sudo systemctl enable --now docker
|
|
```
|
|
- The existing Nginx and MySQL services already installed on the host.
|
|
|
|
---
|
|
|
|
## 1. Configure host MySQL to accept connections from containers
|
|
|
|
On Linux, `host.docker.internal` resolves to the Docker bridge gateway (e.g.
|
|
`172.17.0.1`) via the `host-gateway` mapping already set in
|
|
`docker-compose.yml`. The host MySQL must accept connections on that interface.
|
|
|
|
**a) Listen on all interfaces** — edit
|
|
`/etc/mysql/mysql.conf.d/mysqld.cnf`:
|
|
|
|
```ini
|
|
bind-address = 0.0.0.0
|
|
```
|
|
Then restart: `sudo systemctl restart mysql`.
|
|
|
|
**b) Allow the app user from the Docker subnet.** Prefer a dedicated user over
|
|
`root` (and then set the credentials in `.env.docker`):
|
|
|
|
```sql
|
|
CREATE USER 'ktm'@'172.%' IDENTIFIED BY 'CHANGE_ME_STRONG';
|
|
GRANT ALL PRIVILEGES ON sppm_ktm.* TO 'ktm'@'172.%';
|
|
GRANT SELECT ON sppm.* TO 'ktm'@'172.%';
|
|
FLUSH PRIVILEGES;
|
|
```
|
|
|
|
Make sure the `sppm_ktm` database exists:
|
|
```sql
|
|
CREATE DATABASE IF NOT EXISTS sppm_ktm
|
|
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
```
|
|
|
|
**c) Firewall** — if `ufw` is active, allow MySQL from the Docker subnet only:
|
|
```bash
|
|
sudo ufw allow from 172.16.0.0/12 to any port 3306 proto tcp
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Set the container environment
|
|
|
|
`.env.docker` holds the runtime config (it is git-ignored because it contains
|
|
credentials). Review it and update at least:
|
|
|
|
- `DB_USERNAME` / `DB_PASSWORD` and `SPPM_DB_*` to match step 1b.
|
|
- `APP_KEY` is pre-filled; to rotate it run
|
|
`docker compose run --rm app php artisan key:generate --show` and paste the
|
|
result.
|
|
|
|
`DB_HOST` / `SPPM_DB_HOST` are already set to `host.docker.internal` — leave them.
|
|
|
|
---
|
|
|
|
## 3. Build and start
|
|
|
|
```bash
|
|
docker compose build
|
|
docker compose up -d
|
|
```
|
|
|
|
On boot the `app` container waits for MySQL, caches config/routes/views, runs
|
|
`php artisan migrate --force`, and starts php-fpm. Check it:
|
|
|
|
```bash
|
|
docker compose ps
|
|
docker compose logs -f app
|
|
curl -I http://127.0.0.1:8009 # should return HTTP 200/302
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Point host Nginx at the stack
|
|
|
|
```bash
|
|
sudo cp docker/host-nginx/ktmspr.apps.mbip.my.conf \
|
|
/etc/nginx/sites-available/ktmspr.apps.mbip.my
|
|
sudo ln -s /etc/nginx/sites-available/ktmspr.apps.mbip.my \
|
|
/etc/nginx/sites-enabled/
|
|
sudo nginx -t && sudo systemctl reload nginx
|
|
```
|
|
|
|
The app should now be reachable at <http://ktmspr.apps.mbip.my>.
|
|
|
|
---
|
|
|
|
## Common operations
|
|
|
|
```bash
|
|
# Tail logs
|
|
docker compose logs -f app web queue
|
|
|
|
# Artisan / composer inside the app container
|
|
docker compose exec app php artisan about
|
|
docker compose exec app php artisan tinker
|
|
|
|
# Re-cache config after editing .env.docker
|
|
docker compose exec app php artisan config:cache
|
|
|
|
# Rebuild & redeploy after pulling new code
|
|
docker compose build && docker compose up -d
|
|
```
|
|
|
|
---
|
|
|
|
## Notes & limitations
|
|
|
|
- **Static uploads:** `web` (nginx) serves a baked copy of `public/`. Files
|
|
written at runtime to `storage/app/public` (the `public` disk + `storage:link`)
|
|
live only in the `app` container and are **not** served by nginx. The current
|
|
app renders QR codes as inline SVG, so this isn't needed today — but if you
|
|
later serve uploaded files via `/storage`, switch `web` to share the app's
|
|
public/storage via a volume.
|
|
- **Persistent data:** the named volume `app_storage` keeps logs, compiled
|
|
views, and `storage/app` across restarts. Application/session data lives in
|
|
MySQL.
|
|
- **TLS:** terminate HTTPS at the host Nginx (e.g. with certbot) if/when the
|
|
domain moves to `https://`. Then add `SESSION_SECURE_COOKIE=true` and
|
|
`APP_URL=https://...` in `.env.docker`.
|
|
- **HEALTHCHECK / scheduler:** there are currently no scheduled commands, but
|
|
the `scheduler` service is included so future `app/Console` schedules work
|
|
without changing the stack. Remove it if you prefer.
|