Skip to content

Commit abd4b3a

Browse files
committed
fix: docs
1 parent c469164 commit abd4b3a

File tree

29 files changed

+686
-956
lines changed

29 files changed

+686
-956
lines changed

apps/docs/.gitignore

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
1-
node_modules
1+
# deps
2+
/node_modules
23

4+
# generated content
5+
.source
6+
7+
# test & build
8+
/coverage
9+
/.next/
10+
/out/
11+
/build
12+
*.tsbuildinfo
13+
14+
# misc
315
.DS_Store
4-
.cache
5-
.vercel
6-
.output
7-
.nitro
8-
/build/
9-
/api/
10-
/server/build
11-
/public/build
12-
/test-results/
13-
/playwright-report/
14-
/blob-report/
15-
/playwright/.cache/
16-
.tanstack
16+
*.pem
17+
/.pnp
18+
.pnp.js
19+
npm-debug.log*
20+
yarn-debug.log*
21+
yarn-error.log*
1722

18-
src/routeTree.gen.ts
19-
.source
23+
# others
24+
.env*.local
25+
.vercel
26+
next-env.d.ts

apps/docs/README.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# docs
1+
# docs-next
22

3-
This is a Tanstack Start application generated with
3+
This is a Next.js application generated with
44
[Create Fumadocs](https://github.com/fuma-nama/fumadocs).
55

66
Run development server:
@@ -12,3 +12,34 @@ pnpm dev
1212
# or
1313
yarn dev
1414
```
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+
- `lib/layout.shared.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.dev) - learn about Fumadocs

apps/docs/app/(home)/layout.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { HomeLayout } from 'fumadocs-ui/layouts/home';
2+
import { baseOptions } from '@/lib/layout.shared';
3+
4+
export default function Layout({ children }: LayoutProps<'/'>) {
5+
return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;
6+
}

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

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import Link from 'next/link';
2+
3+
export default function HomePage() {
4+
return (
5+
<div className="min-h-screen bg-landing-bg">
6+
{/* Hero Section */}
7+
<section className="min-h-screen flex items-center justify-center px-4 py-32">
8+
<div className="max-w-5xl w-full text-center">
9+
<h1 className="text-7xl font-semibold mb-8 leading-tight text-landing-text">
10+
keypal
11+
</h1>
12+
<p className="text-3xl mb-6 leading-relaxed font-medium text-landing-text-muted">
13+
Secure API key management for TypeScript
14+
</p>
15+
<p className="text-xl mb-16 max-w-3xl mx-auto leading-relaxed text-landing-text-subtle">
16+
Cryptographic hashing, expiration, scopes, and pluggable storage adapters.
17+
Built for production with zero configuration required.
18+
</p>
19+
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
20+
<Link
21+
href="/docs"
22+
className="px-10 py-4 font-medium text-lg transition-opacity hover:opacity-90 bg-landing-btn-primary-bg text-landing-btn-primary-text border border-landing-btn-border"
23+
>
24+
Get Started
25+
</Link>
26+
<a
27+
href="https://github.com/izadoesdev/keypal"
28+
target="_blank"
29+
rel="noopener noreferrer"
30+
className="px-10 py-4 font-medium text-lg transition-opacity hover:opacity-80 bg-landing-btn-secondary-bg text-landing-btn-secondary-text border border-landing-btn-border"
31+
>
32+
GitHub
33+
</a>
34+
</div>
35+
</div>
36+
</section>
37+
38+
{/* Installation Section */}
39+
<section className="py-24 px-4 border-t border-landing-border">
40+
<div className="max-w-4xl mx-auto">
41+
<h2 className="text-4xl font-semibold text-center mb-4 text-landing-text">
42+
Installation
43+
</h2>
44+
<p className="text-lg text-center mb-12 max-w-2xl mx-auto text-landing-text-muted">
45+
Install keypal with your preferred package manager
46+
</p>
47+
<div className="p-6 overflow-x-auto rounded-lg bg-landing-code-bg text-landing-code-text">
48+
<pre className="text-base leading-relaxed">
49+
<code>{`npm install keypal
50+
# or
51+
bun add keypal
52+
# or
53+
pnpm add keypal`}</code>
54+
</pre>
55+
</div>
56+
</div>
57+
</section>
58+
59+
{/* Features Section */}
60+
<section className="py-24 px-4 bg-landing-bg-muted border-t border-landing-border">
61+
<div className="max-w-6xl mx-auto">
62+
<div className="text-center mb-16">
63+
<h2 className="text-4xl font-semibold mb-4 text-landing-text">
64+
Features
65+
</h2>
66+
<p className="text-lg max-w-2xl mx-auto text-landing-text-muted">
67+
Everything you need for secure API key management
68+
</p>
69+
</div>
70+
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
71+
{[
72+
{
73+
title: 'Secure by Default',
74+
description: 'SHA-256/SHA-512 hashing with optional salt and timing-safe comparison'
75+
},
76+
{
77+
title: 'Smart Detection',
78+
description: 'Automatically extracts keys from Authorization headers or custom headers'
79+
},
80+
{
81+
title: 'Flexible Storage',
82+
description: 'Memory, Redis, Drizzle, Prisma, Kysely, and Convex adapters included'
83+
},
84+
{
85+
title: 'Scope-based Permissions',
86+
description: 'Fine-grained access control with resource-specific scopes'
87+
},
88+
{
89+
title: 'Built-in Caching',
90+
description: 'Optional in-memory or Redis caching for validated keys'
91+
},
92+
{
93+
title: 'TypeScript',
94+
description: 'Full type safety with zero configuration required'
95+
}
96+
].map((feature) => (
97+
<div
98+
key={feature.title}
99+
className="p-6 rounded-lg bg-landing-bg border border-landing-border"
100+
>
101+
<h3 className="text-xl font-semibold mb-3 text-landing-text">
102+
{feature.title}
103+
</h3>
104+
<p className="text-base leading-relaxed text-landing-text-muted">
105+
{feature.description}
106+
</p>
107+
</div>
108+
))}
109+
</div>
110+
</div>
111+
</section>
112+
113+
{/* Code Example Section */}
114+
<section className="py-24 px-4 border-t border-landing-border">
115+
<div className="max-w-5xl mx-auto">
116+
<div className="text-center mb-16">
117+
<h2 className="text-4xl font-semibold mb-4 text-landing-text">
118+
Quick Start
119+
</h2>
120+
<p className="text-lg max-w-2xl mx-auto text-landing-text-muted">
121+
Get up and running in minutes with a simple example
122+
</p>
123+
</div>
124+
<div className="p-8 overflow-x-auto rounded-lg bg-landing-code-bg text-landing-code-text">
125+
<pre className="text-sm leading-relaxed">
126+
<code>{`import { createKeys } from 'keypal'
127+
128+
const keys = createKeys({
129+
prefix: 'sk_',
130+
cache: true,
131+
})
132+
133+
// Create a key
134+
const { key, record } = await keys.create({
135+
ownerId: 'user_123',
136+
scopes: ['read', 'write'],
137+
})
138+
139+
// Verify from headers
140+
const result = await keys.verify(request.headers)
141+
if (result.valid) {
142+
console.log('Authenticated:', result.record.metadata.ownerId)
143+
}`}</code>
144+
</pre>
145+
</div>
146+
</div>
147+
</section>
148+
149+
{/* Storage Adapters Section */}
150+
<section className="py-24 px-4 bg-landing-bg-muted border-t border-landing-border">
151+
<div className="max-w-6xl mx-auto">
152+
<div className="text-center mb-16">
153+
<h2 className="text-4xl font-semibold mb-4 text-landing-text">
154+
Storage Adapters
155+
</h2>
156+
<p className="text-lg max-w-2xl mx-auto text-landing-text-muted">
157+
Choose the storage backend that fits your infrastructure
158+
</p>
159+
</div>
160+
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
161+
{[
162+
{ title: 'Memory', description: 'In-memory storage for development' },
163+
{ title: 'Redis', description: 'Distributed storage for production' },
164+
{ title: 'Drizzle', description: 'PostgreSQL, MySQL, SQLite' },
165+
{ title: 'Prisma', description: 'Works with any Prisma database' },
166+
{ title: 'Kysely', description: 'Type-safe SQL query builder' },
167+
{ title: 'Convex', description: 'Real-time backend storage' },
168+
{ title: 'Custom', description: 'Implement your own adapter' },
169+
{ title: 'More', description: 'See all adapters in the docs' }
170+
].map((adapter) => (
171+
<div
172+
key={adapter.title}
173+
className="p-4 rounded-lg bg-landing-bg border border-landing-border"
174+
>
175+
<h3 className="text-lg font-semibold mb-2 text-landing-text">
176+
{adapter.title}
177+
</h3>
178+
<p className="text-landing-text-muted">
179+
{adapter.description}
180+
</p>
181+
</div>
182+
))}
183+
</div>
184+
</div>
185+
</section>
186+
187+
{/* CTA Section */}
188+
<section className="py-32 px-4 border-t border-landing-border">
189+
<div className="max-w-3xl mx-auto text-center">
190+
<h2 className="text-5xl font-semibold mb-6 text-landing-text">
191+
Ready to get started?
192+
</h2>
193+
<p className="text-xl mb-12 leading-relaxed text-landing-text-muted">
194+
Install keypal and start managing API keys in minutes.
195+
Full documentation available with examples and guides.
196+
</p>
197+
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
198+
<Link
199+
href="/docs"
200+
className="px-10 py-4 font-medium text-lg transition-opacity hover:opacity-90 bg-landing-btn-primary-bg text-landing-btn-primary-text border border-landing-btn-border"
201+
>
202+
View Documentation
203+
</Link>
204+
<a
205+
href="https://github.com/izadoesdev/keypal"
206+
target="_blank"
207+
rel="noopener noreferrer"
208+
className="px-10 py-4 font-medium text-lg transition-opacity hover:opacity-80 bg-landing-btn-secondary-bg text-landing-btn-secondary-text border border-landing-btn-border"
209+
>
210+
View on GitHub
211+
</a>
212+
</div>
213+
</div>
214+
</section>
215+
</div>
216+
);
217+
}

apps/docs/app/api/search/route.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { source } from '@/lib/source';
2+
import { createFromSource } from 'fumadocs-core/search/server';
3+
4+
export const { GET } = createFromSource(source, {
5+
// https://docs.orama.com/docs/orama-js/supported-languages
6+
language: 'english',
7+
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { getPageImage, source } from '@/lib/source';
2+
import {
3+
DocsBody,
4+
DocsDescription,
5+
DocsPage,
6+
DocsTitle,
7+
} from 'fumadocs-ui/page';
8+
import { notFound } from 'next/navigation';
9+
import { getMDXComponents } from '@/mdx-components';
10+
import type { Metadata } from 'next';
11+
import { createRelativeLink } from 'fumadocs-ui/mdx';
12+
13+
export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
14+
const params = await props.params;
15+
const page = source.getPage(params.slug);
16+
if (!page) notFound();
17+
18+
const MDX = page.data.body;
19+
20+
return (
21+
<DocsPage toc={page.data.toc} full={page.data.full}>
22+
<DocsTitle>{page.data.title}</DocsTitle>
23+
<DocsDescription>{page.data.description}</DocsDescription>
24+
<DocsBody>
25+
<MDX
26+
components={getMDXComponents({
27+
// this allows you to link to other pages with relative file paths
28+
a: createRelativeLink(source, page),
29+
})}
30+
/>
31+
</DocsBody>
32+
</DocsPage>
33+
);
34+
}
35+
36+
export async function generateStaticParams() {
37+
return source.generateParams();
38+
}
39+
40+
export async function generateMetadata(
41+
props: PageProps<'/docs/[[...slug]]'>,
42+
): Promise<Metadata> {
43+
const params = await props.params;
44+
const page = source.getPage(params.slug);
45+
if (!page) notFound();
46+
47+
return {
48+
title: page.data.title,
49+
description: page.data.description,
50+
openGraph: {
51+
images: getPageImage(page).url,
52+
},
53+
};
54+
}

apps/docs/app/docs/layout.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { source } from '@/lib/source';
2+
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
3+
import { baseOptions } from '@/lib/layout.shared';
4+
5+
export default function Layout({ children }: LayoutProps<'/docs'>) {
6+
return (
7+
<DocsLayout tree={source.pageTree} {...baseOptions()}>
8+
{children}
9+
</DocsLayout>
10+
);
11+
}

0 commit comments

Comments
 (0)