Cloudflare Workers + D1 e-commerce site for Shengtuo Tractor. Deploy, DB ops, SEO, product sync.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add clowlove/Hermes-House --skill cf-b2b-website --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Cf B2b Website?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/clowlove-cf-b2b-website)More formats (shields.io, HTML) on the badges page.
---
name: cf-b2b-website
description: Cloudflare Workers + D1 e-commerce site for Shengtuo Tractor. Deploy, DB ops, SEO, product sync.
triggers:
- cf-b2b-website
- shengtuo tractor website
- wrangler deploy
- D1 database
- sitemap.xml
---
# cf-b2b-website
## Environment
| Item | Value |
|------|-------|
| Worker | `cf-b2b-website` |
| D1 DB | `b2b_database` (remote) |
| KV | `b2b_kv` (contact form) |
| Deploy token | `cfut_ZENfTsTx1akFdl...` |
| URL | `https://cf-b2b-website.sdtractor.workers.dev` |
| Src | `/home/ubuntu/cf_b2b/` |
## Deploy
### Normal deploy
```bash
cd /home/ubuntu/cf_b2b && CLOUDFLARE_API_TOKEN="cfut_..." npx wrangler deploy
```
### Force redeploy (when cached/stale)
```bash
npx wrangler deploy --force
```
> ⚠️ **Pitfall**: `wrangler deploy` can exit 0 with NO visible output even on syntax errors. Always check output contains `Uploaded` and `Deployed` lines. If output is empty, check for JS syntax errors (e.g., duplicate `const` declarations) before retrying.
## D1 Database
### Batch SQL via file
```bash
npx wrangler d1 execute b2b_database --file=/tmp/sql.sql --remote
```
> ⚠️ **Pitfall**: D1 does NOT support `BEGIN;...COMMIT;` in file mode. Execute statements individually — each `UPDATE/SET` on its own line. No transactions.
### Single query
```bash
npx wrangler d1 execute b2b_database --command="SELECT id, name FROM products LIMIT 5;" --remote
```
## Product Catalog Sync (from shengtuo-tractor.com)
Site is **Error 1016** (partially down) but browser still loads content — product images remain valid at `shengtuo-tractor.com/uploadfile/` URLs.
### Extract product list via browser console
```javascript
[...document.querySelectorAll('ul.products_list li')].map(li => {
const a = li.querySelector('a[href$=".html"]');
const img = li.querySelector('img');
return a ? { name: a.title, href: a.href, img: img ? img.src : null } : null;
}).filter(Boolean)
```
### Match strategy
Extract `ST\d+[A-Z]*` pattern from name to map site products → DB products by ID.
### SQL generation pattern
```sql
UPDATE products SET image_url = 'https://www.shengtuo-tractor.com/uploadfile/FILE.jpg' WHERE id = N;
-- One statement per line, no BEGIN/COMMIT
```
## JS File Editing — CRITICAL
> ⚠️ **NEVER use `sed` on `.js` files** — corrupts template literal backtick nesting (`\`` → raw `` ` ``).
> ⚠️ **NEVER use `echo heredoc` to write `.js` files** — same backtick corruption risk.
**Always use `patch()` or `write_file()` only.**
Common error this causes: inline `<script>` tags inside template literals get mangled, producing "has already been declared" or "unexpected token" build errors.
## SEO Keyword Structure (100+ keywords in layout.js defaultKeywords)
Organized in `src/pages/layout.js`:
- **Brand**: China Shengtuo Tractor, Shengtuo Farm Tractor, Chinese Tractor Manufacturer, etc.
- **Product HP** (25–260HP): Compact Tractor, Mini Farm Tractor, Garden Tractor, Utility Tractor, Heavy Duty Farm Tractor
- **Buyer intent**: Farm Tractor for Sale, China Tractor Exporter, Wholesale Farm Tractor, Buy Farm Tractor
- **Farm equipment**: Rotary Tiller, Disc Harrow, Disc Plough, Front Loader, Backhoe Loader, Hay Baler
- **Long-tail**: 4WD Farm Tractor for Sale, Diesel Farm Tractor China, CE Farm Tractor Manufacturer, OEM Agricultural Tractor
- **Model series**: ST504, ST704, ST804, ST1154, ST1354, ST1604, ST1804, ST2204, ST2404 Tractor
- **Country markets**: Australia Tractor Supplier, South Africa Farm Tractor, Brazil Tractor Manufacturer, Romania Agricultural Tractor
### Homepage Title Override Pattern (IMPORTANT)
`createLayout` uses `isHome = (title === 'Home')` to set special SEO title. When passing site_name (e.g. 'China Shengtuo Farm Tractor Manufacturer') instead of 'Home', `pageTitlePrefix` won't trigger and canonical breaks.
**Always do this for homepage**:
```javascript
const homeTitle = 'China Shengtuo Farm Tractor Manufacturer | 25HP-260HP Agricultural Tractors & Farm Equipment';
const html = createLayout('Home', content, scripts, desc, false);
const finalHtml = html.replace('<title>Shengtuo Tractor</title>', `<title>${homeTitle}</title>`);
return new Response(finalHtml, { ... });
```
### Canonical URL Logic
```javascript
const isHome = title === 'Home';
const canonicalPath = isHome ? '/' : '/' + title.toLowerCase().replace(/\s+/g,'-').replace(/[^a-z0-9-/]/g,'');
```
### Sitemap + Robots.txt
Both generated by `src/pages/sitemap.js`:
- `/sitemap.xml` — 42 product URLs + 4 static pages
- `/robots.txt` — allows all except /api/ and /admin/
### Product Sort Order — FIX BOTH FILES
Products should show Tractors first, then Equipment. Both of these need the same SQL `ORDER BY CASE` pattern:
1. `src/pages/products.js` — server-rendered page
2. `src/api/handlers/products.js` — API endpoint
SQL pattern:
```sql
ORDER BY CASE WHEN category = 'SHENGTUO TRACTOR' THEN 1
WHEN category = 'SHENGTUO FARM EQUIPMENT' THEN 2
ELSE 3 END, name
```
> ⚠️ **Pitfall**: Even if products.js is fixed, if the API returns wrong order, pages that rely on API data will show wrong order. Fix BOTH files.
### Product Detail Page Keywords
Build from DB fields dynamically:
```javascript
const hp = product.hp_range || '';
const spec = product.key_spec || '';
metaKeywords = `${product.name}, Shengtuo ${category}, farm equipment, ${hp} tractor, agricultural machinery China, ${spec}`;
```
## Tech Stack
- Cloudflare Workers (Pages-style routing via `router.js`)
- D1 (SQLite) for product DB
- KV for contact form storage
- No build step — pure ES module `.js` filesIs this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!