Skip to content

Commit 5e69e4a

Browse files
authored
Feat/docs seo (#2)
* docs: add seo * docs: add seo
1 parent b45f6fe commit 5e69e4a

File tree

14 files changed

+711
-74
lines changed

14 files changed

+711
-74
lines changed

docs/README.md

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,3 @@
1-
# docs
1+
# funnel Documentation
22

3-
This is a Next.js application generated with
4-
[Create Fumadocs](https://github.com/fuma-nama/fumadocs).
5-
6-
Run development server:
7-
8-
```bash
9-
npm run dev
10-
# or
11-
pnpm dev
12-
# or
13-
yarn dev
14-
```
15-
16-
Open http://localhost:3000 with your browser to see the result.
17-
18-
## Explore
19-
20-
In the project, you can see:
21-
22-
- `lib/source.ts`: Code for content source adapter, [`loader()`](https://fumadocs.dev/docs/headless/source-api) provides the interface to access your content.
23-
- `app/layout.config.tsx`: Shared options for layouts, optional but preferred to keep.
24-
25-
| Route | Description |
26-
| ------------------------- | ------------------------------------------------------ |
27-
| `app/(home)` | The route group for your landing page and other pages. |
28-
| `app/docs` | The documentation layout and pages. |
29-
| `app/api/search/route.ts` | The Route Handler for search. |
30-
31-
### Fumadocs MDX
32-
33-
A `source.config.ts` config file has been included, you can customise different options like frontmatter schema.
34-
35-
Read the [Introduction](https://fumadocs.dev/docs/mdx) for further details.
36-
37-
## Learn More
38-
39-
To learn more about Next.js and Fumadocs, take a look at the following
40-
resources:
41-
42-
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js
43-
features and API.
44-
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
45-
- [Fumadocs](https://fumadocs.vercel.app) - learn about Fumadocs
3+
this is the documentation site for funnel, built with Next.js and fumadocs

docs/next.config.mjs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,65 @@
1-
import { createMDX } from 'fumadocs-mdx/next';
2-
import { transformerTwoslash } from 'fumadocs-twoslash';
1+
import { createMDX } from "fumadocs-mdx/next";
32

43
const withMDX = createMDX();
54

65
/** @type {import('next').NextConfig} */
76
const config = {
87
reactStrictMode: true,
9-
serverExternalPackages: ['typescript', 'twoslash'],
8+
serverExternalPackages: ["typescript", "twoslash"],
9+
compress: true,
10+
poweredByHeader: false,
11+
generateEtags: true,
12+
13+
trailingSlash: false,
14+
15+
images: {
16+
formats: ["image/avif", "image/webp"],
17+
minimumCacheTTL: 31536000,
18+
},
19+
20+
headers: async () => {
21+
return [
22+
{
23+
source: "/(.*)",
24+
headers: [
25+
{
26+
key: "X-Content-Type-Options",
27+
value: "nosniff",
28+
},
29+
{
30+
key: "X-Frame-Options",
31+
value: "DENY",
32+
},
33+
{
34+
key: "X-XSS-Protection",
35+
value: "1; mode=block",
36+
},
37+
{
38+
key: "Referrer-Policy",
39+
value: "strict-origin-when-cross-origin",
40+
},
41+
],
42+
},
43+
{
44+
source: "/sitemap.xml",
45+
headers: [
46+
{
47+
key: "Cache-Control",
48+
value: "public, max-age=86400, s-maxage=86400",
49+
},
50+
],
51+
},
52+
{
53+
source: "/robots.txt",
54+
headers: [
55+
{
56+
key: "Cache-Control",
57+
value: "public, max-age=86400, s-maxage=86400",
58+
},
59+
],
60+
},
61+
];
62+
},
1063
};
1164

1265
export default withMDX(config);

docs/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
"build": "next build",
77
"dev": "next dev --turbo",
88
"start": "next start",
9-
"postinstall": "fumadocs-mdx"
9+
"postinstall": "fumadocs-mdx",
10+
"types": "tsc --noEmit",
11+
"lint": "next lint"
1012
},
1113
"dependencies": {
1214
"@catppuccin/palette": "1.7.1",

docs/public/favicon.svg

Lines changed: 4 additions & 0 deletions
Loading

docs/public/robots.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
User-agent: *
2+
Allow: /
3+
4+
# Sitemap
5+
Sitemap: https://funnel.karolbroda.com/sitemap.xml
6+
7+
# Crawl delay
8+
Crawl-delay: 1

docs/public/site.webmanifest

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "funnel docs",
3+
"short_name": "funnel",
4+
"description": "A tunneling solution built with Go. Expose local services to the internet through websocket connections.",
5+
"start_url": "/",
6+
"display": "standalone",
7+
"background_color": "#000000",
8+
"theme_color": "#000000",
9+
"icons": [
10+
{
11+
"src": "/favicon.ico",
12+
"sizes": "32x32",
13+
"type": "image/x-icon"
14+
},
15+
{
16+
"src": "/apple-touch-icon.png",
17+
"sizes": "180x180",
18+
"type": "image/png"
19+
},
20+
{
21+
"src": "/android-chrome-192x192.png",
22+
"sizes": "192x192",
23+
"type": "image/png"
24+
},
25+
{
26+
"src": "/android-chrome-512x512.png",
27+
"sizes": "512x512",
28+
"type": "image/png"
29+
}
30+
]
31+
}

docs/src/app/(home)/page.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ import Link from "next/link";
1212
import Mermaid from "@/components/mdx/mermaid";
1313
import Aurora from "@/components/ui/aurora";
1414
import InstallScript from "@/components/home/install-script";
15+
import { createMetadata } from "@/lib/seo";
16+
17+
export const metadata = createMetadata(
18+
"funnel",
19+
"expose local services to the internet through websocket connections. perfect for development, testing, and demonstration purposes.",
20+
undefined,
21+
"/"
22+
);
1523

1624
const architecture = `
1725
graph LR
@@ -155,7 +163,7 @@ export default function HomePage() {
155163
/>
156164
<FeatureCard
157165
icon={<LightningIcon className="h-6 w-6" weight="bold" />}
158-
title="never gives up"
166+
title="never gives you up, never lets you down"
159167
description={
160168
"connection dropped? funnel reconnects like that ex who won't take a hint. but useful."
161169
}
Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1-
import { source } from '@/lib/source';
1+
import { source } from "@/lib/source";
22
import {
33
DocsPage,
44
DocsBody,
55
DocsDescription,
66
DocsTitle,
7-
} from 'fumadocs-ui/page';
8-
import { notFound } from 'next/navigation';
9-
import { createRelativeLink } from 'fumadocs-ui/mdx';
10-
import { getMDXComponents } from '@/mdx-components';
7+
} from "fumadocs-ui/page";
8+
import { notFound } from "next/navigation";
9+
import { createMetadata, generateStructuredData, siteConfig } from "@/lib/seo";
10+
import {
11+
getLastModifiedFromUrl,
12+
getCreatedFromUrl,
13+
getAuthorFromUrl,
14+
} from "@/lib/git";
15+
import { Metadata } from "next";
16+
import LastModified from "@/components/mdx/last-modified";
17+
import { getMDXComponents } from "@/mdx-components";
1118

1219
export default async function Page(props: {
1320
params: Promise<{ slug?: string[] }>;
@@ -16,19 +23,34 @@ export default async function Page(props: {
1623
const page = source.getPage(params.slug);
1724
if (!page) notFound();
1825

19-
const MDXContent = page.data.body;
26+
const MDX = page.data.body;
27+
28+
const lastModified = await getLastModifiedFromUrl(page.url);
29+
const datePublished = await getCreatedFromUrl(page.url);
30+
const author = await getAuthorFromUrl(page.url);
31+
32+
const structuredData = generateStructuredData("article", {
33+
title: page.data.title,
34+
description: page.data.description || siteConfig.description,
35+
url: `${siteConfig.url}${page.url}`,
36+
datePublished,
37+
dateModified: lastModified,
38+
author,
39+
});
2040

2141
return (
22-
<DocsPage toc={page.data.toc} full={page.data.full} tableOfContent={{style: "clerk"}} tableOfContentPopover={{style: "clerk"}}>
42+
<DocsPage toc={page.data.toc} full={page.data.full}>
43+
<script
44+
type="application/ld+json"
45+
dangerouslySetInnerHTML={{
46+
__html: JSON.stringify(structuredData),
47+
}}
48+
/>
2349
<DocsTitle>{page.data.title}</DocsTitle>
2450
<DocsDescription>{page.data.description}</DocsDescription>
2551
<DocsBody>
26-
<MDXContent
27-
components={getMDXComponents({
28-
// this allows you to link to other pages with relative file paths
29-
a: createRelativeLink(source, page),
30-
})}
31-
/>
52+
<MDX components={getMDXComponents()} />
53+
<LastModified date={lastModified} author={author} />
3254
</DocsBody>
3355
</DocsPage>
3456
);
@@ -40,13 +62,15 @@ export async function generateStaticParams() {
4062

4163
export async function generateMetadata(props: {
4264
params: Promise<{ slug?: string[] }>;
43-
}) {
65+
}): Promise<Metadata> {
4466
const params = await props.params;
4567
const page = source.getPage(params.slug);
4668
if (!page) notFound();
4769

48-
return {
49-
title: page.data.title,
50-
description: page.data.description,
51-
};
70+
return createMetadata(
71+
page.data.title,
72+
page.data.description,
73+
undefined,
74+
page.url
75+
);
5276
}

docs/src/app/layout.tsx

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,43 @@ import "./global.css";
33
import { RootProvider } from "fumadocs-ui/provider";
44
import { Inter } from "next/font/google";
55
import type { ReactNode } from "react";
6-
import { type Metadata } from "next";
6+
import { createMetadata, generateStructuredData, siteConfig } from "@/lib/seo";
77

88
const inter = Inter({
99
subsets: ["latin"],
1010
});
1111

12-
export const metadata: Metadata = {
13-
title: {
14-
default: "funnel docs",
15-
template: "%s | funnel",
16-
},
17-
};
12+
export const metadata = createMetadata(
13+
siteConfig.name,
14+
siteConfig.description,
15+
siteConfig.ogImage,
16+
"/"
17+
);
1818

1919
export default function Layout({ children }: { children: ReactNode }) {
20+
const structuredData = generateStructuredData("website", {
21+
title: siteConfig.name,
22+
description: siteConfig.description,
23+
url: siteConfig.url,
24+
image: siteConfig.ogImage,
25+
});
26+
2027
return (
2128
<html lang="en" className={inter.className} suppressHydrationWarning>
29+
<head>
30+
<script
31+
type="application/ld+json"
32+
dangerouslySetInnerHTML={{
33+
__html: JSON.stringify(structuredData),
34+
}}
35+
/>
36+
<link rel="icon" href="/favicon.ico" />
37+
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
38+
{/* <link rel="apple-touch-icon" href="/apple-touch-icon.png" /> */}
39+
<link rel="manifest" href="/site.webmanifest" />
40+
<meta name="theme-color" content="#000000" />
41+
<meta name="color-scheme" content="dark light" />
42+
</head>
2243
<body>
2344
<RootProvider>{children}</RootProvider>
2445
</body>

0 commit comments

Comments
 (0)