Admin API Migration Plan
> Status: RECOMMENDATION > Created: 2025-12-10 > Priority: High (Security)
Admin API Migration Plan
Status: RECOMMENDATION Created: 2025-12-10 Priority: High (Security)
Executive Summary
Currently, the admin panel (crop-front-admin on Vercel) connects directly to MongoDB Atlas, requiring 0.0.0.0/0 IP whitelist which is a security anti-pattern. This document outlines the plan to migrate all MongoDB access through the existing catalog-service microservice.
Current Architecture (Problem)
┌─────────────────────────────────────────────────────────────────┐
│ VERCEL (crop-front-admin) │
│ │
│ Next.js API Routes ──► Direct MongoDB Connection │
│ (requires 0.0.0.0/0 whitelist) │
└─────────────────────────────────────────────────────────────────┘
│
▼ ❌ INSECURE
┌─────────────────────────────────────────────────────────────────┐
│ MongoDB Atlas │
│ │
│ IP Whitelist: 0.0.0.0/0 (allows ALL IPs) │
│ Only protected by username/password │
└─────────────────────────────────────────────────────────────────┘Security Issues
| Issue | Risk Level | Description |
|---|---|---|
| Open IP Whitelist | CRITICAL | 0.0.0.0/0 allows any IP to attempt connection |
| No Network Isolation | HIGH | Single layer of defense (credentials only) |
| Brute Force Exposure | HIGH | Attackers can attempt password guessing |
| Compliance Violation | MEDIUM | Fails SOC2, PCI-DSS requirements |
| Audit Difficulty | MEDIUM | Cannot trace attack sources by IP |
Target Architecture (Solution)
┌─────────────────────────────────────────────────────────────────┐
│ VERCEL (crop-front-admin) │
│ │
│ Next.js App ──► fetch("https://api.crop-dev.app/catalog/...") │
│ (No direct MongoDB access) │
└─────────────────────────────────────────────────────────────────┘
│
▼ HTTPS + JWT
┌─────────────────────────────────────────────────────────────────┐
│ CLOUDFLARE (api.crop-dev.app) │
│ │
│ Worker: Route /catalog/* → GCP API Gateway │
│ Features: Edge caching, DDoS protection, WAF │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ GCP API GATEWAY │
│ │
│ - Clerk JWT validation │
│ - Rate limiting │
│ - Request logging │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ CLOUD RUN: catalog-service (EXISTS) │
│ │
│ VPC Connector: crop-connector │
│ Egress: ALL_TRAFFIC via VPC │
└─────────────────────────────────────────────────────────────────┘
│
▼ Private Link (PSC)
┌─────────────────────────────────────────────────────────────────┐
│ MongoDB Atlas (Private Endpoint) │
│ │
│ pl-00-xxx.dkwuhg.mongodb.net │
│ IP Whitelist: Only VPC IPs (NO 0.0.0.0/0) │
└─────────────────────────────────────────────────────────────────┘Security Benefits
| Benefit | Description |
|---|---|
| Network Isolation | MongoDB only accessible via Private Link |
| Defense in Depth | Multiple security layers (Cloudflare → Gateway → VPC) |
| JWT Validation | Clerk tokens validated at API Gateway |
| IP Restriction | Remove 0.0.0.0/0, whitelist only VPC IPs |
| Audit Trail | All requests logged at Gateway level |
| DDoS Protection | Cloudflare edge protection |
Existing Infrastructure
Already Implemented
| Component | Status | Location |
|---|---|---|
| catalog-service | ✅ EXISTS | /CROP-parts-services/services/catalog/ |
| Vendor endpoints | ✅ EXISTS | /catalog/vendors/* |
| Parts endpoints | ✅ EXISTS | /catalog/parts/* |
| Quality endpoints | ✅ EXISTS | /catalog/quality/* |
| VPC + Private Link | ✅ EXISTS | crop-vpc → MongoDB PSC |
| Cloudflare Worker | ✅ EXISTS | api.crop-dev.app |
| API Gateway | ✅ EXISTS | crop-gateway |
catalog-service Endpoints (Already Available)
# Vendors
GET /catalog/vendors # List vendors
POST /catalog/vendors # Create vendor
GET /catalog/vendors/:code # Get vendor
PATCH /catalog/vendors/:code # Update vendor
DELETE /catalog/vendors/:code # Archive vendor
POST /catalog/vendors/:code/status # Change status
POST /catalog/vendors/refresh-readiness # Bulk refresh
# Parts
GET /catalog/parts/:vendorCode # List parts by vendor
POST /catalog/parts/:vendorCode # Create part
GET /catalog/parts/:vendorCode/:partNumber # Get part
PATCH /catalog/parts/:vendorCode/:partNumber # Update part
DELETE /catalog/parts/:vendorCode/:partNumber # Delete part
# Quality
GET /catalog/quality/summary # All vendors quality
GET /catalog/quality/:vendorCode # Single vendor quality
POST /catalog/quality/revalidate # Cache revalidation
# Collections
GET /catalog/collections # List collections
GET /catalog/collections/:name # Collection schema
# Sync
POST /catalog/sync # Full ES sync
POST /catalog/sync/:vendorCode # Vendor ES syncMigration Scope
Routes to Migrate (from crop-front-admin)
| Current Route | Target | Complexity |
|---|---|---|
/api/vendors | /catalog/vendors | Low (direct map) |
/api/vendors/[code] | /catalog/vendors/:code | Low |
/api/vendors/[code]/status | /catalog/vendors/:code/status | Low |
/api/catalog/quality/vendors | /catalog/quality/summary | Low |
/api/catalog/quality/vendors/[code] | /catalog/quality/:code | Low |
/api/catalog/health/vendors | /catalog/health/vendors | Medium |
/api/parts | /catalog/parts | Medium |
/api/parts/[slug] | /catalog/parts/:vendor/:partNumber | Medium |
/api/dashboard/stats | NEW endpoint needed | Medium |
/api/orders | Separate service | Low |
/api/health/collections | /catalog/collections | Low |
Code to Remove (after migration)
crop-front-admin/
├── lib/
│ ├── mongodb.ts # DELETE - No longer needed
│ ├── db/
│ │ ├── crud.ts # DELETE - Move to catalog-service
│ │ └── collections.ts # DELETE - Move to catalog-service
│ └── services/
│ └── vendor-readiness.ts # DELETE - Already in catalog-service
└── app/api/
├── vendors/ # REFACTOR - Proxy to catalog-service
├── parts/ # REFACTOR - Proxy to catalog-service
├── catalog/ # REFACTOR - Proxy to catalog-service
└── health/ # REFACTOR - Proxy to catalog-serviceEnvironment Variables to Remove (Vercel)
# REMOVE from Vercel after migration
MONGODB_URI # No longer needed
MONGODB_DB_NAME # No longer neededImplementation Phases
Phase 1: Infrastructure Setup (0.5 days)
Tasks:
- Add
/catalog/*routes to API Gateway OpenAPI spec - Update Cloudflare Worker to route
/catalog/*to Gateway - Verify catalog-service is deployed with VPC connector
- Test connectivity: Vercel → Cloudflare → Gateway → catalog-service → MongoDB
Files to modify:
/CROP-parts-services/gateway.yaml- Add catalog routes/CROP-parts-services/cloudflare/api-proxy/src/index.ts- Add routing
Phase 2: Authentication Integration (1 day)
Tasks:
- Add Clerk JWT validation to catalog-service
- Extract user info for audit trail (
activatedBy,deactivatedBy) - Add role-based access control (admin only)
Files to modify:
/CROP-parts-services/services/catalog/src/middleware/clerk-auth.ts- NEW/CROP-parts-services/services/catalog/src/routes/vendors.ts- Add user tracking
Clerk JWT Middleware Example:
import { verifyToken } from '@clerk/backend';
export async function clerkAuth(c: Context, next: Next) {
const authHeader = c.req.header('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
return c.json({ error: 'Unauthorized' }, 401);
}
try {
const token = authHeader.slice(7);
const payload = await verifyToken(token, {
secretKey: process.env.CLERK_SECRET_KEY,
});
c.set('userId', payload.sub);
c.set('userEmail', payload.email);
await next();
} catch {
return c.json({ error: 'Invalid token' }, 401);
}
}Phase 3: Frontend API Client (1 day)
Tasks:
- Create
lib/api/catalog-client.tsin admin panel - Add request interceptor for Clerk token injection
- Add error handling and retry logic
New file: lib/api/catalog-client.ts
import { auth } from '@clerk/nextjs/server';
const CATALOG_API_URL = process.env.NEXT_PUBLIC_API_GATEWAY_URL || 'https://api.crop-dev.app';
export async function catalogFetch<T>(
endpoint: string,
options: RequestInit = {}
): Promise<T> {
const { getToken } = await auth();
const token = await getToken();
const response = await fetch(`${CATALOG_API_URL}/catalog${endpoint}`, {
...options,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
...options.headers,
},
});
if (!response.ok) {
throw new Error(`Catalog API error: ${response.status}`);
}
return response.json();
}
// Typed API methods
export const catalogApi = {
vendors: {
list: (catalog?: string) =>
catalogFetch<VendorListResponse>(`/vendors?catalog=${catalog || 'crop_stage'}`),
get: (code: string) =>
catalogFetch<Vendor>(`/vendors/${code}`),
create: (data: CreateVendorInput) =>
catalogFetch<Vendor>('/vendors', { method: 'POST', body: JSON.stringify(data) }),
update: (code: string, data: UpdateVendorInput) =>
catalogFetch<Vendor>(`/vendors/${code}`, { method: 'PATCH', body: JSON.stringify(data) }),
changeStatus: (code: string, status: VendorStatus) =>
catalogFetch<Vendor>(`/vendors/${code}/status`, { method: 'POST', body: JSON.stringify({ status }) }),
},
quality: {
summary: (catalog?: string) =>
catalogFetch<QualitySummary>(`/quality/summary?catalog=${catalog || 'crop_stage'}`),
vendor: (code: string) =>
catalogFetch<VendorQuality>(`/quality/${code}`),
},
// ... more methods
};Phase 4: Route Migration (2 days)
Tasks:
- Refactor each API route to use
catalogApiclient - Keep route structure same (for frontend compatibility)
- Add feature flag for gradual rollout
Example refactor: /app/api/vendors/route.ts
// BEFORE (direct MongoDB)
import { getClient } from '@/lib/mongodb';
export async function GET(req: NextRequest) {
const client = await getClient();
const db = client.db('crop_stage');
const vendors = await db.collection('vendors').find({}).toArray();
return NextResponse.json({ vendors });
}
// AFTER (via catalog-service)
import { catalogApi } from '@/lib/api/catalog-client';
export async function GET(req: NextRequest) {
const catalog = new URL(req.url).searchParams.get('catalog');
const response = await catalogApi.vendors.list(catalog);
return NextResponse.json(response);
}Phase 5: Testing & Validation (1 day)
Tasks:
- E2E tests for all migrated routes
- Performance comparison (latency before/after)
- Error handling verification
- Rollback procedure documentation
Test checklist:
- Vendor CRUD operations
- Vendor status transitions
- Quality metrics loading
- Parts listing and search
- Dashboard stats
- Error responses (401, 403, 404, 500)
- Rate limiting behavior
Phase 6: Cleanup & Security Hardening (0.5 days)
Tasks:
- Remove
MONGODB_URIfrom Vercel environment - Delete
lib/mongodb.tsand related files - Remove
0.0.0.0/0from MongoDB Atlas whitelist - Update documentation
MongoDB Atlas Network Access (final state):
# REMOVE
0.0.0.0/0 (Allow from anywhere)
# KEEP
10.0.0.0/24 (crop-vpc)
34.74.212.119/32 (payment-vpc NAT)Timeline
| Phase | Duration | Dependencies |
|---|---|---|
| Phase 1: Infrastructure | 0.5 days | None |
| Phase 2: Authentication | 1 day | Phase 1 |
| Phase 3: API Client | 1 day | Phase 1 |
| Phase 4: Route Migration | 2 days | Phase 2, 3 |
| Phase 5: Testing | 1 day | Phase 4 |
| Phase 6: Cleanup | 0.5 days | Phase 5 |
| Total | 6 days |
Rollback Plan
If issues occur during migration:
-
Feature Flag Rollback
const USE_CATALOG_API = process.env.USE_CATALOG_API === 'true'; if (USE_CATALOG_API) { return catalogApi.vendors.list(); } else { return directMongoQuery(); // fallback } -
Environment Rollback
- Re-add
MONGODB_URIto Vercel - Re-add
0.0.0.0/0to Atlas (temporary) - Deploy previous version
- Re-add
-
Monitoring
- Set up alerts for 5xx errors on catalog routes
- Monitor latency increase > 500ms
- Track error rates during rollout
Success Criteria
| Metric | Target |
|---|---|
MongoDB 0.0.0.0/0 removed | ✅ |
| All admin routes via catalog-service | ✅ |
| Latency increase | < 100ms |
| Error rate | < 0.1% |
| Clerk JWT validation working | ✅ |
| Audit trail for status changes | ✅ |
Appendix: Current Duplication
The following code is duplicated between admin panel and catalog-service:
| File (admin) | File (catalog) | Action |
|---|---|---|
lib/services/vendor-readiness.ts | src/services/vendor-readiness.ts | Keep catalog, delete admin |
lib/types/vendor.ts | src/types/vendor.ts | Keep catalog, delete admin |
lib/db/collections.ts | src/services/mongodb.ts | Keep catalog, delete admin |
After migration, admin panel will have zero MongoDB-related code.