From 0fa3c3e3ce8046866f6d33046d733d1fda9eb6ce Mon Sep 17 00:00:00 2001 From: BuyWhere Date: Mon, 3 Aug 2026 14:07:31 +0000 Subject: [PATCH] fix(site): BUY-65830 restore missing public route pages and fix /health redirect Root cause: /how-it-works, /product, /features, /developers/mcp, /developers/api, and /developers/sitemap/us route files existed on disk but were never committed to git. Production deploys from main, so these routes returned 404. Additionally, /health was a 308 redirect to api.buywhere.ai/health instead of a direct 200 response. - Add src/app/how-it-works/page.tsx - Add src/app/product/page.tsx - Add src/app/features/page.tsx - Add src/app/developers/api/page.tsx - Add src/app/developers/mcp/page.tsx - Add src/app/developers/sitemap/us/route.ts - Fix src/app/health/route.ts: return 200 directly instead of 308 redirect - Fix src/app/api/health/route.ts: import ordering + edge runtime Refs: BUY-65830, BUY-65679 --- src/app/api/health/route.ts | 11 +- src/app/developers/api/page.tsx | 112 +++++++++++++++++++ src/app/developers/mcp/page.tsx | 121 +++++++++++++++++++++ src/app/developers/sitemap/us/route.ts | 8 ++ src/app/features/page.tsx | 143 +++++++++++++++++++++++++ src/app/health/route.ts | 42 +++++--- src/app/how-it-works/page.tsx | 129 ++++++++++++++++++++++ src/app/product/page.tsx | 120 +++++++++++++++++++++ 8 files changed, 668 insertions(+), 18 deletions(-) create mode 100644 src/app/developers/api/page.tsx create mode 100644 src/app/developers/mcp/page.tsx create mode 100644 src/app/developers/sitemap/us/route.ts create mode 100644 src/app/features/page.tsx create mode 100644 src/app/how-it-works/page.tsx create mode 100644 src/app/product/page.tsx diff --git a/src/app/api/health/route.ts b/src/app/api/health/route.ts index b01dc1fd3..4f395255e 100644 --- a/src/app/api/health/route.ts +++ b/src/app/api/health/route.ts @@ -1,3 +1,8 @@ +import { NextResponse } from "next/server"; + +export const runtime = "edge"; +export const dynamic = "force-dynamic"; + /** * GET /api/health * @@ -5,12 +10,6 @@ * Returns 200 with a stable JSON body. The deeper content-level probe lives * at /api/health/site. */ - -import { NextResponse } from "next/server"; - -export const runtime = "nodejs"; -export const dynamic = "force-dynamic"; - export async function GET(): Promise { return NextResponse.json( { diff --git a/src/app/developers/api/page.tsx b/src/app/developers/api/page.tsx new file mode 100644 index 000000000..fcb07d1a7 --- /dev/null +++ b/src/app/developers/api/page.tsx @@ -0,0 +1,112 @@ +import Nav from "@/components/Nav"; +import Footer from "@/components/Footer"; +import Link from "next/link"; +import type { Metadata } from "next"; +import Schema from "@/components/Schema"; +import { buildWebPageSchema } from "@/lib/page-schema"; +import { buildPageMetadata } from "@/lib/page-metadata"; + +export const metadata: Metadata = buildPageMetadata({ + title: "REST API — BuyWhere Developers", + description: + "Search, compare, and retrieve products across 158,000+ storefronts with the BuyWhere REST API. One schema, one key, multi-region.", + path: "/developers/api/", +}); + +const curlExample = `curl -sS "https://api.buywhere.ai/v1/products/search?q=wireless+headphones&limit=5" \\ + -H "Authorization: Bearer bw_live_your_key_here"`; + +export default function ApiDevelopersPage() { + const schema = buildWebPageSchema({ + path: "/developers/api", + name: "REST API — BuyWhere Developers", + description: + "Search, compare, and retrieve products across 158,000+ storefronts with the BuyWhere REST API. One schema, one key, multi-region.", + breadcrumb: [ + { name: "Home", path: "/" }, + { name: "Developers", path: "/developers" }, + { name: "REST API", path: "/developers/api" }, + ], + }); + + return ( + <> + +
+
+ + ); +} diff --git a/src/app/developers/mcp/page.tsx b/src/app/developers/mcp/page.tsx new file mode 100644 index 000000000..17310a74b --- /dev/null +++ b/src/app/developers/mcp/page.tsx @@ -0,0 +1,121 @@ +import Nav from "@/components/Nav"; +import Footer from "@/components/Footer"; +import Link from "next/link"; +import type { Metadata } from "next"; +import Schema from "@/components/Schema"; +import { buildWebPageSchema } from "@/lib/page-schema"; +import { buildPageMetadata } from "@/lib/page-metadata"; + +export const metadata: Metadata = buildPageMetadata({ + title: "MCP Server — BuyWhere Developers", + description: + "Add the BuyWhere MCP server to Claude, Cursor, or any MCP client and give your AI agent product search, comparison, and merchant handoff tools.", + path: "/developers/mcp/", +}); + +const mcpConfig = `{ + "mcpServers": { + "buywhere": { + "command": "npx", + "args": ["-y", "@buywhere/mcp-server"], + "env": { + "BUYWHERE_API_KEY": "bw_live_your_key_here" + } + } + } +}`; + +export default function McpDevelopersPage() { + const schema = buildWebPageSchema({ + path: "/developers/mcp", + name: "MCP Server — BuyWhere Developers", + description: + "Add the BuyWhere MCP server to Claude, Cursor, or any MCP client and give your AI agent product search, comparison, and merchant handoff tools.", + breadcrumb: [ + { name: "Home", path: "/" }, + { name: "Developers", path: "/developers" }, + { name: "MCP Server", path: "/developers/mcp" }, + ], + }); + + return ( + <> + +
+
+ + ); +} diff --git a/src/app/developers/sitemap/us/route.ts b/src/app/developers/sitemap/us/route.ts new file mode 100644 index 000000000..4f3274cec --- /dev/null +++ b/src/app/developers/sitemap/us/route.ts @@ -0,0 +1,8 @@ +import { buildSitemapResponse, getMerchantListingSitemapEntries, renderUrlSet } from "@/lib/sitemaps"; + +export const dynamic = "force-dynamic"; + +export async function GET(): Promise { + const entries = await getMerchantListingSitemapEntries(); + return buildSitemapResponse(renderUrlSet(entries)); +} diff --git a/src/app/features/page.tsx b/src/app/features/page.tsx new file mode 100644 index 000000000..bbeb64dc1 --- /dev/null +++ b/src/app/features/page.tsx @@ -0,0 +1,143 @@ +import Nav from "@/components/Nav"; +import Footer from "@/components/Footer"; +import Link from "next/link"; +import type { Metadata } from "next"; +import Schema from "@/components/Schema"; +import { buildWebPageSchema } from "@/lib/page-schema"; +import { buildPageMetadata } from "@/lib/page-metadata"; + +export const metadata: Metadata = buildPageMetadata({ + title: "Features — BuyWhere", + description: + "Agent-first product catalog, real-time availability, normalized data, MCP server, and multi-region coverage — built for AI shopping agents.", + path: "/features/", +}); + +const features = [ + { + title: "Agent-first API", + description: + "REST endpoints return structured JSON with product, merchant, price, and availability fields — designed for LLM reasoning and tool calling.", + }, + { + title: "MCP server", + description: + "Add BuyWhere tools directly to Claude Desktop, Cursor, Windsurf, and any MCP client with one npx command.", + }, + { + title: "Location-aware ranking", + description: + "Pass deliver_to with a country code and receive results ranked by what can actually reach your user: local, ships_to_you, or unavailable.", + }, + { + title: "Normalized product schema", + description: + "One consistent schema across 158,000+ storefronts. No brittle parsing, no site-specific adapters, no cleaning boilerplate.", + }, + { + title: "Sub-250ms search", + description: + "Low-latency semantic and keyword search across 288M+ products, backed by purpose-built indexing and strict query plans.", + }, + { + title: "Price comparisons & history", + description: + "Compare merchant offers for the same product and query historical price trends to surface deals and price-drop alerts.", + }, + { + title: "SDKs & compact mode", + description: + "Official Python, LangChain, and OpenAI-tools SDKs. Use compact=true to reduce context-window usage inside agent loops.", + }, + { + title: "Developer dashboard", + description: + "Track usage, rotate keys, manage alerts, and monitor catalog health from a single developer portal.", + }, +]; + +export default function FeaturesPage() { + const schema = buildWebPageSchema({ + path: "/features", + name: "Features — BuyWhere", + description: + "Agent-first product catalog, real-time availability, normalized data, MCP server, and multi-region coverage — built for AI shopping agents.", + breadcrumb: [ + { name: "Home", path: "/" }, + { name: "Features", path: "/features" }, + ], + }); + + return ( + <> + +
+
+ + ); +} diff --git a/src/app/health/route.ts b/src/app/health/route.ts index 37a1fdedc..8e39fb6b2 100644 --- a/src/app/health/route.ts +++ b/src/app/health/route.ts @@ -1,17 +1,35 @@ -import { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; -function buildRedirectUrl(request: NextRequest): string { - const url = new URL("https://api.buywhere.ai/health"); - request.nextUrl.searchParams.forEach((value, key) => { - url.searchParams.append(key, value); - }); - return url.toString(); -} +export const runtime = "edge"; +export const dynamic = "force-dynamic"; -export function GET(request: NextRequest): Response { - return Response.redirect(buildRedirectUrl(request), 308); +/** + * GET /health + * + * Lightweight site-level liveness probe. Returns 200 with a stable JSON body. + * The deeper content-level probe lives at /api/health/site. + */ +export async function GET(): Promise { + return NextResponse.json( + { + status: "ok", + ts: new Date().toISOString(), + service: "buywhere-site", + }, + { + status: 200, + headers: { + "Cache-Control": "no-store, no-cache, must-revalidate", + }, + }, + ); } -export function HEAD(request: NextRequest): Response { - return Response.redirect(buildRedirectUrl(request), 308); +export function HEAD(): Response { + return new Response(null, { + status: 200, + headers: { + "Cache-Control": "no-store, no-cache, must-revalidate", + }, + }); } diff --git a/src/app/how-it-works/page.tsx b/src/app/how-it-works/page.tsx new file mode 100644 index 000000000..c392f1e65 --- /dev/null +++ b/src/app/how-it-works/page.tsx @@ -0,0 +1,129 @@ +import Nav from "@/components/Nav"; +import Footer from "@/components/Footer"; +import Link from "next/link"; +import type { Metadata } from "next"; +import Schema from "@/components/Schema"; +import { HowItWorksSection } from "@/components/HowItWorksSection"; +import { buildWebPageSchema } from "@/lib/page-schema"; +import { buildPageMetadata } from "@/lib/page-metadata"; + +export const metadata: Metadata = buildPageMetadata({ + title: "How It Works — BuyWhere", + description: + "See how BuyWhere turns a natural language product query into ranked, purchase-ready product data for AI agents.", + path: "/how-it-works/", +}); + +export default function HowItWorksPage() { + const schema = buildWebPageSchema({ + path: "/how-it-works", + name: "How It Works — BuyWhere", + description: + "From natural language query to purchase-ready product data — BuyWhere handles the complexity so your agents don't have to.", + breadcrumb: [ + { name: "Home", path: "/" }, + { name: "How It Works", path: "/how-it-works" }, + ], + }); + + return ( + <> + +
+
+ + ); +} diff --git a/src/app/product/page.tsx b/src/app/product/page.tsx new file mode 100644 index 000000000..27abb8b80 --- /dev/null +++ b/src/app/product/page.tsx @@ -0,0 +1,120 @@ +import Nav from "@/components/Nav"; +import Footer from "@/components/Footer"; +import Link from "next/link"; +import type { Metadata } from "next"; +import Schema from "@/components/Schema"; +import { HomeProductSearch } from "@/components/HomeProductSearch"; +import { buildWebPageSchema } from "@/lib/page-schema"; +import { buildPageMetadata } from "@/lib/page-metadata"; + +export const metadata: Metadata = buildPageMetadata({ + title: "Product Search — BuyWhere", + description: + "Search 288M+ products across 158,000+ storefronts with one API. Real-time, normalized, and location-aware.", + path: "/product/", +}); + +export default function ProductPage() { + const schema = buildWebPageSchema({ + path: "/product", + name: "Product Search — BuyWhere", + description: + "Search 288M+ products across 158,000+ storefronts with one API. Real-time, normalized, and location-aware.", + breadcrumb: [ + { name: "Home", path: "/" }, + { name: "Product Search", path: "/product" }, + ], + }); + + return ( + <> + +
+
+ + ); +}