282 lines
10 KiB
Markdown
282 lines
10 KiB
Markdown
# External Integrations
|
|
|
|
**Analysis Date:** 2026-02-13
|
|
|
|
## APIs & External Services
|
|
|
|
**Next.js Frontend Site Revalidation:**
|
|
- Service: Custom webhook to Next.js `/api/revalidate`
|
|
- What it's used for: Trigger static site regeneration when articles, pages, or documents change
|
|
- SDK/Client: Guzzle HTTP client (`guzzlehttp/guzzle`)
|
|
- Implementation: `app/Services/SiteRevalidationService.php`
|
|
- Methods:
|
|
- `SiteRevalidationService::revalidateArticle(?slug)` - Trigger article cache invalidation
|
|
- `SiteRevalidatService::revalidatePage(?slug)` - Trigger page cache invalidation
|
|
- `SiteRevalidationService::revalidateDocument(?slug)` - Trigger document cache invalidation
|
|
- Triggered by: `app/Observers/DocumentObserver.php` on article/page create/update/delete
|
|
- Timeout: 5 seconds
|
|
- Error handling: Logged as warning, does not fail request
|
|
|
|
**Email Service Provider (Configurable):**
|
|
- Services supported: SMTP, Mailgun, Postmark, AWS SES
|
|
- SDK/Client: Laravel Mail facades
|
|
- Implementation: Controllers use `Mail::to($email)->queue(MailClass::class)`
|
|
- Auth: `MAIL_HOST`, `MAIL_PORT`, `MAIL_USERNAME`, `MAIL_PASSWORD`, or API keys
|
|
- Dev/Test: Mailpit on `localhost:1025` (configured in `.env.example`)
|
|
- Queue integration: All mail uses async queue for better performance
|
|
|
|
## Data Storage
|
|
|
|
**Primary Database:**
|
|
- Type/Provider: MySQL (production) or SQLite (development)
|
|
- Connection config: `config/database.php`
|
|
- Connection env vars:
|
|
- `DB_CONNECTION` - Type: `mysql` or `sqlite`
|
|
- `DB_HOST` - Database server (e.g., `127.0.0.1`)
|
|
- `DB_PORT` - Port (default 3306 for MySQL)
|
|
- `DB_DATABASE` - Database name
|
|
- `DB_USERNAME` - Username
|
|
- `DB_PASSWORD` - Password
|
|
- ORM/Client: Laravel Eloquent (built-in)
|
|
- Encryption: AES-256-CBC via `config/app.php` cipher
|
|
- Notable tables:
|
|
- `users` - System users (staff, admin)
|
|
- `members` - Member profiles (encrypted national IDs)
|
|
- `finance_documents` - Finance approvals with multi-tier workflows
|
|
- `payments` - Member fee payments
|
|
- `articles`, `pages`, `categories`, `tags` - CMS content
|
|
- `documents` - Document library entries
|
|
- `roles`, `permissions`, `model_has_roles` - Spatie permission tables
|
|
- `settings` - System-wide configuration (cached)
|
|
|
|
**File Storage:**
|
|
- Databases: Local disk and S3 configurable
|
|
- Private files: `storage/app/` (served via controller)
|
|
- Public files: `storage/app/public/` (served via `/storage/...` URL)
|
|
- S3 Config: `config/filesystems.php` → `disks.s3`
|
|
- S3 env vars:
|
|
- `AWS_ACCESS_KEY_ID`
|
|
- `AWS_SECRET_ACCESS_KEY`
|
|
- `AWS_DEFAULT_REGION`
|
|
- `AWS_BUCKET`
|
|
- `AWS_URL` (optional CDN)
|
|
- `AWS_ENDPOINT` (optional for S3-compatible services)
|
|
|
|
**Caching:**
|
|
- Providers: File (default), Redis, Memcached, DynamoDB
|
|
- Config: `config/cache.php`
|
|
- Default driver: `file` (suitable for single-server, can switch to Redis)
|
|
- Used for: `settings()` helper caching, query result caching
|
|
- Cache prefix: `laravel_cache_` (configurable via `CACHE_PREFIX` env)
|
|
- Settings cache: Automatically cleared on `SystemSetting` model mutation
|
|
|
|
**Session Storage:**
|
|
- Providers: File (default), Database, Redis
|
|
- Config: `config/session.php`
|
|
- Default driver: `file`
|
|
- Lifetime: 120 minutes (configurable via `SESSION_LIFETIME` env)
|
|
- CSRF tokens: Protected via Laravel middleware
|
|
|
|
## Authentication & Identity
|
|
|
|
**Auth Provider:**
|
|
- Type: Custom (Session-based)
|
|
- Implementation: Built-in Laravel authentication with `User` model
|
|
- Guard: `web` (session-based)
|
|
- Config: `config/auth.php`
|
|
- Provider: Eloquent user provider (`App\Models\User`)
|
|
|
|
**API Token Authentication:**
|
|
- Service: Laravel Sanctum
|
|
- Implementation: `config/sanctum.php`
|
|
- For: Public API endpoints (`/api/v1/*`)
|
|
- Stateful domains: `localhost`, `127.0.0.1`, custom domain (via `SANCTUM_STATEFUL_DOMAINS` env)
|
|
- Token prefix: Customizable via `SANCTUM_TOKEN_PREFIX` env
|
|
- Expiration: No default expiration (tokens live indefinitely unless custom set)
|
|
|
|
**Role-Based Access Control (RBAC):**
|
|
- Service: Spatie Laravel Permission
|
|
- Package: `spatie/laravel-permission@^6.23`
|
|
- Config: `config/permission.php`
|
|
- Models: `Spatie\Permission\Models\Role`, `Spatie\Permission\Models\Permission`
|
|
- Tables:
|
|
- `roles` - Available roles
|
|
- `permissions` - Available permissions
|
|
- `model_has_roles` - User-to-role assignments
|
|
- `model_has_permissions` - User-to-permission direct assignments
|
|
- `role_has_permissions` - Role-to-permission assignments
|
|
- Core roles (seeded in `database/seeders/`):
|
|
- `admin` - Full system access
|
|
- `finance_requester` - Submit finance documents
|
|
- `finance_cashier` - Tier 1 approvals (small amounts)
|
|
- `finance_accountant` - Tier 2 approvals (medium amounts) and ledger recording
|
|
- `finance_chair` - Tier 2 approvals (medium amounts)
|
|
- `finance_board_member` - Tier 3 approvals (large amounts)
|
|
- `secretary_general` - CMS management, member management
|
|
- `membership_manager` - Member lifecycle management
|
|
|
|
**Identity/National ID Encryption:**
|
|
- Encryption: AES-256 via Laravel's encryption
|
|
- Fields in `members` table:
|
|
- `national_id_encrypted` - Stores encrypted national ID
|
|
- `national_id_hash` - SHA256 hash for searching (indexed)
|
|
- `national_id` - Virtual accessor (decrypts on read)
|
|
- Location: `app/Models/Member.php`
|
|
|
|
## Webhooks & Callbacks
|
|
|
|
**Incoming Webhooks:**
|
|
- Site revalidation endpoint: `/api/revalidate` (public, requires valid token)
|
|
- Token: `NEXTJS_REVALIDATE_TOKEN` env var
|
|
- Method: `POST`
|
|
- Payload: `{ type: 'article'|'page'|'document', slug?: string }`
|
|
|
|
**Outgoing Webhooks:**
|
|
- Next.js site revalidation: `POST {NEXTJS_REVALIDATE_URL}`
|
|
- URL: `NEXTJS_REVALIDATE_TOKEN` env var
|
|
- Token header: `x-revalidate-token`
|
|
- Payload: `{ type: 'article'|'page'|'document', slug?: string }`
|
|
- Triggered on: Article/Page/Document create/update/delete
|
|
- Implementation: `app/Services/SiteRevalidationService.php`
|
|
- Timeout: 5 seconds, failures logged but do not block request
|
|
|
|
## Monitoring & Observability
|
|
|
|
**Error Tracking:**
|
|
- Service: Spatie Ignition (error page companion)
|
|
- Package: `spatie/laravel-ignition@^2.0`
|
|
- Used in: Development and error page debugging
|
|
- Config: `config/app.php` → providers
|
|
|
|
**Logging:**
|
|
- Framework: Monolog (via Laravel)
|
|
- Config: `config/logging.php`
|
|
- Default channel: `stack` (multi-handler)
|
|
- Channels available:
|
|
- `single` - Single log file: `storage/logs/laravel.log`
|
|
- `daily` - Rotate daily, keep 14 days
|
|
- `slack` - Send logs to Slack webhook (env: `LOG_SLACK_WEBHOOK_URL`)
|
|
- `papertrail` - Syslog UDP to Papertrail (env: `PAPERTRAIL_URL`, `PAPERTRAIL_PORT`)
|
|
- `stderr` - Output to stderr
|
|
- `syslog` - System syslog
|
|
- `errorlog` - PHP error_log
|
|
- `log` - File channel with custom naming
|
|
- `array` - In-memory (testing only)
|
|
- Log level: `debug` (default, configurable via `LOG_LEVEL` env)
|
|
- Deprecations channel: Null by default (can redirect via `LOG_DEPRECATIONS_CHANNEL` env)
|
|
|
|
**Audit Logging:**
|
|
- Service: Custom audit logger
|
|
- Implementation: `app/Support/AuditLogger.php`
|
|
- Usage: `AuditLogger::log($action, $auditable, $metadata)`
|
|
- Tracked: Model mutations (create/update/delete) for compliance
|
|
- Storage: Appended to audit log entries
|
|
|
|
## CI/CD & Deployment
|
|
|
|
**Hosting:**
|
|
- Platform: Dedicated servers, Docker (Laravel Sail), or cloud (AWS, etc.)
|
|
- Port: API typically runs on port 8001 (not 8000 - that's often occupied)
|
|
|
|
**Build Process:**
|
|
- Frontend assets: `npm run build` (Vite compilation)
|
|
- Backend: `composer install` (or `composer install --no-dev` for production)
|
|
- Migrations: `php artisan migrate --force`
|
|
- Cache: `php artisan config:cache`, `php artisan view:cache`
|
|
|
|
**Code Quality (Pre-commit):**
|
|
- Linter: Laravel Pint (PSR-12)
|
|
- Run: `./vendor/bin/pint`
|
|
- Fixes style issues automatically
|
|
- Static analysis: PHPStan
|
|
- Run: `./vendor/bin/phpstan analyse`
|
|
- Config: `phpstan.neon` (if present)
|
|
|
|
**Testing:**
|
|
- Unit & Feature Tests: PHPUnit
|
|
- Run: `php artisan test`
|
|
- Run specific: `php artisan test --filter=ClassName`
|
|
- Config: `phpunit.xml`
|
|
- Browser/E2E Tests: Laravel Dusk
|
|
- Run: `php artisan dusk`
|
|
- Uses Chrome/Chromium driver
|
|
|
|
## Environment Configuration
|
|
|
|
**Required Environment Variables:**
|
|
|
|
Core:
|
|
- `APP_NAME` - Application name
|
|
- `APP_ENV` - Environment: `local`, `production`
|
|
- `APP_DEBUG` - Boolean: enable/disable debug mode
|
|
- `APP_KEY` - Base64 encryption key (auto-generated)
|
|
- `APP_URL` - Full URL: `https://member.usher.org.tw` (production)
|
|
|
|
Database:
|
|
- `DB_CONNECTION` - `mysql` (default) or `sqlite`
|
|
- `DB_HOST` - Database server
|
|
- `DB_PORT` - Port (3306 for MySQL)
|
|
- `DB_DATABASE` - Database name
|
|
- `DB_USERNAME` - Username
|
|
- `DB_PASSWORD` - Password
|
|
|
|
Mail:
|
|
- `MAIL_MAILER` - `smtp` (default), `mailgun`, `postmark`, `ses`, `log`
|
|
- `MAIL_HOST` - SMTP host
|
|
- `MAIL_PORT` - SMTP port (587 typical)
|
|
- `MAIL_USERNAME` - SMTP username
|
|
- `MAIL_PASSWORD` - SMTP password
|
|
- `MAIL_ENCRYPTION` - `tls`, `ssl`, or null
|
|
- `MAIL_FROM_ADDRESS` - From email address
|
|
- `MAIL_FROM_NAME` - From name
|
|
|
|
Caching & Queuing:
|
|
- `CACHE_DRIVER` - `file` (default), `redis`, `memcached`, `dynamodb`
|
|
- `QUEUE_CONNECTION` - `sync` (default), `database`, `redis`, `sqs`
|
|
- `SESSION_DRIVER` - `file` (default), `database`, `redis`
|
|
|
|
Feature Toggles:
|
|
- `REGISTRATION_ENABLED` - Boolean: allow public registration (`false` by default)
|
|
|
|
Frontend Integration:
|
|
- `NEXTJS_REVALIDATE_URL` - Full URL to Next.js revalidation endpoint
|
|
- `NEXTJS_REVALIDATE_TOKEN` - Bearer token for revalidation requests
|
|
- `NEXTJS_PUBLIC_PATH` - Optional: path to Next.js `public/` directory (for local asset sync)
|
|
|
|
**Secrets Location:**
|
|
- Method: Environment variables (`.env` file, never committed)
|
|
- Production: Use hosted secrets manager or CI/CD environment variables
|
|
- Never commit: Passwords, API keys, encryption keys, tokens
|
|
|
|
## Optional: Redis
|
|
|
|
**If using Redis for caching/sessions/queue:**
|
|
- Config: `config/database.php` → `redis`
|
|
- Env vars:
|
|
- `REDIS_HOST` - `127.0.0.1` (default)
|
|
- `REDIS_PORT` - `6379` (default)
|
|
- `REDIS_PASSWORD` - Password (null by default)
|
|
- `REDIS_CLIENT` - `phpredis` (default)
|
|
- `REDIS_CLUSTER` - `redis` (default)
|
|
- Databases:
|
|
- Default: 0 (primary connection)
|
|
- Cache: 1 (separate database for cache keys)
|
|
|
|
## Optional: AWS Integration
|
|
|
|
**If using AWS S3 for file storage:**
|
|
- Config: `config/filesystems.php` → `disks.s3`
|
|
- Env vars: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`, `AWS_BUCKET`
|
|
|
|
**If using AWS SES for email:**
|
|
- Config: `config/mail.php` → `mailers.ses`
|
|
- Env vars: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`
|
|
|
|
**If using AWS SQS for queue:**
|
|
- Config: `config/queue.php` → `connections.sqs`
|
|
- Env vars: AWS credentials + `SQS_PREFIX`, `SQS_QUEUE`, `SQS_SUFFIX`
|
|
|
|
---
|
|
|
|
*Integration audit: 2026-02-13*
|