feat(create-turbo): apply official-starter transform
9
apps/docs/.eslintrc.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/** @type {import("eslint").Linter.Config} */
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["@repo/eslint-config/next.js"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
project: true,
|
||||
},
|
||||
};
|
||||
36
apps/docs/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.yarn/install-state.gz
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# env files (can opt-in for commiting if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
3
apps/docs/.vscode/extensions.json
vendored
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"recommendations": ["unifiedjs.vscode-mdx", "xyc.vscode-mdx-preview"]
|
||||
}
|
||||
36
apps/docs/README.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
openapi: get /api/v1/spells/get-all/
|
||||
---
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
openapi: get /api/v1/spells/get/
|
||||
---
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
openapi: get /api/v1/spells/search/
|
||||
---
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
openapi: post /api/v1/users/create
|
||||
---
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
openapi: get /api/v1/users/get-all/
|
||||
---
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
openapi: get /api/v1/users/get/
|
||||
---
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
openapi: get /api/v1/users/search/
|
||||
---
|
||||
BIN
apps/docs/app/favicon.ico
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
apps/docs/app/fonts/GeistMonoVF.woff
Normal file
BIN
apps/docs/app/fonts/GeistVF.woff
Normal file
39
apps/docs/app/globals.css
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
max-width: 100vw;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
31
apps/docs/app/layout.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import type { Metadata } from "next";
|
||||
import localFont from "next/font/local";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = localFont({
|
||||
src: "./fonts/GeistVF.woff",
|
||||
variable: "--font-geist-sans",
|
||||
});
|
||||
const geistMono = localFont({
|
||||
src: "./fonts/GeistMonoVF.woff",
|
||||
variable: "--font-geist-mono",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={`${geistSans.variable} ${geistMono.variable}`}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
188
apps/docs/app/page.module.css
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
.page {
|
||||
--gray-rgb: 0, 0, 0;
|
||||
--gray-alpha-200: rgba(var(--gray-rgb), 0.08);
|
||||
--gray-alpha-100: rgba(var(--gray-rgb), 0.05);
|
||||
|
||||
--button-primary-hover: #383838;
|
||||
--button-secondary-hover: #f2f2f2;
|
||||
|
||||
display: grid;
|
||||
grid-template-rows: 20px 1fr 20px;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
min-height: 100svh;
|
||||
padding: 80px;
|
||||
gap: 64px;
|
||||
font-synthesis: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.page {
|
||||
--gray-rgb: 255, 255, 255;
|
||||
--gray-alpha-200: rgba(var(--gray-rgb), 0.145);
|
||||
--gray-alpha-100: rgba(var(--gray-rgb), 0.06);
|
||||
|
||||
--button-primary-hover: #ccc;
|
||||
--button-secondary-hover: #1a1a1a;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
grid-row-start: 2;
|
||||
}
|
||||
|
||||
.main ol {
|
||||
font-family: var(--font-geist-mono);
|
||||
padding-left: 0;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.01em;
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
.main li:not(:last-of-type) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.main code {
|
||||
font-family: inherit;
|
||||
background: var(--gray-alpha-100);
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.ctas {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.ctas a {
|
||||
appearance: none;
|
||||
border-radius: 128px;
|
||||
height: 48px;
|
||||
padding: 0 20px;
|
||||
border: none;
|
||||
font-family: var(--font-geist-sans);
|
||||
border: 1px solid transparent;
|
||||
transition: background 0.2s, color 0.2s, border-color 0.2s;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
a.primary {
|
||||
background: var(--foreground);
|
||||
color: var(--background);
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
a.secondary {
|
||||
border-color: var(--gray-alpha-200);
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
appearance: none;
|
||||
border-radius: 128px;
|
||||
height: 48px;
|
||||
padding: 0 20px;
|
||||
border: none;
|
||||
font-family: var(--font-geist-sans);
|
||||
border: 1px solid transparent;
|
||||
transition: background 0.2s, color 0.2s, border-color 0.2s;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
background: transparent;
|
||||
border-color: var(--gray-alpha-200);
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
font-family: var(--font-geist-sans);
|
||||
grid-row-start: 3;
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.footer img {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Enable hover only on non-touch devices */
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
a.primary:hover {
|
||||
background: var(--button-primary-hover);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
a.secondary:hover {
|
||||
background: var(--button-secondary-hover);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.footer a:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.page {
|
||||
padding: 32px;
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
.main {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.main ol {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ctas {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ctas a {
|
||||
font-size: 14px;
|
||||
height: 40px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
a.secondary {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.logo {
|
||||
filter: invert();
|
||||
}
|
||||
}
|
||||
99
apps/docs/app/page.tsx
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import Image from "next/image";
|
||||
import { Button } from "@repo/ui/button";
|
||||
import styles from "./page.module.css";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<main className={styles.main}>
|
||||
<Image
|
||||
className={styles.logo}
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol>
|
||||
<li>
|
||||
Get started by editing <code>app/page.tsx</code>
|
||||
</li>
|
||||
<li>Save and see your changes instantly.</li>
|
||||
</ol>
|
||||
|
||||
<div className={styles.ctas}>
|
||||
<a
|
||||
className={styles.primary}
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className={styles.logo}
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
</a>
|
||||
<a
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.secondary}
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
<Button appName="docs" className={styles.secondary}>
|
||||
Open alert
|
||||
</Button>
|
||||
</main>
|
||||
<footer className={styles.footer}>
|
||||
<a
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/file-text.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
---
|
||||
title: Documentation config
|
||||
"og:title": Mintlify setup and configuration
|
||||
description: Mintlify setup and configuration
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
The documentation site is built entirely using Mintlify. Mintlify docs are rendered from MDX files and configurations. Mintlify works with a Github integration to
|
||||
constantly build an independent docs site based off your latest published repository.
|
||||
|
||||
Documentation can be edited in raw MDX, or directly through the Mintlify dashboard (which stays in sync with your local Github repo).
|
||||
|
||||
Mintlify is fast to setup and provides tonnes of features out-the-box without the need to pay for a subscription for the basic services.
|
||||
|
||||
You can run the **docs site only** by running the `pnpm run dev` command from a terminal within the `docs` directory.
|
||||
|
||||
## Docs structure
|
||||
|
||||
The docs root directory is structured as follows:
|
||||
|
||||
```
|
||||
docs
|
||||
├── _images
|
||||
├── api
|
||||
├── configuration
|
||||
├── deployment
|
||||
├── logos
|
||||
├── *.mdx
|
||||
├── mint.json
|
||||
└── package.json
|
||||
```
|
||||
|
||||
The docs directory is relatively simple and is made mostly of MDX files. Some notable files are:
|
||||
|
||||
| Item | Description |
|
||||
| ------------ | ----------------------------------------------------------- |
|
||||
| api | Automatically generated files (except for Introduction.mdx) |
|
||||
| mint.json | Mintlify configuration object |
|
||||
| package.json | Scripts needed in the monorepo/for generating the API files |
|
||||
|
||||
## Creating API endpoint documentation
|
||||
|
||||
We can leverage FastAPI's baked in OpenAPI support to automatically generate API documentation.
|
||||
|
||||
Using Mintlify's `@mintlify/scraping` package, we can scrape our FastAPI's OpenAPI schema to generate an interactive API documentation site.
|
||||
|
||||
<Tip>
|
||||
For a more detailed description, read the [official
|
||||
documentation](https://mintlify.com/docs/api-playground/openapi/setup#create-mdx-files-for-openapi-endpoints)
|
||||
</Tip>
|
||||
|
||||
### Generating OpenAPI schema
|
||||
|
||||
#### Step 1: Generate MDX
|
||||
|
||||
Run the `generate-api` scripts within the `package.json` to generate the required MDX
|
||||
files. There are two scripts here, one configured to use the production
|
||||
`OpenAPI.json` and the second configured to use the development version.\
|
||||
\
|
||||
It is typically preferred to use the production version once any changes to
|
||||
your API have been made and published.
|
||||
|
||||
#### Step 2: Update mint.json
|
||||
|
||||
<Steps>
|
||||
<Step title="Add new MDX files to navigation">
|
||||
Upon generating the relevant API information, `@mintlify/scraping` helpfully outputs the suggested navigation to the terminal:
|
||||
|
||||
```bash
|
||||
navigation object suggestion:
|
||||
[
|
||||
{
|
||||
"group": "users",
|
||||
"pages": [
|
||||
"api/users/get-user",
|
||||
"api/users/get-all-users",
|
||||
"api/users/search-users",
|
||||
"api/users/create-user"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "spells",
|
||||
"pages": [
|
||||
"api/spells/get-spell",
|
||||
"api/spells/get-all-spells",
|
||||
"api/spells/search-spells"
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
For sake of simplicity, copy this navigation object suggestion and add it your `mint.json` under the `navigation` object:
|
||||
|
||||
```json
|
||||
"navigation": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"pages": ["documentation/introduction", "documentation/local-development"]
|
||||
},
|
||||
{
|
||||
"group": "Configuration",
|
||||
"pages": [
|
||||
"documentation/configuration/turbo",
|
||||
"documentation/configuration/fastapi",
|
||||
"documentation/configuration/nextjs",
|
||||
"documentation/configuration/docs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Deployment",
|
||||
"pages": [
|
||||
"documentation/deployment/vercel",
|
||||
"documentation/deployment/deployment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "API Reference",
|
||||
"pages": ["api/introduction"]
|
||||
},
|
||||
{
|
||||
"group": "Users",
|
||||
"pages": [
|
||||
"api/users/get-user",
|
||||
"api/users/get-all-users",
|
||||
"api/users/search-users",
|
||||
"api/users/create-user"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Spells",
|
||||
"pages": [
|
||||
"api/spells/get-spell",
|
||||
"api/spells/get-all-spells",
|
||||
"api/spells/search-spells"
|
||||
]
|
||||
}
|
||||
],
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="Add OpenAPI endpoint to `mint.json`">
|
||||
Add the `openapi` key to your `mint.json`. This, preferably, can be linked to your published OpenAPI schema:
|
||||
|
||||
```json
|
||||
{
|
||||
...
|
||||
"openapi":"https://next-fast-turbo-api.vercel.app/openapi.json"
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
This enables the generated MDX files to describe and interact with your API.
|
||||
|
||||
<Warning>
|
||||
This has been hit and miss in the past for me. If doing this does not auto-populate your API reference with the documentation, the solution is to manually\
|
||||
create an `openapi.json` file in the `docs` root. Manually copy and paste/save your `openapi.json` into this file.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
|
@ -1,377 +0,0 @@
|
|||
---
|
||||
title: FastAPI config
|
||||
"og:title": "FastAPI configuration and setup"
|
||||
description: FastAPI configuration and setup
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
The backend uses [FastAPI](https://fastapi.tiangolo.com/). FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.8+ based on standard Python type hints
|
||||
|
||||
You can run the **api only** by running the `pnpm run dev` command from a terminal within the `api` directory. Alternatively, you can directly run the `run.py` file.
|
||||
|
||||
## API structure
|
||||
|
||||
The API root directory is structured as follows:
|
||||
|
||||
```
|
||||
api
|
||||
├── src
|
||||
│ ├── api
|
||||
│ ├── crud
|
||||
│ ├── schemas
|
||||
│ ├── __init__.py
|
||||
│ ├── config.py
|
||||
│ └── main.py
|
||||
├── tests
|
||||
├── .env
|
||||
├── .env.example
|
||||
├── harry-potter-db-seed-spells.csv
|
||||
├── harry-potter-db-seed-users.csv
|
||||
├── package.json
|
||||
├── poetry.lock
|
||||
├── pyproject.toml
|
||||
├── requirements.txt
|
||||
├── run.py
|
||||
└── vercel.json
|
||||
```
|
||||
|
||||
### Src directory
|
||||
|
||||
The `src` directory is where all the application code sits. Below briefly explains each folder/file
|
||||
|
||||
| Item | Description |
|
||||
| --------- | ----------------------------------------------- |
|
||||
| api | The API endpoints for the application |
|
||||
| crud | The CRUD operations used within the application |
|
||||
| schemas | The schemas used within the application |
|
||||
| config.py | Main application configuration |
|
||||
| main.py | Application |
|
||||
|
||||
### Root directory
|
||||
|
||||
The root directory contains the typical files one would expect to see in a Python project. The below files are worth describing:
|
||||
|
||||
| Item | Description |
|
||||
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| package.json | Not typical in a Python programme; used due to the nature of the monorepo. Running `pnpm run dev` at the project root will execute the `dev` script in this `package.json` |
|
||||
| vercel.json | The configuration for deploying the FastAPI aspect of the application to Vercel. |
|
||||
| \*.csv | Simple seed data for the database, sourced from the [Harry Potter API](https://hp-api.onrender.com/) |
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Python 3.9
|
||||
|
||||
Because the project is being deployed on Vercel, Python version `3.9` must be used as this is the latest supported version of Python. For more information, read the [official documentation](https://vercel.com/docs/functions/runtimes/python).
|
||||
|
||||
If deploying to somewhere other than Vercel, check which version of Python you may use and adjust the project according to your needs.
|
||||
|
||||
### Supabase
|
||||
|
||||
The project uses [Supabase](https://supabase.com/) as a database. Since Planetscale [removed their free Hobby tier](https://planetscale.com/blog/planetscale-forever),
|
||||
Supabase has seemed like a good alternative to use. You do not have to use Supabase with the backend, but the project is written from the perspective of using it.
|
||||
|
||||
<Tip>
|
||||
If you are using Supabase,
|
||||
[`supabase-py-async`](https://pypi.org/project/supabase-py-async/) is already
|
||||
included as a project dependency within the `pyproject.toml`. If you are not
|
||||
using Supabase, this can be removed.
|
||||
</Tip>
|
||||
|
||||
### Poetry
|
||||
|
||||
[Poetry](https://python-poetry.org/) is used to manage the virtual environment and project dependencies.
|
||||
A `requirements.txt` has been generated to enable the installation of Python packages via the `pip install` command.
|
||||
|
||||
If you do not use Poetry, you can remove the `poetry.lock` and `pyproject.toml` files.
|
||||
|
||||
<Tip>You will need a `requirements.txt` when deploying. Vercel also accepts a `Pipenv` file if you use Pipenv, otherwise, you'll need the `requirements.txt` for the `api` to build correctly</Tip>
|
||||
|
||||
## Adding your own endpoints
|
||||
|
||||
Given the project structure, there are three areas that you must be aware of when adding your own endpoints with new models. The main areas are:
|
||||
|
||||
- Schemas
|
||||
- CRUD
|
||||
- API
|
||||
|
||||
### Example: Creation of Spells endpoints
|
||||
|
||||
#### Step 1: Create a schema
|
||||
|
||||
<Steps>
|
||||
<Step title="Add a schema">
|
||||
Add a new schema to the `schemas` directory.
|
||||
|
||||
```python src/schemas/spell.py
|
||||
from typing import ClassVar, Sequence
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Spell(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
description: str
|
||||
table_name: ClassVar[str] = "spells"
|
||||
|
||||
|
||||
class SpellCreate(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
description: str
|
||||
|
||||
|
||||
class SpellUpdate(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
description: str
|
||||
|
||||
|
||||
class SpellSearchResults(BaseModel):
|
||||
results: Sequence[Spell]
|
||||
|
||||
```
|
||||
|
||||
<Info>The `table_name` value needs to be included in the base `Spell` class. This **must** be the same as the name of the table created in Supabase. It is used in the CRUD operations to identify the table to work with.</Info>
|
||||
|
||||
| Item | Description |
|
||||
|--------------------|---------------------------------------------------------------------------------------------|
|
||||
| `Spell` | The base class describing the columns that will be in the Supabase table |
|
||||
| SpellCreate | The class for creating a new `Spell` |
|
||||
| SpellUpdate | The class for updating an existing `Spell` |
|
||||
| SpellSearchResults | Describes how data will be returned in the API response. It will be a `Sequence` of `Spell` |
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Add to __init__.py">
|
||||
To easily import the new `Spell` classes throughout our application, they need to be added to the `src/schemas/__init__.py` file.
|
||||
|
||||
```python src/schemas/__init__.py
|
||||
from .user import User, UserCreate, UserSearchResults, UserUpdate
|
||||
from .spell import Spell, SpellCreate, SpellSearchResults, SpellUpdate
|
||||
```
|
||||
|
||||
This allows us to import from `src/schemas` like so:
|
||||
|
||||
```python
|
||||
from src.schemas import Spell, SpellCreate, SpellSearchResults, SpellUpdate
|
||||
```
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
#### Step 2: Setup CRUD operations
|
||||
|
||||
Specific CRUD operations can be created for each endpoint. However, generic CRUD operations in `src/crud/base.py` can also be used without modification.
|
||||
|
||||
<Steps>
|
||||
<Step title="Create CRUD file">
|
||||
```python src/crud/crud_spell.py
|
||||
from fastapi import HTTPException
|
||||
from supabase_py_async import AsyncClient
|
||||
|
||||
from src.crud.base import CRUDBase
|
||||
from src.schemas import Spell, SpellCreate, SpellUpdate
|
||||
|
||||
|
||||
class CRUDSpell(CRUDBase[Spell, SpellCreate, SpellUpdate]):
|
||||
async def get(self, db: AsyncClient, *, id: str) -> Spell | None:
|
||||
try:
|
||||
return await super().get(db, id=id)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"{e.code}: Spell not found. {e.details}",
|
||||
)
|
||||
|
||||
async def get_all(self, db: AsyncClient) -> list[Spell]:
|
||||
try:
|
||||
return await super().get_all(db)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"An error occurred while fetching spells. {e}",
|
||||
)
|
||||
|
||||
async def search_all(
|
||||
self, db: AsyncClient, *, field: str, search_value: str, max_results: int
|
||||
) -> list[Spell]:
|
||||
try:
|
||||
return await super().search_all(
|
||||
db, field=field, search_value=search_value, max_results=max_results
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"An error occurred while searching for spells. {e}",
|
||||
)
|
||||
|
||||
|
||||
spell = CRUDSpell(Spell)
|
||||
```
|
||||
The `CRUDSpell` class inherits from the `CRUDBase` class located in `src/crud/base.py`. The `Spell`, `SpellCreate` and `SpellUpdate` classes are passed to the `CRUDBase` class to specify the types of data that will be used in the CRUD operations.
|
||||
|
||||
The operations that will be used in the API endpoints are functions of the `CRUDSpell` class.
|
||||
|
||||
Finally, `spell` is an instance of `CRUDSpell`. We import this instance to use in the API endpoints like so: `spell.get_all(db)` or `spell.get(db, id=id)`.
|
||||
|
||||
<Note>
|
||||
**This part of the project is intended to be read-only**, however, the `SpellCreate` and `SpellUpdate` classes are created in the schema regardless as they are required by the `CRUDBase` model's function arguments. `CRUDBase` can be refactored to have optional parameters, or a read-only version of the `CRUDBase` class can be created. This is beyond the scope of this project.
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Add to __init__.py">
|
||||
Similar to setting up the schemas, the `CRUDSpell` class needs to be added to the `src/crud/__init__.py` file.
|
||||
|
||||
```python src/crud/__init__.py
|
||||
from .crud_user import user
|
||||
from .crud_spell import spell
|
||||
```
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
#### Step 3: Create the API endpoints
|
||||
|
||||
<Steps>
|
||||
<Step title="Create the endpoints">
|
||||
Create the endpoints in the `src/api/api_v1/endpoints/spells.py` file.
|
||||
|
||||
```python src/api/api_v1/endpoints/spells.py
|
||||
from typing import Literal, Optional, Union
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from src.api.deps import SessionDep
|
||||
from src.crud import spell
|
||||
from src.schemas import Spell, SpellSearchResults
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/get/", status_code=200, response_model=Spell)
|
||||
async def get_spell(session: SessionDep, spell_id: str) -> Spell:
|
||||
"""Returns a spell from a spell_id.
|
||||
|
||||
**Returns:**
|
||||
- spell: spell object.
|
||||
"""
|
||||
return await spell.get(session, id=spell_id)
|
||||
|
||||
|
||||
@router.get("/get-all/", status_code=200, response_model=list[Spell])
|
||||
async def get_all_spells(session: SessionDep) -> list[Spell]:
|
||||
"""Returns a list of all spells.
|
||||
|
||||
**Returns:**
|
||||
- list[spell]: List of all spells.
|
||||
"""
|
||||
return await spell.get_all(session)
|
||||
|
||||
|
||||
@router.get("/search/", status_code=200, response_model=SpellSearchResults)
|
||||
async def search_spells(
|
||||
session: SessionDep,
|
||||
search_on: Literal["spells", "description"] = "spell",
|
||||
keyword: Optional[Union[str, int]] = None,
|
||||
max_results: Optional[int] = 10,
|
||||
) -> SpellSearchResults:
|
||||
"""
|
||||
Search for spells based on a keyword and return the top `max_results` spells.
|
||||
|
||||
**Args:**
|
||||
- keyword (str, optional): The keyword to search for. Defaults to None.
|
||||
- max_results (int, optional): The maximum number of search results to return. Defaults to 10.
|
||||
- search_on (str, optional): The field to perform the search on. Defaults to "email".
|
||||
|
||||
**Returns:**
|
||||
- SpellSearchResults: Object containing a list of the top `max_results` spells that match the keyword.
|
||||
"""
|
||||
if not keyword:
|
||||
results = await spell.get_all(session)
|
||||
return SpellSearchResults(results=results)
|
||||
|
||||
results = await spell.search_all(
|
||||
session, field=search_on, search_value=keyword, max_results=max_results
|
||||
)
|
||||
|
||||
if not results:
|
||||
raise HTTPException(
|
||||
status_code=404, detail="No spells found matching the search criteria"
|
||||
)
|
||||
|
||||
return SpellSearchResults(results=results)
|
||||
```
|
||||
|
||||
Here we are calling `spell` object which we setup in step 2.
|
||||
|
||||
The `SessionDep` (located in `src/api/deps.py`) dependency is used to access the database.
|
||||
|
||||
The `response_model` parameter is used to specify the type of data that will be returned from the endpoint. This is used to generate TypeScript types for the frontend.
|
||||
</Step>
|
||||
|
||||
<Step title="Add the endpoint to the router">
|
||||
To include the new endpoints in the API, it must be added to the API router, located in `src/api/api_v1/api.py`.
|
||||
|
||||
```python src/api/api_v1/api.py
|
||||
from fastapi import APIRouter
|
||||
from src.api.api_v1.endpoints import users, spells
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(users.router, prefix="/users", tags=["users"], responses={404: {"description": "Not found"}})
|
||||
api_router.include_router(spells.router, prefix="/spells", tags=["spells"], responses={404: {"description": "Not found"}})
|
||||
```
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
#### Step 4: Create the table in Supabase
|
||||
|
||||
<Steps>
|
||||
<Step title="Create the table">
|
||||
Create a new table in your Supabase dashboard. It is **imperative** that the table name matches the `table_name` value in the `Spell` class in `src/schemas/spell.py`.
|
||||
|
||||
Columns should be added to match the `Spell` class. For example, we have the following `Spell` class:
|
||||
|
||||
```python
|
||||
class Spell(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
description: str
|
||||
table_name: ClassVar[str] = "spells"
|
||||
```
|
||||
|
||||
In this example, the table should be named `spells` (table names are case-sensitive) with the columns `id`, `name`, and `description`. \
|
||||
\
|
||||
For data types, `id` can be automatically assigned - in this example, it is set to a `uuid`. The `name` and `description` fields are strings, therefore their column's type is set to `text`. The `id` column should be the primary key.
|
||||
<Tip>You do not need to add the `table_name` column as this is used in the FastAPI code to identify the table to work with.</Tip>
|
||||
</Step>
|
||||
<Step title="Seed the table with data">
|
||||
The data can be seeded using the Supabase dashboard by uploading the `harry-potter-db-seed-spells.csv` file. For a more detailed explanation, please see the [official documentation](https://supabase.com/docs/guides/database/import-data#option-1-csv-import-via-supabase-dashboard)
|
||||
</Step>
|
||||
<Step title="Configure table security">
|
||||
The table security should be configured to allow the FastAPI application to access the data. By default, Supabase will have [RLS (Row Level Security)](https://supabase.com/docs/guides/auth/row-level-security) enabled.\
|
||||
\
|
||||
If left un-configured, the database will return an empty array. As this is a simple read-only project, I am turning `RLS` off. However, for true CRUD operations, it should be configured to use authentication which is beyond the scope of this project.
|
||||
|
||||
\
|
||||
To disable `RLS`, in the table settings, select `configure RLS` and then `disable RLS`.\
|
||||
</Step>
|
||||
<Step title="Add table connection to .env">
|
||||
Once the table has been created, you must ensure that the `DB_URL` and `DB_API_KEY` parameters are populated in your `.env` file located in the root of the `API` directory.\
|
||||
\
|
||||
These values come from the Supabase dashboard by going to `settings` and then `API`. Copy the Project URL (`DB_URL`) and the Project API Key (`DB_API_KEY`).
|
||||
|
||||
<Info>
|
||||
`DB_USERNAME` and `DB_PASSWORD` should also included in the `.env` as they are configured in the `src/config.py`. If you do not include these you will receive an error from Pydantic.\
|
||||
\
|
||||
Their inclusion is a pre-cursor to authentication, but they are not actually used in this project scaffold.
|
||||
</Info>
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
#### Step 5: Test your new endpoints
|
||||
You can now test your new endpoints using the FastAPI Swagger UI or by making requests to the API.
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
---
|
||||
title: Next.js config
|
||||
"og:title": "Next.js config and setup"
|
||||
"description": "Next.js config and setup"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
The frontend uses the latest version of Next.js and TypeScript; it is configured using the latest [app router](https://nextjs.org/docs/app).
|
||||
|
||||
You can run the **frontend only** by running the `pnpm run dev` command from a terminal within the `web` directory. Alternatively, you can run the entire application by running `pnpm run dev` from the **root directory**.
|
||||
|
||||
## App structure
|
||||
|
||||
The app root directory is structured as follows:
|
||||
|
||||
```
|
||||
web
|
||||
├── app
|
||||
├── components
|
||||
├── lib
|
||||
├── public
|
||||
├── .env
|
||||
├── .env.example
|
||||
├── .eslintrc.js
|
||||
├── components.json
|
||||
├── next.config.js
|
||||
├── package.json
|
||||
├── postcss.config.js
|
||||
├── README.md
|
||||
├── tailwind.config.js
|
||||
├── tsconfig.json
|
||||
└── yarn.lock
|
||||
```
|
||||
|
||||
#### App
|
||||
|
||||
| Item | Description |
|
||||
| ----------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| globals.css | Tailwind global config. Uses a generated theme from [ShadCN UI themes](https://ui.shadcn.com/themes) |
|
||||
| layout.tsx | App layout |
|
||||
| page.tsx | Landing page |
|
||||
|
||||
#### Components
|
||||
|
||||
| Item | Description |
|
||||
| ------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| layouts | The dashboard layout is stored which wraps the application `{children}` in `root/app/layout.tsx` |
|
||||
| theme | The `ThemeProvider` component(s) that wrap the application. |
|
||||
| ui | Where `ShadCN` components are stored upon being generated. Configured by the `components.json` located in the `frontend root` |
|
||||
| \*.tsx | Components used within the application |
|
||||
|
||||
#### Lib
|
||||
| Item | Description |
|
||||
| ----------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| api | The output generated by the `openapi` npm package. Configured in the `package.json` task named `generate-client` |
|
||||
| config | Stores site config. Currently contains the setup for sidebar navigation |
|
||||
| twConfig.ts | Tailwind colour config exported to an accessible Object. Used for assigning theme-aware colours in charts |
|
||||
| utils.ts | ShadCN utils |
|
||||
|
||||
|
||||
|
||||
## TypeScript from FastAPI
|
||||
As FastAPI is based on the OpenAPI specification, you get automatic compatibility with many tools, including the automatic API docs (provided by Swagger UI).
|
||||
|
||||
One particular advantage that is not necessarily obvious is that you can generate clients (sometimes called SDKs ) for your API, for many different programming languages.
|
||||
|
||||
<Note>For a more detailed explanation, see the [official FastAPI documentation](https://fastapi.tiangolo.com/advanced/generate-clients/#generate-a-typescript-client-with-the-preprocessed-openapi)</Note>
|
||||
|
||||
## Why generate TypeScript?
|
||||
Generating a TypeScript client for your FastAPI backend is a great way to ensure that your frontend and backend are always in sync. It is also a way to provide type hinting while writing your frontend code without needing a permanent reference to the API, or re-creating the schema using something like Zod.
|
||||
|
||||
<Frame caption="Type hinting from the generated API client"><img src="/_images/type-hinting.png" /></Frame>
|
||||
|
||||
### How to generate TypeScript
|
||||
<Steps>
|
||||
<Step title="Configure and run generate-client task">
|
||||
There are two generate-client tasks configured in the `package.json`:
|
||||
```JSON
|
||||
"scripts": {
|
||||
...
|
||||
"generate-client": "openapi --input https://next-fast-turbo.vercel.app/openapi.json --output ./lib/api/client --client axios --useOptions --useUnionTypes",
|
||||
"generate-client:dev": "openapi --input http://127.0.0.0:8000/openapi.json --output ./lib/api/client --client axios --useOptions --useUnionTypes"
|
||||
...
|
||||
},
|
||||
```
|
||||
The `generate-client` task is set to run off the production OpenAPI schema.\
|
||||
\
|
||||
The `generate-client:dev` task is set to use the localhost OpenAPI schema. This is useful for development, as it will use the latest schema from the backend.
|
||||
|
||||
Ensure your production API URL is configured, or that your local API URL is correct, and then run the relevant task.
|
||||
</Step>
|
||||
|
||||
<Step title="Update your root layout">
|
||||
|
||||
A key file to be aware of is the `OpenAPI.ts` which is generated in the `lib/api/client/core` directory. This file has the main configuration for the API connection as below:
|
||||
|
||||
```tsx lib/api/client/core/OpenAPI.ts
|
||||
export const OpenAPI: OpenAPIConfig = {
|
||||
BASE: "http://127.0.0.0:8000",
|
||||
VERSION: "0.1.0",
|
||||
WITH_CREDENTIALS: false,
|
||||
CREDENTIALS: "include",
|
||||
TOKEN: undefined,
|
||||
USERNAME: undefined,
|
||||
PASSWORD: undefined,
|
||||
HEADERS: undefined,
|
||||
ENCODE_PATH: undefined,
|
||||
};
|
||||
```
|
||||
|
||||
The problem with this is that we would want to differentiate between which API is being used. When the TypeScript is generated, the given OpenAPI URL is used as the base URL for the API.
|
||||
This is not ideal for production, as you would want to use the production API URL, and vice versa in development.
|
||||
|
||||
You can override this by adding the following to your root `layout.tsx` file:
|
||||
|
||||
```tsx layout.tsx
|
||||
import { OpenAPI } from "@/lib/api/client";
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
OpenAPI.BASE = "https://next-fast-turbo.vercel.app"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Using the generated API
|
||||
Once you have generated your TypeScript interface to your API, you can use it in your frontend code.
|
||||
|
||||
In its simplest form, you can import the generated API and use it as below:
|
||||
|
||||
```tsx
|
||||
import { UsersService } from "@/lib/api/client";
|
||||
|
||||
const response = await UsersService.usersSearchUsers({
|
||||
keyword: "keyword",
|
||||
searchOn: "searchOn",
|
||||
maxResults: "maxResults",
|
||||
});
|
||||
```
|
||||
|
||||
You can see this code being used in the `components/search-users.tsx` file, as part of the form's onSubmit function:
|
||||
|
||||
```tsx
|
||||
const onSubmit = async (data: z.infer<typeof FormSchema>) => {
|
||||
try {
|
||||
const maxResults = data.searchResults ? parseInt(data.searchResults) : 10;
|
||||
const response = await UsersService.usersSearchUsers({
|
||||
keyword: data.keyword,
|
||||
searchOn: data.searchOn,
|
||||
maxResults: maxResults,
|
||||
});
|
||||
setSearchResults(response);
|
||||
setError(null);
|
||||
} catch (error) {
|
||||
setSearchResults({ results: [] });
|
||||
setError(error);
|
||||
}
|
||||
};
|
||||
```
|
||||
This code also imports the `UserSearchResults` type which is used to set the state of the search results:
|
||||
```tsx
|
||||
import { UsersService, UserSearchResults } from "@/lib/api/client";
|
||||
|
||||
const [searchResults, setSearchResults] = React.useState<UserSearchResults>({
|
||||
results: [],
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Important considerations
|
||||
- If you make changes to your API, you will need to re-generate the TypeScript interface.
|
||||
- Re-generating this interface will overwrite the existing files. If you want to modify anything (e.g. `api/client/core/OpenAPI.ts`) and have it persist, do this outside of the generated files. You can see this in action in the `root/app/layout.tsx` file.
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
---
|
||||
title: Turbo config
|
||||
"og:title": "Configuring Turbo for FastAPI, Next.js and Turborepo"
|
||||
description: Configuring Turbo for FastAPI, Next.js and Turborepo.
|
||||
---
|
||||
|
||||
## Introduction
|
||||
This project was first created using the `create-turbo` npm package:
|
||||
|
||||
<CodeGroup>
|
||||
```bash npm
|
||||
npx create-turbo@latest
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn dlx create-turbo@latest
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm dlx create-turbo@latest
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Note>It is recommended that you read through the [official documentation](https://turbo.build/repo/docs/getting-started/create-new) to understand the breakdown of the monorepo.</Note>
|
||||
|
||||
## Changes to the official Turbo starter
|
||||
The official Turbo starter is a great starting point for a monorepo, but it has some configurations I did not need. As I will not be sharing code between the backend and frontend (nor the docs), I have **removed** the `packages/ui` directory.
|
||||
|
||||
If you want to **add a new package** to the project, please reference the [official documentation](https://turbo.build/repo/docs/handbook/sharing-code/internal-packages).
|
||||
|
||||
For quick reference:
|
||||
1. Create a new folder in `packages/<folder>`
|
||||
2. Add a `package.json`, with `name` and `types` pointing at `src/index.ts[x]`
|
||||
3. Add `src/index.ts[x]`, with at least one named export
|
||||
4. Install your packages from `root`
|
||||
|
||||
|
||||
## Turbo.json
|
||||
The currently configured `turbo.json` does not have many changes from the official starter. The only change is the addition of the `globalEnv` key.
|
||||
|
||||
```json
|
||||
{
|
||||
"globalEnv": [
|
||||
"NODE_ENV"
|
||||
],
|
||||
}
|
||||
```
|
||||
Without adding these environment variables to the `globalEnv`, you will receive an eslint error when referencing variables in your `.env`, however, your code will still work.
|
||||
|
||||
<Warning>
|
||||
There are different ways that people solve this. While it's possible to use\
|
||||
`"globalDependencies": ["**/.env"]`, that did not work in this project.\
|
||||
</Warning>
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
---
|
||||
title: Deploying the monorepo
|
||||
description: Deploying your monorepo to Vercel/Mintlify
|
||||
"og:title": Deploying your monorepo to Vercel/Mintlify
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Deploying your monorepo to Vercel is relatively straightforward, but there are a handful of things to be aware of.
|
||||
The frontend deployment is relatively straightforward, but the backend deployment can be a little more complex and need a bit more attention.
|
||||
|
||||
<Note>For a more detailed guide on deploying a monorepo to Vercel, see the [official documentation](https://vercel.com/docs/monorepos/turborepo).</Note>
|
||||
|
||||
## Prerequisites
|
||||
Before continuing, ensure you have:
|
||||
- A [Vercel](https://vercel.com/) account. It is typically easiest to sign up with your GitHub account, as we'll also be deploying directly from the GitHub repository.
|
||||
- Published your repo to GitHub. If you haven't done this yet, you can follow the instructions [here](https://docs.github.com/en/get-started/quickstart/create-a-repo).
|
||||
- Optional. If you're planning to deploy your documentation site to Mintlify, you'll need to have a [Mintlify](https://mintlify.com/) account.
|
||||
|
||||
## Frontend Deployment
|
||||
<Steps>
|
||||
<Step title="Add new project in Vercel">
|
||||
Go to your [Vercel dashboard](https://vercel.com/dashboard) and click the `Add New...` button, and select `Project`.
|
||||
</Step>
|
||||
|
||||
<Step title="Import your monorepo">
|
||||
Select the `Import Git Repository` option and select your repository from the list of repositories.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Project">
|
||||
Vercel will automatically detect that you have a monorepo, and should have correctly populated all the required fields.
|
||||
|
||||
The project root directory should be automatically detected, but if it isn't, you can manually set it to the folder where your frontend is stored. In this project, that is `apps/web`.
|
||||
|
||||
<Frame>
|
||||
<img src="/_images/deploy-web-configure-project.png" />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Deploy">
|
||||
Click the `Deploy` button to deploy your project.
|
||||
</Step>
|
||||
|
||||
<Step title="Add an Ignored Build Step">
|
||||
We don't want to continuously rebuild our frontend every time we push changes to the backend or documentation. To prevent this, we can add an ignored build step.\
|
||||
\
|
||||
From the newly deployed frontend dashboard, navigate to `Settings`, then to `Git`. Scroll down to the `Ignored Build Step` section and set the behaviour to
|
||||
**Only build Turborepo app if there are changes**.
|
||||
|
||||
<Frame>
|
||||
<img src="/_images/deploy-web-ignored-build-step.png" />
|
||||
</Frame>
|
||||
|
||||
<Note>For more information, see the [official documentation](https://vercel.com/docs/projects/overview#ignored-build-step).</Note>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Backend deployment
|
||||
<Steps>
|
||||
<Step title="Add new project in Vercel">
|
||||
Go to your [Vercel dashboard](https://vercel.com/dashboard) and click the `Add New...` button, and select `Project`.
|
||||
</Step>
|
||||
|
||||
<Step title="Import your monorepo">
|
||||
Select the `Import Git Repository` option and select your repository from the list of repositories.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Project">
|
||||
Vercel will automatically detect that you have a monorepo, but for the backend we need to change the default configuration.
|
||||
|
||||
- Configure your project name
|
||||
- Change the `Framework Preset` to `Other` (there is currently no Python option)
|
||||
- Change the `Root Directory` to the folder where your backend is stored. In this project, that is `apps/api`
|
||||
- Add your Environment Variables
|
||||
|
||||
<Frame>
|
||||
<img src="/_images/deploy-api-configure-project.png" />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Deploy">
|
||||
Click the `Deploy` button to deploy your project. Once built, the API should be running at the URL provided.
|
||||
|
||||
<Frame>
|
||||
<img src="/_images/deploy-api-running.png" />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Add an Ignored Build Step">
|
||||
We don't want to continuously rebuild our frontend every time we push changes to the backend or documentation. To prevent this, we can add an ignored build step.\
|
||||
\
|
||||
From the newly deployed frontend dashboard, navigate to `Settings`, then to `Git`. Scroll down to the `Ignored Build Step` section and set the behaviour to
|
||||
**Only build Turborepo app if there are changes**.
|
||||
|
||||
<Frame>
|
||||
<img src="/_images/deploy-web-ignored-build-step.png" />
|
||||
</Frame>
|
||||
|
||||
<Note>For more information, see the [official documentation](https://vercel.com/docs/projects/overview#ignored-build-step).</Note>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
|
||||
## Documentation deployment
|
||||
<Note>For a more detailed explanation, visit the [offical documentation](https://mintlify.com/docs/quickstart)</Note>
|
||||
|
||||
Connecting to an existing repository with Mintlify is a little bit tricky, as their walkthrough currently seems to only support creating a new repository. However, it is possible to connect an existing repository by following the steps below.
|
||||
|
||||
<Steps>
|
||||
<Step title="Sign in to Mintlify">
|
||||
Sign in to [Mintlify](https://mintlify.com/). You will further be prompted to **Sign in with GitHub**. Follow the onscreen instructions.
|
||||
</Step>
|
||||
|
||||
<Step title="Select Monorepo">
|
||||
Configure your deployment to be pointed to your `docs` directory. Ensure **set up as a monorepo** is selected.
|
||||
|
||||
<Frame>
|
||||
<img src="/_images/deploy-docs-select-repo.png" />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Install Mintlify app to GitHub">
|
||||
From the Mintlify dashboard, click **Things to Do** and then click **Enable automatic updates**.
|
||||
|
||||
<Frame>
|
||||
<img src="/_images/deploy-docs-enable-auto-update.png" />
|
||||
</Frame>
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
---
|
||||
title: Introduction
|
||||
"og:title": "Getting started with Next-Fast-Turbo"
|
||||
description: A starter project for FastAPI, Next.js and Turborepo.
|
||||
---
|
||||
|
||||
## What is Next-Fast-Turbo?
|
||||
|
||||
Next-Fast-Turbo is designed as a personal starter kit for Next.js and FastAPI projects. It is a monorepo that includes a Next.js frontend and a FastAPI backend. The project is designed to be deployed to Vercel, but can be deployed to any platform that supports monorepos.
|
||||
|
||||
This documentation is not written to be a tutorial, but instead to be a reference for the project. It is designed to be a living document that can be updated as the project evolves.
|
||||
|
||||
## Features
|
||||
|
||||
**Frontend**\
|
||||
The Next.js application comes with a fully built frontend that includes:
|
||||
|
||||
- A responsive layout
|
||||
- A dashboard/sidebar design
|
||||
- Pre-configured connection to the backend API
|
||||
- Autogenerated TypeScript types based off the FastAPI OpenAPI schema
|
||||
- A variety of design components, mostly from ShadCN UI (including chart examples)
|
||||
|
||||
**Backend**\
|
||||
The FastAPI application comes with a fully built backend that includes:
|
||||
|
||||
- Example endpoints
|
||||
- Pre-configured schema/crud operations
|
||||
- Easily extensible to add more endpoints
|
||||
|
||||
**Documentation**\
|
||||
Built using Mintlify, a fully responsive and configured documentation site that features:
|
||||
|
||||
- A fully built documentation site
|
||||
- A variety of Mintlify components
|
||||
- A fully configured mint.json
|
||||
|
||||
## Tech stack
|
||||
|
||||
Next-Fast-Turbo is fully open-source built using the following technologies:
|
||||
|
||||
**Frontend**
|
||||
|
||||
- [Next.js](https://nextjs.org/) - Framework for building React applications
|
||||
- [Tailwind CSS](https://tailwindcss.com/) - CSS framework
|
||||
- [ShadCN UI](https://ui.shadcn.com/) - UI kit
|
||||
|
||||
**Backend**
|
||||
|
||||
- [FastAPI](https://fastapi.tiangolo.com/) - Python backend API
|
||||
|
||||
**Documentation**
|
||||
|
||||
- [Mintlify](https://mintlify.io/) - Documentation
|
||||
|
||||
**Global**
|
||||
|
||||
- [Vercel](https://vercel.com/) - hosting
|
||||
- [Turbo](https://turbo.build/repo) - monorepo
|
||||
|
||||
## Getting started
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Local development" icon="code" href="local-development">
|
||||
Install the application locally
|
||||
</Card>
|
||||
<Card title="Deployment" icon="code-branch" href="deployment/vercel">
|
||||
Deploy the monorepo to Vercel
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
|
@ -1,308 +0,0 @@
|
|||
---
|
||||
title: "Local Development"
|
||||
"og:title": "How to setup local development"
|
||||
description: "A guide on how to run the codebase locally."
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Next-Fast-Turbo 's codebase is set up in a monorepo (via [Turborepo](https://turbo.build/repo)) and is fully open-source.
|
||||
Here's the monorepo structure:
|
||||
|
||||
```
|
||||
apps
|
||||
├── api
|
||||
├── docs
|
||||
├── web
|
||||
packages
|
||||
├── eslint-config
|
||||
├── typescript-config
|
||||
```
|
||||
|
||||
The `apps` directory contains the code for:
|
||||
|
||||
- `web`: The frontend of the Next-Fast-Turbo's application
|
||||
- `api`: Next-Fast-Turbo's FastAPI backend - written in Python
|
||||
- `docs`: Next-Fast-Turbo's documentation site
|
||||
|
||||
The `packages` directory contains the code for:
|
||||
|
||||
- `eslint-config`: ESLint configurations for Next-Fast-Turbo's codebase. Boilerplate code included as part of the [create Turbo](https://turbo.build/repo/docs/getting-started/create-new) command
|
||||
- `typescript-config`: TypeScript configurations for Next-Fast-Turbo's codebase. Boilerplate code included as part of the [create Turbo](https://turbo.build/repo/docs/getting-started/create-new) command
|
||||
|
||||
## Running Next-Fast-Turbo
|
||||
### Step 1: Local setup
|
||||
|
||||
<Steps>
|
||||
<Step title="Clone the repo">
|
||||
Clone the [Next-Fast-Turbo repo](https://github.com/cording12/next-fast-turbo.git).
|
||||
|
||||
```bash
|
||||
git clone https://github.com/cording12/next-fast-turbo.git app-name
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="Install dependencies">
|
||||
Change to the root directory of the cloned repository and install the dependencies using the following command:
|
||||
|
||||
```bash
|
||||
cd app-name
|
||||
pnpm install
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Open code-workspace">
|
||||
It is recommended to use the pre-configured Workspace stored in the `.vscode` folder at the project root.
|
||||
|
||||
Navigate to `app-name/.vscode/` and double click `next-fast-turbo.code-workspace` to open in VS Code, or, in VS Code navigate to **File** and then **Open Workspace from File**.
|
||||
|
||||
<Tip>You can rename this to match your project name. The extension, `code-workspace`, must stay the same, but it can be changed to `app-name.code-workspace`</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Step 2: Python setup
|
||||
<Note>
|
||||
In a monorepo, VS Code sometimes uses the wrong Python interpreter, leading to **module not found** errors. You can open the `api` folder in its own VS Code window, but using
|
||||
the pre-configured Workspace is recommended.
|
||||
</Note>
|
||||
While working on the Python backend, ensure that your terminal is activated in the correct folder. From the root, run the following command to change to the `api` directory:
|
||||
|
||||
```bash
|
||||
cd apps/api
|
||||
```
|
||||
|
||||
<Steps>
|
||||
<Step title="Create a virtual environment">
|
||||
Create a virtual environment in the `api` directory:
|
||||
|
||||
<CodeGroup>
|
||||
```bash Poetry
|
||||
poetry install
|
||||
```
|
||||
|
||||
```bash Pip
|
||||
python -m venv .venv
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Tip>
|
||||
If you're using Poetry, you could receive an error noting incorrect format of the `poetry.lock` file. This is a version mismatch between the version installed and the version used to generate the lock file. You can fix this by deleting the `poetry.lock` file
|
||||
and running `poetry install` again.
|
||||
</Tip>
|
||||
|
||||
|
||||
</Step>
|
||||
<Step title="Install dependencies (if not using Poetry)">
|
||||
Run the following command to install the Python dependencies:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Configure .env file">
|
||||
Create a `.env` file in the `api` directory and add the following environment variables:
|
||||
|
||||
```env
|
||||
DB_URL=supabase_url
|
||||
DB_API_KEY=supabase_api_key
|
||||
DB_EMAIl=email_address
|
||||
DB_PASSWORD=password
|
||||
```
|
||||
|
||||
These can be placeholder values for now, but you'll need to replace them with your actual Supabase credentials (covered in step 3).
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
|
||||
### Step 3: Creating tables in Supabase
|
||||
Next-Fast-Turbo uses [Supabase](https://supabase.com/) as the database for the backend. You'll need to create a new project in Supabase and then create the required tables. To get this example running, you need to only create two tables in Supabase.
|
||||
<Steps>
|
||||
|
||||
<Step title="Create an account and new project">
|
||||
Visit [Supabase](https://supabase.com/) and register an account. Once you're logged in, create a new project and give it a name.
|
||||
<Frame>
|
||||
<img src="/_images/supabase-project-create.png" />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Add credentials to `.env`">
|
||||
While your project is building, copy the `Project API Key` and `URL` values and add these to the `.env` file in the `api` directory, as described in step 3 of the [Python setup](#step-2-python-setup).
|
||||
<Frame>
|
||||
<img src="/_images/supabase-credentials.png" />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Create tables">
|
||||
The tables are seeded with the two `.csv` files located in the `api` root, but the tables must be created before seeding.
|
||||
|
||||
From the dashboard, visit the `Table Editor` and click the `New table` button.
|
||||
<Frame>
|
||||
<img src="/_images/supabase-new-table.png" />
|
||||
</Frame>
|
||||
|
||||
Create the `users` and `spells` tables with columns that match their respective CSV columns. Below is how they are both configured:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Users table">
|
||||
<Frame>
|
||||
<img src="/_images/supabase-create-user-table.png" />
|
||||
</Frame>
|
||||
</Tab>
|
||||
<Tab title="Spells table">
|
||||
<Frame>
|
||||
<img src="/_images/supabase-create-spells-table.png" />
|
||||
</Frame>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
<Warning>
|
||||
RLS is set to disabled on these tables. Authentication with Supabase was not in the scope for this project, but you will want to configure this yourself for anything more than this simple example.
|
||||
You can read more about [RLS](https://supabase.com/docs/guides/auth/row-level-security) in the Supabase documentation.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Upload CSV seed data">
|
||||
Once the tables are created, you can seed them with the data from the `.csv` files. From the `Table Editor`, click the `Insert` button and select the relevant `.csv` file to upload.
|
||||
<Frame>
|
||||
<img src="/_images/supabase-upload-csv.png" />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
### Step 4: Configure Turbo remote caching (optional)
|
||||
Turborepo can use a technique known as [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching) to share cache artifacts across machines, enabling you to share build caches with your team and CI/CD pipelines.
|
||||
|
||||
By default, Turborepo will cache locally. To enable Remote Caching you will need an account with Vercel.
|
||||
|
||||
<Steps>
|
||||
<Step title="Login via Turborepo CLI">
|
||||
From the project root, run the command:
|
||||
```bash
|
||||
npx turbo login
|
||||
```
|
||||
This will authenticate the Turborepo CLI with your [Vercel account](https://vercel.com/docs/concepts/personal-accounts/overview).
|
||||
</Step>
|
||||
<Step title="Link your Turborepo to your Remote Cache">
|
||||
Link your Turborepo to your Remote Cache by running the following command from the root of your Turborepo:
|
||||
```bash
|
||||
npx turbo link
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Step 5: Running everything
|
||||
To make the most of Turbo's monorepo structure, you can run the frontend, backend and documentation site simultaneously. From the root, run the following command:
|
||||
|
||||
```bash root
|
||||
pnpm run dev
|
||||
```
|
||||
<Tip>You can still run each separately by running the task directly from the relevant `package.json` or by running the `pnpm run dev` command from a terminal activated in the desired target location</Tip>
|
||||
|
||||
|
||||
## Working with a monorepo in VS Code
|
||||
|
||||
For a better development experience, you can use VS Code Workspaces for the monorepo. This will allow you to run tasks and debug the codebase from a single window, while keeping things more organised.
|
||||
|
||||
Furthermore, VS Code doesn't handle Python virtual environments particularly well when working within a monorepo. Running the `dev` command from the project root can make VS Code use your global Python installation,
|
||||
instead of the `.venv` created in the `api` root. By using a Workspace, this alleviates the problem.
|
||||
|
||||
<Note>For a more detailed guide on setting up a monorepo in VS Code, check out the [official Multi-root Workspaces](https://code.visualstudio.com/docs/editor/multi-root-workspaces) documentation</Note>
|
||||
|
||||
### Step 1: Open the monorepo
|
||||
In the `/.vscode/` directory, you'll find a `next-fast-turbo.code-workspace` file. Open this file in VS Code to open the monorepo Workspace.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="No code-workspace">
|
||||
<Frame caption="Frontend (web) folder open without using the Workspace">
|
||||
<img src="/_images/no-workspace.png" />
|
||||
</Frame>
|
||||
</Tab>
|
||||
|
||||
<Tab title="With a code-workspace">
|
||||
<Frame caption="Frontend (web) folder open with a Workspace">
|
||||
<img src="/_images/workspace.png" />
|
||||
</Frame>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Step 2: Running tasks
|
||||
VS Code will try to autodetect tasks from gulp, grunt, npm, and TypeScript project files across all folders in a workspace as well as search for tasks defined in tasks.json files. The location of tasks is indicated by a folder name suffix
|
||||
|
||||
<Frame caption="Workspace tasks">
|
||||
<img src="/_images/tasks.png" />
|
||||
</Frame>
|
||||
|
||||
From the above example, you can see there are several configured tasks with the relevant folder name after the task name.
|
||||
|
||||
### Step 3: Debugging
|
||||
With multi-root workspaces, VS Code searches across all folders for `launch.json` debug configuration files and displays them with the folder name as a suffix.
|
||||
Additionally VS Code will also display launch configurations defined in the workspace configuration file.
|
||||
|
||||
<Frame caption="Run and debug panel">
|
||||
<img src="/_images/run-and-debug.png" />
|
||||
</Frame>
|
||||
|
||||
You can still create [launch configurations](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations) for each individual package in the monorepo and they will populate in the dropdown list automatically.
|
||||
|
||||
#### Workspace launch configurations
|
||||
If you want to create a Workspace level configuration with [compound launch](https://code.visualstudio.com/docs/editor/debugging#_compound-launch-configurations), you can edit the `next-fast-turbo.code-workspace` file and add the configurations you wish to launch.
|
||||
|
||||
<Tip>You can also edit the Workspace configuration file via the Command Palette\
|
||||
(Windows: Ctrl + Shift + P) and searching for `open workspace config`</Tip>
|
||||
|
||||
A compound launch configuration can reference the individual launch configurations by name as long as the names are unique within the workspace, for example:
|
||||
|
||||
```json
|
||||
{
|
||||
"launch": {
|
||||
"version": "0.2.0",
|
||||
"configurations": [],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Launch Frontend and Backend",
|
||||
"configurations": ["Next.js: Chrome", "Python: FastAPI"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For a more detailed explanation, check out the [official documentation](https://code.visualstudio.com/docs/editor/multi-root-workspaces#_workspace-launch-configurations)
|
||||
|
||||
### Optional: Extensions
|
||||
<AccordionGroup>
|
||||
<Accordion title="Python Envy">
|
||||
Helps VS Code identify the correct Python virtual environment when installed in the working directory. This is especially useful when working with Python in a monorepo, as it can be difficult for VS Code to manage multiple virtual environments.
|
||||
|
||||
[Python Envy](https://marketplace.visualstudio.com/items?itemName=teticio.python-envy)
|
||||
</Accordion>
|
||||
<Accordion title="Workspace Terminals (recommended)">
|
||||
<Frame caption="Workspace Terminals in VS Code">
|
||||
<img src="/_images/terminals.png" />
|
||||
</Frame>
|
||||
Terminal management in a monorepo can become cumbersome. This extension automatically creates a terminal in each of your monorepo's directories and names them accordingly. This will allow you to run commands and tasks from a terminal that's already set up in the correct directory.
|
||||
|
||||
[Workspace Terminals](https://marketplace.visualstudio.com/items?itemName=joshx.workspace-terminals)
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Turbo config" icon="code" href="configuration/turbo">
|
||||
Configuring Turbo for your monorepo
|
||||
</Card>
|
||||
<Card title="FastAPI config" icon="database" href="configuration/fastapi">
|
||||
Configuring FastAPI
|
||||
</Card>
|
||||
<Card title="Frontend config" icon="computer" href="configuration/nextjs">
|
||||
Configuring Next.js
|
||||
</Card>
|
||||
<Card title="Documentation config" icon="book" href="configuration/docs">
|
||||
Configuring Mintlify for documentation
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
|
Before Width: | Height: | Size: 755 B |
|
|
@ -1 +0,0 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:8px;}</style></defs><circle class="cls-1" cx="50" cy="50" r="49.9"/><path class="cls-2" d="M61.77,69.61,81.38,50,61.77,30.39"/><path class="cls-2" d="M38.23,69.61,18.62,50,38.23,30.39"/></svg>
|
||||
|
Before Width: | Height: | Size: 403 B |
|
|
@ -1 +0,0 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><style>.cls-1{fill:#020000;}.cls-2{fill:none;stroke:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-width:8px;}</style></defs><circle class="cls-1" cx="50" cy="50" r="49.9"/><path class="cls-2" d="M61.77,69.61,81.38,50,61.77,30.39"/><path class="cls-2" d="M38.23,69.61,18.62,50,38.23,30.39"/></svg>
|
||||
|
Before Width: | Height: | Size: 406 B |
|
|
@ -1,96 +0,0 @@
|
|||
{
|
||||
"$schema": "https://mintlify.com/schema.json",
|
||||
"name": "Next-Fast-Turbo Docs",
|
||||
"favicon": "/logos/logo.svg",
|
||||
"logo": {
|
||||
"light": "/logos/logo.svg",
|
||||
"dark": "/logos/logo-light.svg"
|
||||
},
|
||||
"colors": {
|
||||
"primary": "#7c3aed",
|
||||
"light": "#af87ff",
|
||||
"dark": "#1e293b",
|
||||
"background": {
|
||||
"dark": "#030712",
|
||||
"light": "#ffffff"
|
||||
}
|
||||
},
|
||||
"modeToggle": {
|
||||
"default": "dark"
|
||||
},
|
||||
"topbarCtaButton": {
|
||||
"name": "Dashboard",
|
||||
"url": "https://next-fast-turbo-web.vercel.app/"
|
||||
},
|
||||
"tabs": [
|
||||
{
|
||||
"name": "API Reference",
|
||||
"url": "api"
|
||||
}
|
||||
],
|
||||
"anchors": [
|
||||
{
|
||||
"name": "Documentation",
|
||||
"icon": "book-open-cover",
|
||||
"url": "documentation"
|
||||
},
|
||||
{
|
||||
"name": "API Reference",
|
||||
"icon": "rectangle-terminal",
|
||||
"url": "api"
|
||||
}
|
||||
],
|
||||
"navigation": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"pages": [
|
||||
"documentation/introduction",
|
||||
"documentation/local-development"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Configuration",
|
||||
"pages": [
|
||||
"documentation/configuration/turbo",
|
||||
"documentation/configuration/fastapi",
|
||||
"documentation/configuration/nextjs",
|
||||
"documentation/configuration/docs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Deployment",
|
||||
"pages": [
|
||||
"documentation/deployment/deployment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Users",
|
||||
"pages": [
|
||||
"api/users/get-user",
|
||||
"api/users/get-all-users",
|
||||
"api/users/search-users",
|
||||
"api/users/create-user"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Spells",
|
||||
"pages": [
|
||||
"api/spells/get-spell",
|
||||
"api/spells/get-all-spells",
|
||||
"api/spells/search-spells"
|
||||
]
|
||||
}
|
||||
],
|
||||
"api": {
|
||||
"maintainOrder": true,
|
||||
"baseUrl": "https://next-fast-turbo-api.vercel.app"
|
||||
},
|
||||
"openapi": "https://next-fast-turbo-api.vercel.app/openapi.json",
|
||||
"feedback": {
|
||||
"thumbsRating": true
|
||||
},
|
||||
"footerSocials": {
|
||||
"github": "https://github.com/cording12/next-fast-turbo",
|
||||
"linkedin": "https://www.linkedin.com/in/jon-cording/"
|
||||
}
|
||||
}
|
||||
4
apps/docs/next.config.mjs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {};
|
||||
|
||||
export default nextConfig;
|
||||
|
|
@ -1,545 +0,0 @@
|
|||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "FastAPI App",
|
||||
"description": "A simple FastAPI app",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://next-fast-turbo-api.vercel.app"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/api/v1/users/get/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Get User",
|
||||
"description": "Returns a user from a user_id.\n\n**Returns:**\n- User: User object.",
|
||||
"operationId": "users-get_user",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "user_id",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"title": "User Id"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/User"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not found"
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/users/get-all/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Get All Users",
|
||||
"description": "Returns a list of all users.\n\n**Returns:**\n- list[User]: List of all users.",
|
||||
"operationId": "users-get_all_users",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/User"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Response Users-Get All Users"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/users/search/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Search Users",
|
||||
"description": "Search for users based on a keyword and return the top `max_results` items.\n\n**Args:**\n- keyword (str, optional): The keyword to search for. Defaults to None.\n- max_results (int, optional): The maximum number of search results to return. Defaults to 10.\n- search_on (str, optional): The field to perform the search on. Defaults to \"email\".\n\n**Returns:**\n- UserSearchResults: Object containing a list of the top `max_results` items that match the keyword.",
|
||||
"operationId": "users-search_users",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "search_on",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"id",
|
||||
"email",
|
||||
"forename",
|
||||
"surname"
|
||||
],
|
||||
"type": "string",
|
||||
"default": "email",
|
||||
"title": "Search On"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "keyword",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Keyword"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "max_results",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": 10,
|
||||
"title": "Max Results"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UserSearchResults"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not found"
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/users/create": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Create User",
|
||||
"description": "Craete a new user.\n\n**Args:**\n- user_in (UserCreate): JSON of the user to create. Forename, surname and email. Email must be unique.\n\n**Returns:**\n- User: User object",
|
||||
"operationId": "users-create_user",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UserCreate"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/User"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not found"
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/spells/get/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"spells"
|
||||
],
|
||||
"summary": "Get Spell",
|
||||
"description": "Returns a spell from a spell_id.\n\n**Returns:**\n- spell: spell object.",
|
||||
"operationId": "spells-get_spell",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "spell_id",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"title": "Spell Id"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Spell"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not found"
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/spells/get-all/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"spells"
|
||||
],
|
||||
"summary": "Get All Spells",
|
||||
"description": "Returns a list of all spells.\n\n**Returns:**\n- list[spell]: List of all spells.",
|
||||
"operationId": "spells-get_all_spells",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Spell"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Response Spells-Get All Spells"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/spells/search/": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"spells"
|
||||
],
|
||||
"summary": "Search Spells",
|
||||
"description": "Search for spells based on a keyword and return the top `max_results` items.\n\n**Args:**\n- keyword (str, optional): The keyword to search for. Defaults to None.\n- max_results (int, optional): The maximum number of search results to return. Defaults to 10.\n- search_on (str, optional): The field to perform the search on. Defaults to \"email\".\n\n**Returns:**\n- spellSearchResults: Object containing a list of the top `max_results` items that match the keyword.",
|
||||
"operationId": "spells-search_spells",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "search_on",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"enum": [
|
||||
"id",
|
||||
"spells",
|
||||
"description"
|
||||
],
|
||||
"type": "string",
|
||||
"default": "spells",
|
||||
"title": "Search On"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "keyword",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Keyword"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "max_results",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": 10,
|
||||
"title": "Max Results"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SpellSearchResults"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not found"
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ValidationError"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Detail"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "HTTPValidationError"
|
||||
},
|
||||
"Spell": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"title": "Id"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "Name"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"title": "Description"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"name",
|
||||
"description"
|
||||
],
|
||||
"title": "Spell"
|
||||
},
|
||||
"SpellSearchResults": {
|
||||
"properties": {
|
||||
"results": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Spell"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Results"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"results"
|
||||
],
|
||||
"title": "SpellSearchResults"
|
||||
},
|
||||
"User": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"title": "Id"
|
||||
},
|
||||
"forename": {
|
||||
"type": "string",
|
||||
"title": "Forename"
|
||||
},
|
||||
"surname": {
|
||||
"type": "string",
|
||||
"title": "Surname"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"title": "Email"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"forename",
|
||||
"surname",
|
||||
"email"
|
||||
],
|
||||
"title": "User"
|
||||
},
|
||||
"UserCreate": {
|
||||
"properties": {
|
||||
"forename": {
|
||||
"type": "string",
|
||||
"title": "Forename"
|
||||
},
|
||||
"surname": {
|
||||
"type": "string",
|
||||
"title": "Surname"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"title": "Email"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"forename",
|
||||
"surname",
|
||||
"email"
|
||||
],
|
||||
"title": "UserCreate"
|
||||
},
|
||||
"UserSearchResults": {
|
||||
"properties": {
|
||||
"results": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/User"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Results"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"results"
|
||||
],
|
||||
"title": "UserSearchResults"
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Location"
|
||||
},
|
||||
"msg": {
|
||||
"type": "string",
|
||||
"title": "Message"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"title": "Error Type"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"loc",
|
||||
"msg",
|
||||
"type"
|
||||
],
|
||||
"title": "ValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,27 @@
|
|||
{
|
||||
"name": "docs",
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"dev": "mintlify dev --port 3001",
|
||||
"generate-api": "npx @mintlify/scraping@latest openapi-file https://next-fast-turbo-api.vercel.app/openapi.json --outDir ./api/",
|
||||
"generate-api:dev": "npx @mintlify/scraping@latest openapi-file https://127.0.0.1:8000/openapi.json --outDir ./api/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mintlify/scraping": "^3.0.88",
|
||||
"mintlify": "^4.0.127"
|
||||
}
|
||||
}
|
||||
"name": "docs",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbo --port 3001",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@repo/ui": "workspace:*",
|
||||
"react": "19.0.0-rc-f994737d14-20240522",
|
||||
"react-dom": "19.0.0-rc-f994737d14-20240522",
|
||||
"next": "15.0.0-rc.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@repo/eslint-config": "workspace:*",
|
||||
"@repo/typescript-config": "workspace:*",
|
||||
"typescript": "^5",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "15.0.0-rc.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
3
apps/docs/public/file-text.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.5 13.5V6.5V5.41421C14.5 5.149 14.3946 4.89464 14.2071 4.70711L9.79289 0.292893C9.60536 0.105357 9.351 0 9.08579 0H8H3H1.5V1.5V13.5C1.5 14.8807 2.61929 16 4 16H12C13.3807 16 14.5 14.8807 14.5 13.5ZM13 13.5V6.5H9.5H8V5V1.5H3V13.5C3 14.0523 3.44772 14.5 4 14.5H12C12.5523 14.5 13 14.0523 13 13.5ZM9.5 5V2.12132L12.3787 5H9.5ZM5.13 5.00062H4.505V6.25062H5.13H6H6.625V5.00062H6H5.13ZM4.505 8H5.13H11H11.625V9.25H11H5.13H4.505V8ZM5.13 11H4.505V12.25H5.13H11H11.625V11H11H5.13Z" fill="#666666"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 645 B |
10
apps/docs/public/globe.svg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_868_525)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.268 14.0934C11.9051 13.4838 13.2303 12.2333 13.9384 10.6469C13.1192 10.7941 12.2138 10.9111 11.2469 10.9925C11.0336 12.2005 10.695 13.2621 10.268 14.0934ZM8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM8.48347 14.4823C8.32384 14.494 8.16262 14.5 8 14.5C7.83738 14.5 7.67616 14.494 7.51654 14.4823C7.5132 14.4791 7.50984 14.4759 7.50647 14.4726C7.2415 14.2165 6.94578 13.7854 6.67032 13.1558C6.41594 12.5744 6.19979 11.8714 6.04101 11.0778C6.67605 11.1088 7.33104 11.125 8 11.125C8.66896 11.125 9.32395 11.1088 9.95899 11.0778C9.80021 11.8714 9.58406 12.5744 9.32968 13.1558C9.05422 13.7854 8.7585 14.2165 8.49353 14.4726C8.49016 14.4759 8.4868 14.4791 8.48347 14.4823ZM11.4187 9.72246C12.5137 9.62096 13.5116 9.47245 14.3724 9.28806C14.4561 8.87172 14.5 8.44099 14.5 8C14.5 7.55901 14.4561 7.12828 14.3724 6.71194C13.5116 6.52755 12.5137 6.37904 11.4187 6.27753C11.4719 6.83232 11.5 7.40867 11.5 8C11.5 8.59133 11.4719 9.16768 11.4187 9.72246ZM10.1525 6.18401C10.2157 6.75982 10.25 7.36805 10.25 8C10.25 8.63195 10.2157 9.24018 10.1525 9.81598C9.46123 9.85455 8.7409 9.875 8 9.875C7.25909 9.875 6.53877 9.85455 5.84749 9.81598C5.7843 9.24018 5.75 8.63195 5.75 8C5.75 7.36805 5.7843 6.75982 5.84749 6.18401C6.53877 6.14545 7.25909 6.125 8 6.125C8.74091 6.125 9.46123 6.14545 10.1525 6.18401ZM11.2469 5.00748C12.2138 5.08891 13.1191 5.20593 13.9384 5.35306C13.2303 3.7667 11.9051 2.51622 10.268 1.90662C10.695 2.73788 11.0336 3.79953 11.2469 5.00748ZM8.48347 1.51771C8.4868 1.52089 8.49016 1.52411 8.49353 1.52737C8.7585 1.78353 9.05422 2.21456 9.32968 2.84417C9.58406 3.42562 9.80021 4.12856 9.95899 4.92219C9.32395 4.89118 8.66896 4.875 8 4.875C7.33104 4.875 6.67605 4.89118 6.04101 4.92219C6.19978 4.12856 6.41594 3.42562 6.67032 2.84417C6.94578 2.21456 7.2415 1.78353 7.50647 1.52737C7.50984 1.52411 7.51319 1.52089 7.51653 1.51771C7.67615 1.50597 7.83738 1.5 8 1.5C8.16262 1.5 8.32384 1.50597 8.48347 1.51771ZM5.73202 1.90663C4.0949 2.51622 2.76975 3.7667 2.06159 5.35306C2.88085 5.20593 3.78617 5.08891 4.75309 5.00748C4.96639 3.79953 5.30497 2.73788 5.73202 1.90663ZM4.58133 6.27753C3.48633 6.37904 2.48837 6.52755 1.62761 6.71194C1.54392 7.12828 1.5 7.55901 1.5 8C1.5 8.44099 1.54392 8.87172 1.62761 9.28806C2.48837 9.47245 3.48633 9.62096 4.58133 9.72246C4.52807 9.16768 4.5 8.59133 4.5 8C4.5 7.40867 4.52807 6.83232 4.58133 6.27753ZM4.75309 10.9925C3.78617 10.9111 2.88085 10.7941 2.06159 10.6469C2.76975 12.2333 4.0949 13.4838 5.73202 14.0934C5.30497 13.2621 4.96639 12.2005 4.75309 10.9925Z" fill="#666666"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_868_525">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
1
apps/docs/public/next.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
10
apps/docs/public/vercel.svg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_977_547)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 3L18.5 17H2.5L10.5 3Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_977_547">
|
||||
<rect width="16" height="16" fill="white" transform="translate(2.5 2)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 367 B |
3
apps/docs/public/window.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5H14.5V12.5C14.5 13.0523 14.0523 13.5 13.5 13.5H2.5C1.94772 13.5 1.5 13.0523 1.5 12.5V2.5ZM0 1H1.5H14.5H16V2.5V12.5C16 13.8807 14.8807 15 13.5 15H2.5C1.11929 15 0 13.8807 0 12.5V2.5V1ZM3.75 5.5C4.16421 5.5 4.5 5.16421 4.5 4.75C4.5 4.33579 4.16421 4 3.75 4C3.33579 4 3 4.33579 3 4.75C3 5.16421 3.33579 5.5 3.75 5.5ZM7 4.75C7 5.16421 6.66421 5.5 6.25 5.5C5.83579 5.5 5.5 5.16421 5.5 4.75C5.5 4.33579 5.83579 4 6.25 4C6.66421 4 7 4.33579 7 4.75ZM8.75 5.5C9.16421 5.5 9.5 5.16421 9.5 4.75C9.5 4.33579 9.16421 4 8.75 4C8.33579 4 8 4.33579 8 4.75C8 5.16421 8.33579 5.5 8.75 5.5Z" fill="#666666"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 750 B |
18
apps/docs/tsconfig.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends": "@repo/typescript-config/nextjs.json",
|
||||
"compilerOptions": {
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"next.config.mjs",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||