Skip to content

Commit

Permalink
feat: engineering docs styling
Browse files Browse the repository at this point in the history
  • Loading branch information
chronark committed Nov 27, 2024
1 parent fdcc5e2 commit f147d4a
Show file tree
Hide file tree
Showing 22 changed files with 782 additions and 52 deletions.
24 changes: 23 additions & 1 deletion apps/engineering/app/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { source } from "@/app/source";
import { getGithubLastEdit } from "fumadocs-core/server";
import defaultMdxComponents from "fumadocs-ui/mdx";
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/page";
import type { Metadata } from "next";

import { notFound } from "next/navigation";

export default async function Page({
Expand All @@ -14,10 +16,30 @@ export default async function Page({
notFound();
}

const lastUpdate = await getGithubLastEdit({
owner: "unkeyed",
repo: "unkey",
path: `apps/engineering/content/docs/${page.file.path}`,
});

const MDX = page.data.body;

return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsPage
toc={page.data.toc}
tableOfContent={{
style: "clerk",
single: true,
}}
full={page.data.full}
lastUpdate={lastUpdate ?? undefined}
editOnGithub={{
owner: "unkeyed",
repo: "unkey",
sha: "main",
path: `apps/engineering/content/docs/${page.file.path}`,
}}
>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
Expand Down
40 changes: 38 additions & 2 deletions apps/engineering/app/docs/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,48 @@
import { source } from "@/app/source";
import { RootToggle } from "fumadocs-ui/components/layout/root-toggle";
import { DocsLayout } from "fumadocs-ui/layouts/docs";
import { Code, Component, Handshake, Terminal } from "lucide-react";

import type { ReactNode } from "react";
import { baseOptions } from "../layout.config";

export default function Layout({ children }: { children: ReactNode }) {
return (
<DocsLayout tree={source.pageTree} {...baseOptions}>
<DocsLayout
tree={source.pageTree}
{...baseOptions}
sidebar={{
tabs: [
{
title: "Contributing",
description: "Create your first PR",
url: "/docs/contributing",
icon: <Code className="size-4 text-blue-600 dark:text-blue-400" />,
},
/*
{
title: "Company",
description: "How we work",
url: "/docs/company",
icon: <Handshake className="size-4 text-amber-600 dark:text-amber-400" />,
},
*/
{
title: "API Design",
description: "Look and feel",
url: "/docs/api-design",
icon: <Terminal className="size-4 text-emerald-600 dark:text-emerald-400" />,
},
{
title: "Architecture",
description: "How does Unkey work",
url: "/docs/architecture",
icon: <Component className="size-4 text-purple-600 dark:text-purple-400" />,
},
]
}}
>
{children}
</DocsLayout>
</DocsLayout >
);
}
12 changes: 12 additions & 0 deletions apps/engineering/app/layout.config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,23 @@ export const baseOptions: HomeLayoutProps = {
url: "/docs/contributing",
active: "nested-url",
},
/*
{
text: "Company",
url: "/docs/company",
active: "nested-url",
},
*/
{
text: "Architecture",
url: "/docs/architecture",
active: "nested-url",
},
{
text: "RFCs",
url: "/rfcs",
active: "nested-url",
},
{
text: "GitHub",
url: "https://github.com/unkeyed/unkey",
Expand Down
11 changes: 11 additions & 0 deletions apps/engineering/app/rfcs/[[...slug]]/local-date.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use client";

type Props = {
date: Date | string;
};

export const LocalDate: React.FC<Props> = (props) => {
const date = typeof props.date === "string" ? new Date(props.date) : props.date;

return <span suppressHydrationWarning>{date.toLocaleDateString()}</span>;
};
76 changes: 76 additions & 0 deletions apps/engineering/app/rfcs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { rfcSource } from "@/app/source";
import { Card } from "fumadocs-ui/components/card";
import defaultMdxComponents from "fumadocs-ui/mdx";
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/page";
import type { Metadata } from "next";
import { LocalDate } from "./local-date";

import { notFound } from "next/navigation";

export default async function Page({
params,
}: {
params: { slug?: string[] };
}) {

const page = rfcSource.getPage(params.slug);

if (!page) {
notFound();
}

if (page.slugs.length === 0) {
return (
<div className="min-h-screen border text-center -mt-16 pt-16 flex items-center w-screen justify-center ">
<div>
<h1 className="text-7xl md:text-8xl font-bold leading-none uppercase tracking-tight">
RFCS

</h1>
<p className="text-xl mt-8 font-light ">Check the sidebar</p>
</div>
</div>

)
}

const MDX = page.data.body;

return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsTitle>{page.data.title}</DocsTitle>

<Card title="">
<div className="grid grid-cols-2 font-mono">
<span>ID</span>
<span>{page.data.title.split(" ").at(0)}</span>
<span>{page.data.authors.length > 1 ? "Authors" : "Author"}</span>
<span>{page.data.authors.join(", ")}</span>
<span>Date</span>
<LocalDate date={new Date(page.data.date)} />
</div>
</Card>
<DocsDescription>{page.data.description}</DocsDescription>

<DocsBody >
<MDX components={{ ...defaultMdxComponents }} />
</DocsBody>
</DocsPage>
);
}

export async function generateStaticParams() {
return rfcSource.generateParams();
}

export function generateMetadata({ params }: { params: { slug?: string[] } }) {
const page = rfcSource.getPage(params.slug);
if (!page) {
notFound();
}

return {
title: page.data.title,
description: page.data.description,
} satisfies Metadata;
}
12 changes: 12 additions & 0 deletions apps/engineering/app/rfcs/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { rfcSource } from "@/app/source";
import { DocsLayout } from "fumadocs-ui/layouts/notebook";
import type { ReactNode } from "react";
import { baseOptions } from "../layout.config";

export default function Layout({ children }: { children: ReactNode }) {
return (
<DocsLayout tree={rfcSource.pageTree} {...baseOptions}>
{children}
</DocsLayout>
);
}
7 changes: 6 additions & 1 deletion apps/engineering/app/source.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { docs, meta } from "@/.source";
import { docs, meta, rfcs } from "@/.source";
import { loader } from "fumadocs-core/source";
import { createMDXSource } from "fumadocs-mdx";

export const source = loader({
baseUrl: "/docs",
source: createMDXSource(docs, meta),
});

export const rfcSource = loader({
baseUrl: "/rfcs",
source: createMDXSource(rfcs, []),
});
2 changes: 1 addition & 1 deletion apps/engineering/content/docs/api-design/meta.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"title": "API Design",
"description": "Intuitive and helpful",
"icon": "Terminal2",
"icon": "Code",
"root": true,
"pages": ["rpc", "errors"]
}
2 changes: 1 addition & 1 deletion apps/engineering/content/docs/architecture/meta.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"title": "Architecture",
"description": "How does Unkey work",
"icon": "Building2",
"icon": "Pencil",
"root": true,
"pages": ["index", "---Services---", "vault", "clickhouse", "clickhouse-proxy"]
}
13 changes: 0 additions & 13 deletions apps/engineering/content/docs/index.mdx

This file was deleted.

2 changes: 1 addition & 1 deletion apps/engineering/content/docs/meta.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"pages": ["contributing", "company", "api-design", "architecture"]
"pages": ["contributing", "api-design", "company", "architecture"]
}
86 changes: 86 additions & 0 deletions apps/engineering/content/rfcs/0001-rbac.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
title: 0001 RBAC
authors:
- Andreas Thomas
date: 2023-12-12
---


To reduce the scope and time to implementation, we will be reducing our initial permission model to RBAC instead of ReBAC. This has fewer moving parts and can be implemented with just 1 new table.

There are many ways to store this data. Initially I had a 2 table setup, one for roles and one for an M:N relation between roles and keys, but there are some operational issues with that, mainly around planetscale’s foreignkeys and the lack of an easy “upsert” method. This requires us to query all roles of a workspace, then figuring out which ones are missing and creating them. For every key creation… That’s not amazing.

Here’s a much simpler proposal using a single table:

### Table Schema

- `id`

unique id for this row

- `workspaceId`

Every role is scoped to a workspace, no role sharing between tenants

- `keyId`

The key holding this role

- `role: string`

the actual name of a role, ie: `finance` (or more elaborate, see below)
This is completely up to the user, the only limitation is a length ≤ 512chars

for our own roles, we’ll likely do some schema for roles, like `api::{id}::create_key`


This single table design is the simplest form of doing roles. By adding indices for these queries, it should scale far enough:

- roles by key
- roles by workspace
- keys by role

### Unkey internal role schema

`*` denotes either an id or a wildcard

```tsx
root_key::*::read_root_key
root_key::*::create_root_key // a root key MUST NOT be allowed to create another key with more permissions than itself
root_key::*::delete_root_key
root_key::*::update_root_key
api::*::create_api
api::*::delete_api // either wildcard or a specific id -> api::api_123::delete_api
api::*::read_api
api::*::update_api
api::*::read_key
api::*::create_key
api::*::update_key
api::*::delete_key
```

Some of these internal roles (`api::*::create_api`) seem overly complicated, because the wildcard will always be present, since it’s impossible to write this role in advance without knowing the api_id that will get generated later, but by sticking with this schema, we stay consistent and can build our types and tooling more easily.

We could go deeper like `api::*::keys::*::read_key` but I’m not convinced anyone needs this and it just adds complexity for now. It’s trivial to add more roles later, let’s wait it out.

### Examples

1. A key should be allowed to create new apis, modify them and be able to perform all actions on keys.

```tsx
api::*::create_api
api::*::update_api
api::*::read_key
api::*::create_key
api::*::update_key
api::*::delete_key
```

2. Update access to one api and its keys, read access to all apis and their keys

```tsx
api::api_123::update
api::api_123::update_key
api::*::read_api
api::*::read_key
```
Loading

0 comments on commit f147d4a

Please sign in to comment.