Building Deterministic Vendor Pages & Domain Routing for Multi-Site Deployments
What Was Done
Took three vendor-listing pages across the JADA network from manual, fragile state to deterministic, tested infrastructure. Built automated tests for URL and phone validity, refactored the vendor data model into a single source of truth, rebuilt all three vendor pages from a clean JSON source, registered a new domain via API, and deployed the corrected pages to live CloudFront distributions with proper i18n support and routing layers.
The Problem: Vendor Links Rotting
Three sites—sailjada.com, queenofsandiego.com, and burialsatseasandiego.com—each maintained their own vendor partner pages in hand-coded HTML. Links died, phone numbers went stale, and there was no way to know without manual spot-checks. The pages were never tested, and any rebuild risked introducing new typos.
Technical Architecture
1. Deterministic Test Layer
Created /Users/cb/icloud-repos/sites/queenofsandiego.com/tests/test_vendor_pages.py with:
- HTTP HEAD requests with retries: Verify each vendor URL returns 200 or 301 (not 404, 500, timeout). Retry logic handles transient network hiccups; timeout set to 10 seconds per request to catch slow or unreachable servers.
- Phone number validation: Check E.164 format and, where feasible, reverse-lookup against known provider databases to catch typos early.
- Structure validation: Confirm each vendor object in the source JSON has required fields:
name,url,phone,category,description.
The test runs on every rebuild. If a vendor URL is dead or malformed, the build fails loudly before anything ships to S3.
2. Single Source of Truth: vendors.json
Moved all vendor data to /Users/cb/icloud-jada-ops/vendors/vendors.json:
{
"vendors": [
{
"name": "Ferro & Moreno Immigration Law",
"url": "https://morenoferrolaw.com",
"phone": "+14422580072",
"category": "Legal",
"description": "Immigration and human rights law..."
},
...
]
}
This is the canonical source. Build scripts read it once, render three separate HTML pages (one per domain), and tests validate it before deployment.
3. Multi-Site Rendering & Deployment
The build process (build.py in each site repo):
- Reads
vendors.jsonfrom jada-ops - Renders each vendor object into an HTML fragment
- Injects the fragment into the site's vendor page template
- Writes the final HTML to the local
dist/folder - Calls
jada-deployto push to S3 and invalidate CloudFront
Three separate S3 uploads, three separate cache invalidations—but one source of truth for the content.
4. Domain Registration & Routing
Registered morenoferrolaw.com via the Namecheap API (called from Lightsail, which has the whitelisted IP). The domain is now authoritative and ready for ACM certificate provisioning.
Routing logic added to the dc-sites CloudFront function (the router Lambda for dangerouscentaur-affiliated domains) to detect the Host header and serve the Daniela Ferro page from either the dangerouscentaur.com subdomain or the new primary domain, sharing the same S3 object path.
Infrastructure Details
S3 Buckets & Paths
- sailjada.com: S3 bucket
sailjada-web, path/vendors/for the vendor page - queenofsandiego.com: S3 bucket
queenofsandiego-web, path/vendors/ - burialsatseasandiego.com: S3 bucket
bssd-web, path/vendors/ - dangerouscentaur (Daniela Ferro site): S3 bucket
dc-sites, path/state/danielaferro/with subdirectories for assets, i18n, and styles
CloudFront Distributions
E10WJ716X823DQ: dragon-bodyguards.com (unrelated, no-cache configured)EPF415U2AO8B3: Web tier (sailjada + queenofsandiego + bssd), default TTL 3600sEJJKHNHYNK4Q5: Staging tier for testing before prod
The /vendors/ paths cache aggressively (3600s TTL), but jada-deploy automatically invalidates the path pattern /vendors/* after every upload—so updates are live within seconds.
Namecheap API Integration
All domain operations (registration, DNS updates, cert validation) go through the Namecheap API, called from the Lightsail box (34.239.233.28, whitelisted). Mac-local requests fail due to dynamic IP.
Example command flow (no secrets):
#!/bin/bash
# On Lightsail, with API key in environment
curl -s "https://api.namecheap.com/api/xml.response?ApiUser=..." \
> check_domain_result.xml
# Parse response, register if available, get auth code if needed
Key Decisions & Tradeoffs
Why Deterministic Tests First?
Vendor pages are low-churn but high-visibility. A dead link in a proposal is worse than no mention at all. By making tests run on every commit and build, we catch regressions immediately. The cost: 10–15 seconds per build for HEAD requests. The benefit: zero silent failures.
Why One vendors.json for Three Sites?
Copy-paste breaks. The moment the same vendor appears on two sites, they diverge. A single JSON source with site-specific rendering templates keeps the content authoritative and makes updates atomic. If a vendor changes their phone, one edit propagates everywhere.
Why Separate Domain for Daniela?
The dangerouscentaur.com subdomain signals creative/experimental—wrong for a legal professional. A dedicated domain (morenoferrolaw.com) establishes authority and SEO credibility independently. The site still lives in the same S3 bucket and CloudFront, but DNS routing treats it as a distinct entity.
Deployment Workflow
- Update vendors.json in
/Users/cb/icloud-jada-ops/vendors/vendors.json - Run tests:
cd /Users/cb/icloud-repos/sites/queenofsandiego.com && python -m pytest tests/test_vendor_pages.py -v - Rebuild all three sites:
build.pyin each site reads vendors.json and renders HTML - Deploy:
~/bin/jada-deployfrom each site directory pushes to S3, auto-invalidates CloudFront, updates bothdangerouscentaur.comDNS and the newmorenoferrolaw.comalias - Verify: Curl the live domains and confirm vendor links resolve
What's Next
- SEO Supercharge: Daniela Ferro's site needs structured data (schema.org for Legal Professional), backlinks from partner sites, and Google Business Profile verification.
- Certificate Provisioning: ACM certificate for
morenoferrolaw.commust be requested and validated via DNS CNAME before the domain is live in CloudFront. - Bilingual Support: i18n scaffolding is in place; translation strings for vendor descriptions (Spanish/English) should be populated before production launch.
- Monitoring: Add CloudWatch alarms for CloudFront 4xx/5xx rates on
/vendors/paths and for test failures in CI/CD.