Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion apps/docs/app/(home)/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,30 @@ export async function generateMetadata({
notFound();
}

const docsBaseUrl = process.env.DOCS_URL ?? "https://docs.llmgateway.io";
const path = page.url === "/" ? "" : page.url;
const canonicalUrl = `${docsBaseUrl}${path}`;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
Comment thread
smakosh marked this conversation as resolved.
const image = ["/docs-og", ...slug, "image.png"].join("/");

return {
metadataBase: new URL(process.env.DOCS_URL ?? "https://docs.llmgateway.io"),
metadataBase: new URL(docsBaseUrl),
title: page.data.title,
description: page.data.description,
alternates: {
canonical: canonicalUrl,
},
openGraph: {
title: page.data.title,
description: page.data.description,
url: canonicalUrl,
images: image,
type: "article",
siteName: "LLM Gateway Docs",
},
twitter: {
card: "summary_large_image",
title: page.data.title,
description: page.data.description,
images: image,
},
};
Expand Down
20 changes: 17 additions & 3 deletions apps/docs/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,31 @@ const mono = Geist_Mono({

export const dynamic = "force-dynamic";

const docsBaseUrl = process.env.DOCS_URL ?? "https://docs.llmgateway.io";

export const metadata: Metadata = {
metadataBase: new URL("https://llmgateway.io"),
title: "LLM Gateway Documentation",
metadataBase: new URL(docsBaseUrl),
title: {
default: "LLM Gateway Documentation",
template: "%s | LLM Gateway Docs",
},
description:
"LLM Gateway Documentation - Route, manage, and analyze your LLM requests across multiple providers with a unified API interface.",
"Route, manage, and analyze LLM requests across multiple providers with a unified API. Guides, API reference, and self-hosting docs.",
icons: {
icon: "/favicon/favicon.ico?v=2",
},
alternates: {
canonical: "./",
},
openGraph: {
siteName: "LLM Gateway Docs",
type: "website",
locale: "en_US",
},
robots: {
index: true,
follow: true,
},
};

export default function Layout({ children }: { children: ReactNode }) {
Expand Down
16 changes: 16 additions & 0 deletions apps/docs/app/robots.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { MetadataRoute } from "next";

export default function robots(): MetadataRoute.Robots {
const docsBaseUrl = process.env.DOCS_URL ?? "https://docs.llmgateway.io";

return {
rules: [
{
userAgent: "*",
allow: "/",
disallow: ["/api/", "/llms.mdx/", "/docs-og/"],
Comment thread
smakosh marked this conversation as resolved.
Outdated
},
],
sitemap: `${docsBaseUrl}/sitemap.xml`,
};
Comment on lines +10 to +25

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if other files in the docs app use force-dynamic alongside env var reads
rg -n "force-dynamic|dynamic.*=.*force" apps/docs/ --type=ts -C2

Repository: theopenco/llmgateway

Length of output: 418


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the docs app files that read DOCS_URL and the robots route itself.
sed -n '1,120p' apps/docs/app/layout.tsx
printf '\n---\n'
sed -n '1,120p' apps/docs/app/robots.ts
printf '\n---\n'
rg -n "DOCS_URL|dynamic = \"force-dynamic\"" apps/docs/app -C 2

Repository: theopenco/llmgateway

Length of output: 3605


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the robots route and nearby app route conventions.
sed -n '1,120p' apps/docs/app/robots.ts
printf '\n---\n'
sed -n '1,80p' apps/docs/app/layout.tsx
printf '\n---\n'
rg -n "export const dynamic|MetadataRoute.Robots|DOCS_URL" apps/docs/app -C 2

Repository: theopenco/llmgateway

Length of output: 3674


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,120p' apps/docs/app/sitemap.ts

Repository: theopenco/llmgateway

Length of output: 685


Add export const dynamic = "force-dynamic" to the docs metadata routes that read DOCS_URL. apps/docs/app/layout.tsx already does this, but apps/docs/app/robots.ts and apps/docs/app/sitemap.ts can otherwise be emitted with the fallback host instead of the runtime value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/docs/app/robots.ts` around lines 3 - 15, Add dynamic rendering to the
docs metadata routes that depend on DOCS_URL so they resolve the runtime host
instead of being statically emitted with the fallback value. Update robots() in
robots.ts, and the matching sitemap route, to export const dynamic =
"force-dynamic" alongside the existing MetadataRoute logic, mirroring the
pattern already used in apps/docs/app/layout.tsx.

}
18 changes: 18 additions & 0 deletions apps/docs/app/sitemap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { source } from "@/lib/source";

import type { MetadataRoute } from "next";

export default function sitemap(): MetadataRoute.Sitemap {
const docsBaseUrl = process.env.DOCS_URL ?? "https://docs.llmgateway.io";
const buildDate = new Date();

return source.getPages().map((page) => {
const path = page.url === "/" ? "" : page.url;
return {
url: `${docsBaseUrl}${path}`,
lastModified: buildDate,
Comment thread
smakosh marked this conversation as resolved.
Outdated
changeFrequency: page.url === "/" ? "weekly" : "monthly",
priority: page.url === "/" ? 1 : 0.7,
};
});
}
7 changes: 5 additions & 2 deletions apps/ui/src/app/agents/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import type { Metadata } from "next";
export const metadata: Metadata = {
title: "AI Agents — Pre-built Tool-Calling Agents",
description:
"Pre-built AI agents ready to integrate into your applications. Weather, search, and more.",
"Pre-built tool-calling AI agents ready to integrate — weather, search, and more, powered by LLM Gateway.",
alternates: { canonical: "/agents" },
openGraph: {
title: "AI Agents — Pre-built Tool-Calling Agents",
description:
"Pre-built AI agents ready to integrate into your applications. Weather, search, and more.",
"Pre-built tool-calling AI agents ready to integrate — weather, search, and more, powered by LLM Gateway.",
url: "https://llmgateway.io/agents",
type: "website",
},
};

Expand Down
5 changes: 4 additions & 1 deletion apps/ui/src/app/apps/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Apps using LLM Gateway",
description:
"Browse coding agents and tools sending traffic through LLM Gateway, ranked by tokens processed. Claude Code, Cursor, Cline, OpenCode, Aider, Continue, and more.",
"Browse coding agents and tools on LLM Gateway, ranked by tokens. Claude Code, Cursor, Cline, OpenCode, Aider, and more.",
alternates: { canonical: "/apps" },
openGraph: {
title: "Apps using LLM Gateway",
description:
"Coding agents and tools running on LLM Gateway, ranked by tokens processed.",
url: "https://llmgateway.io/apps",
type: "website",
},
};

Expand Down
11 changes: 10 additions & 1 deletion apps/ui/src/app/blog/category/[category]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ export async function generateMetadata({
const decoded = decodeURIComponent(category);
return {
title: `Blog: ${decoded}`,
description: `Articles in the ${decoded} category at LLM Gateway`,
description: `Articles in the ${decoded} category at LLM Gateway — news, tutorials, and product updates.`,
alternates: {
canonical: `/blog/category/${encodeURIComponent(decoded)}`,
Comment thread
smakosh marked this conversation as resolved.
Outdated
},
openGraph: {
Comment thread
smakosh marked this conversation as resolved.
title: `Blog: ${decoded}`,
description: `Articles in the ${decoded} category at LLM Gateway — news, tutorials, and product updates.`,
url: `https://llmgateway.io/blog/category/${encodeURIComponent(decoded)}`,
type: "website",
},
};
}
89 changes: 87 additions & 2 deletions apps/ui/src/app/blog/category/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,88 @@
export default function Page() {
return null;
import Link from "next/link";
import { redirect } from "next/navigation";

import { HeroRSC } from "@/components/landing/hero-rsc";

import type { Metadata } from "next";

function slugify(label: string) {
return label
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/(^-|-$)/g, "");
}

export const metadata: Metadata = {
title: "Blog Categories",
description:
"Browse LLM Gateway blog posts by category — product updates, tutorials, deep-dives, and more.",
alternates: { canonical: "/blog/category" },
openGraph: {
title: "Blog Categories",
description:
"Browse LLM Gateway blog posts by category — product updates, tutorials, deep-dives, and more.",
url: "https://llmgateway.io/blog/category",
type: "website",
},
};

export default async function BlogCategoryIndexPage() {
const { allBlogs } = await import("content-collections");
Comment thread
smakosh marked this conversation as resolved.
Outdated

const categories = new Map<string, string>();
for (const post of allBlogs) {
if (post.draft) {
continue;
}
for (const category of post.categories ?? []) {
const slug = slugify(category);
if (!categories.has(slug)) {
categories.set(slug, category);
}
}
}

const sorted = Array.from(categories.entries()).sort((a, b) =>
a[1].localeCompare(b[1]),
);

if (sorted.length === 1) {
redirect(`/blog/category/${encodeURIComponent(sorted[0][0])}`);
Comment thread
smakosh marked this conversation as resolved.
Outdated
}

return (
<>
<HeroRSC navbarOnly />
<div className="container mx-auto max-w-3xl px-4 py-16 pt-28 md:pt-36">
<h1 className="mb-4 text-3xl font-bold tracking-tight md:text-4xl">
Blog Categories
</h1>
<p className="mb-10 text-muted-foreground">
Browse posts by topic across the LLM Gateway blog.
</p>
{sorted.length === 0 ? (
<p className="text-muted-foreground">
No categories yet.{" "}
<Link href="/blog" className="text-primary underline">
View all posts
</Link>
.
</p>
) : (
<ul className="grid gap-3 sm:grid-cols-2">
{sorted.map(([slug, label]) => (
<li key={slug}>
<Link
href={`/blog/category/${encodeURIComponent(slug)}`}
className="block rounded-lg border border-border bg-card px-4 py-3 text-sm font-medium transition-colors hover:bg-muted"
>
{label}
</Link>
</li>
))}
</ul>
)}
</div>
</>
);
}
11 changes: 8 additions & 3 deletions apps/ui/src/app/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,21 @@ export default async function BlogPage() {
export async function generateMetadata() {
return {
title: "Blog — News, Tutorials, and Deep-Dives",
description: "News, tutorials, and deep-dives from the LLM Gateway team.",
description:
"News, tutorials, and deep-dives from the LLM Gateway team on AI gateways, routing, costs, and building with LLMs.",
alternates: { canonical: "/blog" },
openGraph: {
title: "Blog — News, Tutorials, and Deep-Dives",
description: "News, tutorials, and deep-dives from the LLM Gateway team.",
description:
"News, tutorials, and deep-dives from the LLM Gateway team on AI gateways, routing, costs, and building with LLMs.",
type: "website",
url: "https://llmgateway.io/blog",
},
twitter: {
card: "summary_large_image",
title: "Blog — News, Tutorials, and Deep-Dives",
description: "News, tutorials, and deep-dives from the LLM Gateway team.",
description:
"News, tutorials, and deep-dives from the LLM Gateway team on AI gateways, routing, and building with LLMs.",
},
};
}
9 changes: 6 additions & 3 deletions apps/ui/src/app/brand/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import { HeroRSC } from "@/components/landing/hero-rsc";
import type { Metadata } from "next";

export const metadata: Metadata = {
title: "Brand Assets | LLM Gateway",
title: "Brand Assets",
description:
"Download official LLM Gateway logos and brand assets. Get our logo in PNG or SVG format, with or without the name, in black and white variants.",
alternates: { canonical: "/brand" },
openGraph: {
title: "Brand Assets | LLM Gateway",
title: "Brand Assets",
description:
"Download official LLM Gateway logos and brand assets. Get our logo in PNG or SVG format, with or without the name, in black and white variants.",
"Download official LLM Gateway logos and brand assets in PNG or SVG, light and dark variants.",
url: "https://llmgateway.io/brand",
type: "website",
},
};

Expand Down
11 changes: 6 additions & 5 deletions apps/ui/src/app/brand/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ import Logo from "@/lib/icons/Logo";
import type { Metadata } from "next";

export const metadata: Metadata = {
title: "Brand Assets — LLM Gateway Logos & Guidelines",
title: "Brand Assets — Logos & Guidelines",
description:
"Download official LLM Gateway logos, marks, and brand assets. SVG files in light and dark variants, plus usage guidelines for partners and integrators.",
"Download official LLM Gateway logos, marks, and brand assets. SVG files in light and dark variants, plus usage guidelines for partners.",
openGraph: {
title: "Brand Assets — LLM Gateway Logos & Guidelines",
title: "Brand Assets — Logos & Guidelines",
description:
"Download official LLM Gateway logos, marks, and brand assets. SVG files in light and dark variants, plus usage guidelines.",
"Download official LLM Gateway logos, marks, and brand assets. SVG files in light and dark variants.",
type: "website",
url: "https://llmgateway.io/brand",
},
twitter: {
card: "summary_large_image",
title: "Brand Assets — LLM Gateway Logos & Guidelines",
title: "Brand Assets — Logos & Guidelines",
description:
"Download official LLM Gateway logos, marks, and brand assets.",
},
Expand Down
6 changes: 4 additions & 2 deletions apps/ui/src/app/changelog/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,12 @@ export async function generateMetadata({
return {
title: `${entry.title} - Changelog`,
description: entry.summary ?? "LLM Gateway changelog entry",
alternates: { canonical: `/changelog/${entry.slug}` },
openGraph: {
title: `${entry.title} - Changelog - LLM Gateway`,
title: `${entry.title} - Changelog`,
description: entry.summary ?? "LLM Gateway changelog entry",
type: "article",
url: `https://llmgateway.io/changelog/${entry.slug}`,
images: entry.image
? [
{
Expand All @@ -208,7 +210,7 @@ export async function generateMetadata({
},
twitter: {
card: "summary_large_image",
title: `${entry.title} - Changelog - LLM Gateway`,
title: `${entry.title} - Changelog`,
description: entry.summary ?? "LLM Gateway changelog entry",
},
};
Expand Down
12 changes: 7 additions & 5 deletions apps/ui/src/app/compare/aws-bedrock/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,20 @@ export async function generateMetadata() {
return {
title: "LLM Gateway vs AWS Bedrock — The Cloud-Neutral Alternative",
description:
"Compare LLM Gateway and Amazon Bedrock: 40+ providers behind one OpenAI-compatible API vs an AWS-hosted catalog. Keep Bedrock as a provider with 0% markup and add cross-cloud failover, caching, and cost analytics.",
"Compare 40+ providers behind one OpenAI-compatible API vs Amazon Bedrock. Keep Bedrock with 0% markup plus failover, caching, and cost analytics.",
alternates: { canonical: "/compare/aws-bedrock" },
openGraph: {
title: "LLM Gateway vs AWS Bedrock - Feature Comparison",
title: "LLM Gateway vs AWS Bedrock Feature Comparison",
description:
"Compare LLM Gateway's cloud-neutral, open-source platform against Amazon Bedrock's AWS-hosted model catalog. Route to Bedrock and 40+ other providers from one API.",
"Cloud-neutral gateway vs AWS Bedrock. Route to Bedrock and 40+ providers from one API with failover and analytics.",
type: "website",
url: "https://llmgateway.io/compare/aws-bedrock",
},
twitter: {
card: "summary_large_image",
title: "LLM Gateway vs AWS Bedrock - Feature Comparison",
title: "LLM Gateway vs AWS Bedrock Feature Comparison",
description:
"Compare LLM Gateway's cloud-neutral, open-source platform against Amazon Bedrock's AWS-hosted model catalog. Route to Bedrock and 40+ other providers from one API.",
"Cloud-neutral gateway vs AWS Bedrock. Route to Bedrock and 40+ providers from one API.",
},
};
}
12 changes: 7 additions & 5 deletions apps/ui/src/app/compare/azure-ai-foundry/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,20 @@ export async function generateMetadata() {
return {
title: "LLM Gateway vs Azure AI Foundry — The Cloud-Neutral Alternative",
description:
"Compare LLM Gateway and Azure AI Foundry: 40+ providers behind one OpenAI-compatible API vs an Azure-hosted catalog with deployments and quotas. Keep Azure as a provider with 0% markup and add cross-cloud failover, caching, and cost analytics.",
"Compare 40+ providers behind one OpenAI-compatible API vs Azure AI Foundry. Keep Azure with 0% markup plus failover, caching, and cost analytics.",
alternates: { canonical: "/compare/azure-ai-foundry" },
openGraph: {
title: "LLM Gateway vs Azure AI Foundry - Feature Comparison",
title: "LLM Gateway vs Azure AI Foundry Feature Comparison",
description:
"Compare LLM Gateway's cloud-neutral, open-source platform against Azure AI Foundry's Azure-hosted model catalog. Route to Azure and 40+ other providers from one API.",
"Cloud-neutral gateway vs Azure AI Foundry. Route to Azure and 40+ providers from one API with failover and analytics.",
type: "website",
url: "https://llmgateway.io/compare/azure-ai-foundry",
},
twitter: {
card: "summary_large_image",
title: "LLM Gateway vs Azure AI Foundry - Feature Comparison",
title: "LLM Gateway vs Azure AI Foundry Feature Comparison",
description:
"Compare LLM Gateway's cloud-neutral, open-source platform against Azure AI Foundry's Azure-hosted model catalog. Route to Azure and 40+ other providers from one API.",
"Cloud-neutral gateway vs Azure AI Foundry. Route to Azure and 40+ providers from one API.",
},
};
}
Loading
Loading