113 lines
5.7 KiB
Markdown
113 lines
5.7 KiB
Markdown
# JobsBoard Platform — Production Deployment & Operational Architecture Guide
|
|
|
|
## 1. System Topology Overview
|
|
|
|
```text
|
|
[ Internet / CDN (Cloudflare) ]
|
|
│
|
|
HTTPS / TLS Termination
|
|
│
|
|
▼
|
|
[ Next.js Application ]
|
|
(Docker Container / Node.js 20+)
|
|
│
|
|
┌───────────────────────────┼───────────────────────────┐
|
|
▼ ▼ ▼
|
|
[ PostgreSQL 16+ ] [ Upstash / Redis ] [ S3 / Cloudflare R2 ]
|
|
Primary Relational DB Distributed Rate Limiting Private Candidate Files
|
|
(Foreign Keys & BOLA) & Session Revocations (Pre-signed downloads)
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Infrastructure System States
|
|
|
|
| Subsystem | State | Implementation / Configuration Details |
|
|
| :--- | :--- | :--- |
|
|
| **Relational Database** | **ACTIVE AND VERIFIED** | **PostgreSQL 16** container actively running on `localhost:5432` (`jobsboard-postgres`). Schema synchronized with 76 production indexes, cascades, and constraints. All 1,425 jobs, 400 companies, 9 users, 1 resume, and active applications imported and verified. |
|
|
| **Multi-Tenant Ownership**| **ACTIVE AND VERIFIED** | Strict foreign-key relationship (`job.postedById === user.id` OR `job.companyId === user.companyId`). String matching eliminated; scraped jobs cannot be claimed or manipulated by employers. |
|
|
| **Session Invalidation** | **ACTIVE AND VERIFIED** | Dynamic DB rehydration in `getAuthUser()`. Suspended accounts (`lockedUntil > now`) are immediately denied access on next HTTP request regardless of unexpired JWT token. |
|
|
| **Distributed Rate Limiting** | **IMPLEMENTED BUT NOT CONFIGURED** | Unified `IRateLimiter` with `DistributedRedisRateLimiter` (atomic `INCR` + `EXPIRE` over Upstash REST). Automatically activates when `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` are set. Falls back to in-memory store with 10k key eviction cap. |
|
|
| **Object Storage** | **IMPLEMENTED BUT NOT CONFIGURED** | `IObjectStorage` abstraction in `lib/storage.ts`. Local adapter stores files with UUID keys and strict path traversal sanitization (`../../../etc/passwd` -> `etcpasswd`). Production S3/R2 adapter activates when `STORAGE_ACCESS_KEY` & `STORAGE_SECRET_KEY` are provided. |
|
|
| **Background Processing** | **DEVELOPMENT ONLY** | In-memory asynchronous queue (`lib/queue.ts`) with exponential backoff retries and dead-letter log handling. Suitable for non-blocking local email and notification dispatches. Requires BullMQ/SQS for persistent distributed workers. |
|
|
|
|
---
|
|
|
|
## 3. Environment Variables Specification
|
|
|
|
Create `.env.production` in the deployment environment:
|
|
|
|
```bash
|
|
# Database Connection (PostgreSQL 16+)
|
|
DATABASE_URL="postgresql://username:password@db-host.internal:5432/jobsboard?schema=public&sslmode=require"
|
|
|
|
# NextAuth Authentication
|
|
NEXTAUTH_URL="https://jobsboard.yourdomain.com"
|
|
NEXTAUTH_SECRET="generate-strong-64-character-crypto-hex-secret"
|
|
|
|
# Node Environment
|
|
NODE_ENV="production"
|
|
PORT=3000
|
|
|
|
# Distributed Rate Limiting (Upstash / Redis REST API)
|
|
UPSTASH_REDIS_REST_URL="https://your-redis-instance.upstash.io"
|
|
UPSTASH_REDIS_REST_TOKEN="your-upstash-rest-token"
|
|
|
|
# Object Storage (AWS S3, Cloudflare R2, MinIO)
|
|
STORAGE_ENDPOINT="https://<account-id>.r2.cloudflarestorage.com"
|
|
STORAGE_BUCKET="jobsboard-private-assets"
|
|
STORAGE_ACCESS_KEY="your-storage-access-key"
|
|
STORAGE_SECRET_KEY="your-storage-secret-key"
|
|
|
|
# Automated Cron & Job Alert Security Secret
|
|
CRON_SECRET="generate-strong-random-bearer-token-for-alerts"
|
|
|
|
# Outbound Email Service (Resend / AWS SES)
|
|
RESEND_API_KEY="re_your_api_key_here"
|
|
EMAIL_FROM="JobsBoard Talent Alerts <notifications@jobsboard.yourdomain.com>"
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Production Database Migration & Restoration
|
|
|
|
### Step 1: Initialize Database Schema
|
|
```bash
|
|
npx prisma db push --schema=prisma/schema.postgresql.prisma
|
|
```
|
|
|
|
### Step 2: Restore Core Dataset (If Migrating Existing SQLite dev.db)
|
|
```bash
|
|
DATABASE_URL="postgresql://user:password@host:5432/jobsboard?schema=public" node scripts/import-postgres-data.js
|
|
```
|
|
|
|
### Step 3: Run E2E Database & Multi-Tenant Tests
|
|
```bash
|
|
npm run test:postgres
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Failure Modes & Resilience Policy
|
|
|
|
| Failure Condition | System Impact | Degraded Behavior & Safeguards |
|
|
| :--- | :--- | :--- |
|
|
| **PostgreSQL Outage** | Critical | `/api/health` returns `503 Service Unavailable`. Database queries fail fast; sensitive operations reject rather than expose unauthenticated resources. |
|
|
| **Redis / Rate Limiter Down** | Medium | `DistributedRedisRateLimiter` automatically degrades to high-throughput local memory limiter (`MemoryRateLimiter`). Sensitive endpoints remain bounded by local 10k LRU memory table. |
|
|
| **Object Storage Unreachable** | Medium | Resume download fallback serves generated PDF stream on-the-fly via PDFKit. `/api/health` reports degraded storage status. |
|
|
| **Queue Worker Crash** | Low | In-memory queue logs dead-letter events for retry upon container restart. |
|
|
|
|
---
|
|
|
|
## 6. Disaster Recovery & Backup Plan
|
|
|
|
1. **Daily Automated Snapshots**:
|
|
```bash
|
|
# Daily PostgreSQL Logical Backup
|
|
pg_dump -U postgres -h localhost -d jobsboard --format=custom --file="/backups/jobsboard_$(date +%Y%m%d_%H%M%S).dump"
|
|
```
|
|
2. **Restoration Procedure**:
|
|
```bash
|
|
pg_restore -U postgres -h localhost -d jobsboard --clean --if-exists "/backups/jobsboard_target.dump"
|
|
```
|
|
3. **Point-in-Time Recovery (PITR)**: Enable Write-Ahead Log (WAL) archiving in production cloud database providers (AWS RDS, Supabase, Neon, GCP Cloud SQL).
|