194 lines
9.3 KiB
Markdown
194 lines
9.3 KiB
Markdown
# External Integrations
|
|
|
|
**Analysis Date:** 2026-05-28
|
|
|
|
## APIs & External Services
|
|
|
|
**Firebase Cloud Messaging (FCM):**
|
|
- Service: Firebase Cloud Messaging — push notification delivery to mobile devices via topic-based messaging
|
|
- Implementation: `src/app/Services/FcmService.php`
|
|
- Auth: Service Account JSON file (path configured via `FIREBASE_CREDENTIALS` env var); uses `Google\Auth\Credentials\ServiceAccountCredentials` to fetch OAuth2 access tokens
|
|
- Scopes: `https://www.googleapis.com/auth/firebase.messaging`
|
|
- Endpoint: `https://fcm.googleapis.com/v1/projects/{projectId}/messages:send`
|
|
- Env vars: `FIREBASE_PROJECT_ID`, `FIREBASE_CREDENTIALS`
|
|
- Topic mapping: `FCM_TOPIC_RAINFALL_WARNING` env var (set in `src/app/Http/Controllers/Api/AlertController.php`)
|
|
|
|
**Google Auth Library:**
|
|
- Package: `google/auth ^1.49` (`src/composer.json`)
|
|
- Purpose: Generates OAuth2 bearer tokens for Firebase API calls using service account credentials
|
|
- Implementation: `src/app/Services/FcmService.php` — creates `ServiceAccountCredentials` instance and calls `fetchAuthToken()`
|
|
|
|
**Amazon SES (Simple Email Service):**
|
|
- Configured in `src/config/services.php` — `ses` key with `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`
|
|
- Mail transport: `ses` driver in `src/config/mail.php`
|
|
- Also used as a transport in `roundrobin` failover group
|
|
|
|
**Postmark:**
|
|
- Configured in `src/config/services.php` — `postmark` key with `POSTMARK_API_KEY` env var
|
|
- Mail transport: `postmark` driver in `src/config/mail.php`
|
|
- Optional `message_stream_id` config commented out
|
|
- Used in `roundrobin` failover group with SES
|
|
|
|
**Resend:**
|
|
- Configured in `src/config/services.php` — `resend` key with `RESEND_API_KEY` env var
|
|
- Mail transport: `resend` driver in `src/config/mail.php`
|
|
|
|
**Slack:**
|
|
- Notifications: configured in `src/config/services.php` — `slack.notifications` with `SLACK_BOT_USER_OAUTH_TOKEN` and `SLACK_BOT_USER_DEFAULT_CHANNEL` env vars
|
|
- Logging: Slack webhook channel available in `src/config/logging.php` — `LOG_SLACK_WEBHOOK_URL` env var for critical-level log alerts
|
|
|
|
**Papertrail:**
|
|
- Logging channel configured in `src/config/logging.php` — `SyslogUdpHandler` via `PAPERTRAIL_URL` and `PAPERTRAIL_PORT` env vars with TLS
|
|
|
|
## Data Storage
|
|
|
|
**Databases:**
|
|
- PostgreSQL 18.1
|
|
- Container: `sides-db` (image `postgres:18.1`)
|
|
- Connection: `pgsql` driver; env vars `DB_HOST`, `DB_PORT` (5432), `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD`
|
|
- Persistence: `./docker/postgres/data:/var/lib/postgres/data`
|
|
- Exposed port: 5432
|
|
- Schema: `public`
|
|
- Client library: `pdo_pgsql` and `pgsql` PHP extensions installed in Docker image
|
|
|
|
**Redis:**
|
|
- Configured in `src/config/database.php` under the `redis` key
|
|
- Client: `phpredis` (configured via `REDIS_CLIENT` env var)
|
|
- Separate connections for `default` (DB 0) and `cache` (DB 1)
|
|
- Retry/backoff: decorrelated jitter strategy, max 3 retries, 100ms base, 1000ms cap
|
|
- Used as optional driver for: cache (`CACHE_STORE=redis`), queue (`QUEUE_CONNECTION=redis`), session (`SESSION_DRIVER=redis`)
|
|
|
|
**File Storage:**
|
|
- Local disk (`local`): `storage/app/private/` — default filesystem
|
|
- Public disk (`public`): `storage/app/public/` — symlinked to `public/storage`
|
|
- S3 disk (`s3`) configured for AWS S3: env vars `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`, `AWS_BUCKET`, `AWS_URL`, `AWS_ENDPOINT`
|
|
- Default disk: selected via `FILESYSTEM_DISK` env var (falls back to `local`)
|
|
|
|
## Authentication & Identity
|
|
|
|
**Auth Provider:**
|
|
- Laravel's built-in session-based authentication (`src/config/auth.php`)
|
|
- Guard: `web` driver using `session`
|
|
- Provider: `users` using `eloquent` driver on `App\Models\User`
|
|
- Password resets via `password_reset_tokens` table; 60-minute expiry; 60-second throttle
|
|
- Custom `access_level` field on `users` table (integer, default `2`), with `is_blocked` and `login_attempts` fields for account lockout
|
|
- API authentication: custom tokenless login in `src/app/Http/Controllers/Api/AuthController.php` — validates username/password via raw SQL and `Hash::check()`, returns user data with `access_level`
|
|
|
|
**Email Verification:**
|
|
- Built-in Laravel email verification flow (`MustVerifyEmail` trait available but not used in `App\Models\User`)
|
|
- Routes use `signed` middleware with throttling (`6:1`)
|
|
|
|
## Monitoring & Observability
|
|
|
|
**Error Tracking:**
|
|
- Not detected — no Sentry, Bugsnag, or similar APM integration
|
|
|
|
**Logs:**
|
|
- Laravel logging via Monolog (`src/config/logging.php`)
|
|
- Default channel: `stack` → `single` (writes to `storage/logs/laravel.log`)
|
|
- Available channels: `single`, `daily` (14-day retention), `slack`, `papertrail`, `stderr`, `syslog`, `errorlog`
|
|
- `pail` CLI log viewer available in dev (`laravel/pail ^1.2.2`)
|
|
|
|
**Dozzle:**
|
|
- Log viewer container (`amir20/dozzle:latest`) in `docker-compose.yml`
|
|
- Port: 777
|
|
- Container actions and shell access enabled
|
|
|
|
## CI/CD & Deployment
|
|
|
|
**Hosting:**
|
|
- Not explicitly declared — Docker-based deployment assumed
|
|
- Artisan serve on port 8000 (dev); Nginx on port 8080/8443 (production via Docker)
|
|
- `composer setup` script performs full bootstrap: `composer install`, `.env` creation, `key:generate`, migrations, npm install + build
|
|
|
|
**CI Pipeline:**
|
|
- Not detected — no GitHub Actions, GitLab CI, or similar config files found
|
|
|
|
## Environment Configuration
|
|
|
|
**Required env vars (from code analysis):**
|
|
| Variable | Used In | Purpose |
|
|
|---|---|---|
|
|
| `APP_KEY` | `config/app.php` | Laravel encryption key (AES-256-CBC) |
|
|
| `APP_ENV` | `config/app.php` | Environment (local/production) |
|
|
| `APP_DEBUG` | `config/app.php` | Debug mode toggle |
|
|
| `APP_URL` | `config/app.php`, `config/filesystems.php` | Application base URL |
|
|
| `APP_NAME` | `config/app.php`, `config/session.php`, `config/cache.php` | Application name |
|
|
| `DB_CONNECTION` | `config/database.php`, `config/queue.php` | Database connection (default: `sqlite`) |
|
|
| `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD` | `config/database.php` | PostgreSQL connection |
|
|
| `SESSION_DRIVER` | `config/session.php` | Session backend (default: `database`) |
|
|
| `SESSION_DRIVER` | `config/session.php` | Session backend (default: `database`) |
|
|
| `QUEUE_CONNECTION` | `config/queue.php` | Queue driver (default: `database`) |
|
|
| `CACHE_STORE` | `config/cache.php` | Cache backend (default: `database`) |
|
|
| `FILESYSTEM_DISK` | `config/filesystems.php` | Storage disk (default: `local`) |
|
|
| `MAIL_MAILER` | `config/mail.php` | Mail driver (default: `log`) |
|
|
| `FIREBASE_PROJECT_ID` | `FcmService.php` | Firebase project identifier |
|
|
| `FIREBASE_CREDENTIALS` | `FcmService.php` | Path to Firebase service account JSON |
|
|
| `FCM_TOPIC_RAINFALL_WARNING` | `AlertController.php` | FCM topic for alerts |
|
|
| `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD` | `docker-compose.yml` | PostgreSQL container credentials |
|
|
| `PGADMIN_EMAIL`, `PGADMIN_PASSWORD` | `docker-compose.yml` | pgAdmin login credentials |
|
|
|
|
**Secrets location:**
|
|
- Environment variables via `.env` file (not committed per `.env.example` pattern)
|
|
- Docker Compose `environment` blocks reference `${VAR}` from `.env` file (no hardcoded secrets)
|
|
|
|
## Frontend CDN Dependencies
|
|
|
|
**Loaded via CDN (not bundled by Vite):**
|
|
- Bootstrap 5.3.3 (CSS + JS bundle) — `cdn.jsdelivr.net`
|
|
- Leaflet 1.9.4 (CSS + JS) — `unpkg.com`
|
|
- Chart.js 4.4.0 + chartjs-plugin-zoom 2.1.1 — `cdn.jsdelivr.net`
|
|
- jsPDF 2.5.1 + jspdf-autotable 3.5.28 — `cdnjs.cloudflare.com`
|
|
- jQuery 3.6.0 — `cdn.jsdelivr.net`
|
|
- Boxicons 2.1.4 — `unpkg.com`
|
|
- Flatpickr — `cdn.jsdelivr.net`
|
|
|
|
## Webhooks & Callbacks
|
|
|
|
**Incoming:**
|
|
- Not detected — no webhook endpoint routes found
|
|
|
|
**Outgoing:**
|
|
- Firebase FCM HTTP POST to `fcm.googleapis.com` for push notifications (`FcmService.php`)
|
|
- Slack webhook for log alerts (configured via `LOG_SLACK_WEBHOOK_URL`)
|
|
|
|
## Queue & Job Processing
|
|
|
|
**Queue Driver:**
|
|
- Default: `database` (using PostgreSQL `jobs` table)
|
|
- Alternate drivers configured: `sync`, `beanstalkd`, `sqs`, `redis`, `deferred`, `background`, `failover`
|
|
- Failover chain: `database` → `deferred`
|
|
- Job batching table: `job_batches`
|
|
- Failed jobs table: `failed_jobs` (driver: `database-uuids`)
|
|
|
|
**Queue Worker:**
|
|
- Dev command: `php artisan queue:listen --tries=1` (part of `composer dev` script, runs via `concurrently`)
|
|
|
|
## Session Management
|
|
|
|
**Session Driver:**
|
|
- Default: `database` (using PostgreSQL `sessions` table)
|
|
- Alternate drivers available: `file`, `cookie`, `memcached`, `redis`, `dynamodb`, `array`
|
|
- Lifetime: 120 minutes (configurable via `SESSION_LIFETIME`)
|
|
- Cookie: `{app_name}-session`, HTTP-only, SameSite=Lax
|
|
|
|
## Localization
|
|
|
|
**Supported Locales:**
|
|
- English (`en`): `src/lang/en/` — `messages.php`, `toast.php`, `auth.php`, `validation.php`, `pagination.php`, `passwords.php`
|
|
- Bahasa Malaysia (`bm`): `src/lang/bm/` — same file set as English
|
|
- Locale switching: `src/app/Http/Controllers/LocaleController.php` via `GET /locale/{locale}` route
|
|
- Stored in session via `Session::put('locale', $lang)`
|
|
|
|
## Management & Utility Containers
|
|
|
|
**Docker auxiliary services (in `docker-compose.yml`):**
|
|
- **pgAdmin4** (`dpage/pgadmin4`) - Port 5050, for PostgreSQL management
|
|
- **Adminer** - Port 6060, lightweight DB management
|
|
- **Dozzle** (`amir20/dozzle:latest`) - Port 777, real-time Docker log viewer
|
|
- **Filebrowser** (`gtstef/filebrowser:stable`) - Port 8900, web file manager with access to `/root/sides`
|
|
|
|
---
|
|
|
|
*Integration audit: 2026-05-28*
|