Compare commits

...

2 Commits

Author SHA1 Message Date
2990913f9d website/docs: Remove comment. 2025-05-03 04:06:45 +02:00
550775dc69 Reapply "website/docs: Prepare for monorepo. (#14119) 2025-05-03 04:01:37 +02:00
62 changed files with 6628 additions and 4428 deletions

1
website/.gitignore vendored
View File

@ -25,4 +25,5 @@ yarn-error.log*
static/docker-compose.yml
static/schema.yml
static/releases.gen.json
docs/developer-docs/api/reference/**

View File

@ -1,7 +0,0 @@
# Ignore artifacts:
build
coverage
.docusaurus
node_modules
help
static

View File

@ -1 +0,0 @@
{}

View File

@ -29,8 +29,8 @@ You can also use custom email templates, to use your own design or layout.
Starting with authentik 2024.2, it is possible to create `.txt` files with the same name as the `.html` template. If a matching `.txt` file exists, the email sent will be a multipart email with both the text and HTML template.
:::
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs
defaultValue="docker-compose"

View File

@ -2,8 +2,9 @@
title: Caddy
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
import Placeholders from "./__placeholders.md";
import CaddyStandalone from "./_caddy_standalone.md";

View File

@ -2,8 +2,9 @@
title: Envoy
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
import Placeholders from "./__placeholders.md";
import EnvoyIstio from "./_envoy_istio.md";

View File

@ -1,5 +1,5 @@
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
# nginx

View File

@ -1,5 +1,5 @@
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
# Traefik

View File

@ -0,0 +1,82 @@
import styles from "./styles.module.css";
const RADIUSProtocols = [
"PAP",
"CHAP",
"Digest",
"MS-CHAP",
"PEAP",
"MS-CHAPv2",
"Cisco LEAP",
"EAP-GTC",
"EAP-MD5",
"EAP-PWD",
] as const satisfies string[];
type RADIUSProtocol = (typeof RADIUSProtocols)[number];
const HashKinds = [
"Cleartext",
"NT",
"MD5",
"Salted MD5",
"SHA1",
"Salted SHA1",
"Unix Crypt",
] as const satisfies string[];
type HashKind = (typeof HashKinds)[number];
const supportMatrix: Record<HashKind, RADIUSProtocol[]> = {
"Cleartext": [
"PAP",
"CHAP",
"Digest",
"MS-CHAP",
"PEAP",
"MS-CHAPv2",
"Cisco LEAP",
"EAP-GTC",
"EAP-MD5",
"EAP-PWD",
],
"NT": ["PAP", "MS-CHAP", "PEAP", "MS-CHAPv2", "Cisco LEAP", "EAP-GTC"],
"MD5": ["PAP", "EAP-GTC"],
"Salted MD5": ["PAP", "EAP-GTC"],
"SHA1": ["PAP", "EAP-GTC"],
"Salted SHA1": ["PAP", "EAP-GTC", "EAP-PWD"],
"Unix Crypt": ["PAP", "EAP-GTC", "EAP-PWD"],
};
export const HashSupport: React.FC = () => {
return (
<table className={styles.table}>
<thead>
<tr>
<th></th>
{HashKinds.map((hashKind, i) => (
<th key={i}>{hashKind}</th>
))}
</tr>
</thead>
<tbody>
{RADIUSProtocols.map((radiusProtocol, i) => (
<tr key={i}>
<td>{radiusProtocol}</td>
{HashKinds.map((hashKind) => {
const protocols = supportMatrix[hashKind];
const supported = protocols.includes(radiusProtocol);
return (
<td data-supported={supported} key={hashKind}>
{supported ? "✓" : "✗"}
</td>
);
})}
</tr>
))}
</tbody>
</table>
);
};

View File

@ -2,7 +2,7 @@
title: RADIUS Provider
---
import { Check, X, AlertTriangle } from "react-feather";
import { HashSupport } from "./HashSupport";
You can configure a Radius provider for applications that don't support any other protocols or that require Radius.
@ -56,15 +56,4 @@ After creation, make sure to select the RADIUS property mapping in the RADIUS pr
The RADIUS provider only supports the [PAP](https://en.wikipedia.org/wiki/Password_Authentication_Protocol) (Password Authentication Protocol) protocol:
| | Clear-text | NT hash | MD5 hash | Salted MD5 hash | SHA1 hash | Salted SHA1 hash | Unix Crypt |
| ------------ | --------------- | --------------- | --------------- | --------------- | --------------- | ---------------- | --------------- |
| PAP | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> |
| CHAP | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| Digest | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| MS-CHAP | <Check></Check> | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| PEAP | <Check></Check> | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| EAP-MSCHAPv2 | <Check></Check> | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| Cisco LEAP | <Check></Check> | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| EAP-GTC | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> | <Check></Check> |
| EAP-MD5 | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> | <X></X> |
| EAP-PWD | <Check></Check> | <X></X> | <X></X> | <X></X> | <X></X> | <Check></Check> | <Check></Check> |
<HashSupport />

View File

@ -0,0 +1,20 @@
.table td {
text-align: center;
font-weight: bold;
&:first-child {
text-align: right;
width: 13ch;
}
&:not(:first-child) {
width: 10ch;
}
&[data-supported="true"] {
color: var(--ifm-color-success-dark);
}
&[data-supported="false"] {
color: var(--ifm-color-danger-dark);
}
}

View File

@ -4,6 +4,7 @@ title: Example
This is one of the default packaged blueprints to create the default authentication flow.
<!-- prettier-ignore-start -->
```yaml
version: 1
metadata:
@ -64,3 +65,4 @@ entries:
target: !KeyOf flow
model: authentik_flows.flowstagebinding
```
<!-- prettier-ignore-end -->

View File

@ -48,6 +48,8 @@ Returns the value of the given environment variable. Can be used as a scalar wit
Examples:
{/* prettier-ignore-start */}
```yaml
configure_flow: !Find [authentik_flows.flow, [slug, default-password-change]]
```
@ -60,6 +62,8 @@ configure_flow:
]
```
{/* prettier-ignore-end */}
Looks up any model and resolves to the the matches' primary key.
First argument is the model to be queried, remaining arguments are expected to be pairs of key=value pairs to query for.
@ -67,10 +71,15 @@ First argument is the model to be queried, remaining arguments are expected to b
Example:
{/* prettier-ignore-start */}
```yaml
configure_flow: !Context foo
```
{/* prettier-ignore-end */}
Find values from the context. Can optionally be called with a default like `!Context [foo, default-value]`.
#### `!Format`
@ -209,6 +218,8 @@ For example, given a sequence like this - `["a", "b", "c"]`, this tag will resol
Minimal examples:
{/* prettier-ignore-start */}
```yaml
configuration_stages: !Enumerate [
!Context map_of_totp_stage_names_and_types,
@ -224,6 +235,8 @@ configuration_stages: !Enumerate [
]
```
{/* prettier-ignore-end */}
The above example will resolve to something like this:
```yaml
@ -265,6 +278,8 @@ Full example:
Note that an `!Enumeration` tag's iterable can never be an `!Item` or `!Value` tag with a depth of `0`. Minimum depth allowed is `1`. This is because a depth of `0` refers to the `!Enumeration` tag the `!Item` or `!Value` tag is in, and an `!Enumeration` tag cannot iterate over itself.
:::
{/* prettier-ignore-start */}
```yaml
example: !Enumerate [
!Context sequence, # ["foo", "bar"]
@ -288,6 +303,8 @@ example: !Enumerate [
]
```
{/* prettier-ignore-end */}
The above example will resolve to something like this:
```yaml

View File

@ -2,8 +2,8 @@
To further modify the look of authentik, a custom CSS file can be created. Creating such a file is outside the scope of this document.
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs
defaultValue="docker-compose"

View File

@ -255,8 +255,8 @@ This section covers the usage of React components within our documentation. File
Use **Tabs** to display different configurations (e.g., setting up authentication with OIDC vs. SAML) to help users navigate between options. Default to the easier or more common option. Insert the following lines wherever you want the code block to appear:
```jsx
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs
defaultValue="oidc"

View File

@ -148,7 +148,6 @@ We'll be publishing a security Issue (CVE-2022-xxxxx) and accompanying fix on _d
<details>
<summary>Mailing list template</summary>
<p>
Subject: `Release of authentik Security releases 2022.10.3 and 2022.11.3`
@ -158,12 +157,10 @@ The security advisory for CVE-2022-xxxxx has been published: https://github.com/
Releases 2022.10.3 and 2022.11.3 with fixes included are available here: https://github.com/goauthentik/authentik/releases
```
</p>
</details>
<details>
<summary>Discord template</summary>
<p>
```markdown
[...existing announcement...]
@ -175,5 +172,4 @@ Advisory for for CVE-2022-xxxxx has been published here https://github.com/goaut
The fixed versions 2022.10.3 and 2022.11.3 are available here: https://github.com/goauthentik/authentik/releases
```
</p>
</details>

View File

@ -62,7 +62,7 @@ Depending on platform, some native dependencies might be required. On macOS, run
4. From the cloned repository root, install the front-end dependencies using NPM.
```shell
cd web
# From the root of the repository
npm ci
```

View File

@ -9,9 +9,9 @@ tags:
- docker
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import ExecutionEnvironment from "@docusaurus/ExecutionEnvironment";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
## Requirements

View File

@ -24,39 +24,34 @@ The authentik product provides the following consoles:
In authentik, you can use Light or Dark mode for the Admin interface, User interface, and the Flow interface.
import "react-before-after-slider-component/dist/build.css";
import ReactBeforeSliderComponent from "react-before-after-slider-component";
import useBaseUrl from "@docusaurus/useBaseUrl";
import ReactBeforeSliderComponent from "react-before-after-slider-component";
import "react-before-after-slider-component/dist/build.css";
<ReactBeforeSliderComponent
firstImage={{
id: 1,
imageUrl: useBaseUrl("img/screen_flow_dark.jpg"),
}}
secondImage={{
id: 2,
imageUrl: useBaseUrl("img/screen_flow_light.jpg"),
}}
/>
<ReactBeforeSliderComponent
firstImage={{
id: 1,
imageUrl: useBaseUrl("img/screen_apps_dark.jpg"),
}}
secondImage={{
id: 2,
imageUrl: useBaseUrl("img/screen_apps_light.jpg"),
}}
/>
<ReactBeforeSliderComponent
firstImage={{
id: 1,
imageUrl: useBaseUrl("img/screen_admin_dark.jpg"),
}}
secondImage={{
id: 2,
imageUrl: useBaseUrl("img/screen_admin_light.jpg"),
}}
/>

View File

@ -17,8 +17,8 @@ To disable these outbound connections, adjust the settings as follows:
To view a list of all configuration options, refer to the [Configuration](./configuration/configuration.mdx) documentation.
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs
defaultValue="docker-compose"

View File

@ -8,8 +8,8 @@ You can test upcoming authentik versions, including major new features that are
Downgrading from the Beta is not supported. It is recommended to take a backup before upgrading, or test Beta versions on a separate install. Upgrading from Beta versions to the next release is usually possible, however also not supported.
:::
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs
defaultValue="docker-compose"

View File

@ -17,8 +17,8 @@ All of these variables can be set to values, but you can also use a URI-like for
## Set your environment variables
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs groupId="platform">
<TabItem value="docker-compose" label="Docker Compose" default>

View File

@ -27,8 +27,8 @@ This installation method is for test setups and small-scale production setups.
To download the latest `docker-compose.yml` open your terminal and navigate to the directory of your choice.
Run the following command:
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
{/* prettier-ignore */}
<Tabs groupId="OS">

View File

@ -20,8 +20,8 @@ authentik does not support downgrading. Make sure to back up your database in ca
## Upgrade authentik
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs groupId="platform">
<TabItem value="docker-compose" label="Docker Compose" default>

View File

@ -14,8 +14,8 @@ By default, the GeoIP database is loaded from `/geoip/GeoLite2-City.mmdb`. If mo
If you want to disable GeoIP, you can set the path to a non-existent path and authentik will skip the GeoIP.
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs
defaultValue="docker-compose"

View File

@ -2,8 +2,8 @@
title: General troubleshooting steps
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
## Set the log level to TRACE

View File

@ -10,8 +10,8 @@ The server and worker containers support multiple log levels: `debug`, `info`, `
To modify the log level, follow the steps for your platform
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs
groupId="platform"

View File

@ -32,6 +32,7 @@ See the [overview](../../property-mappings/index.md) for information on how prop
Each top level SCIM attribute is available as a variable in the expression. For example given an SCIM request with the payload of
<!-- prettier-ignore-start -->
```json
{
"schemas": [
@ -57,6 +58,7 @@ Each top level SCIM attribute is available as a variable in the expression. For
}
}
```
<!-- prettier-ignore-end -->
The following variables are available in the expression:

View File

@ -1,13 +1,14 @@
import type { Config } from "@docusaurus/types";
import type * as Preset from "@docusaurus/preset-classic";
import { themes as prismThemes } from "prism-react-renderer";
import type { Config } from "@docusaurus/types";
import type * as OpenApiPlugin from "docusaurus-plugin-openapi-docs";
import remarkGithub, { BuildUrlValues } from "remark-github";
import { defaultBuildUrl } from "remark-github";
import * as path from "node:path";
import { themes as prismThemes } from "prism-react-renderer";
import remarkDirective from "remark-directive";
import remarkVersionDirective from "./remark/version-directive.js";
import remarkPreviewDirective from "./remark/preview-directive.js";
import remarkSupportDirective from "./remark/support-directive.js";
import remarkGithub, { BuildUrlValues, defaultBuildUrl } from "remark-github";
import remarkPreviewDirective from "./remark/preview-directive.mjs";
import remarkSupportDirective from "./remark/support-directive.mjs";
import remarkVersionDirective from "./remark/version-directive.mjs";
const createConfig = (): Config => {
return {
@ -59,16 +60,16 @@ const createConfig = (): Config => {
target: "_self",
},
{
href: "https://github.com/goauthentik/authentik",
"href": "https://github.com/goauthentik/authentik",
"data-icon": "github",
"aria-label": "GitHub",
position: "right",
"position": "right",
},
{
href: "https://goauthentik.io/discord",
"href": "https://goauthentik.io/discord",
"data-icon": "discord",
"aria-label": "Discord",
position: "right",
"position": "right",
},
],
},
@ -111,8 +112,7 @@ const createConfig = (): Config => {
id: "docs",
sidebarPath: "./sidebars.js",
showLastUpdateTime: false,
editUrl:
"https://github.com/goauthentik/authentik/edit/main/website/",
editUrl: "https://github.com/goauthentik/authentik/edit/main/website/",
docItemComponent: "@theme/ApiItem",
beforeDefaultRemarkPlugins: [
@ -128,8 +128,7 @@ const createConfig = (): Config => {
repository: "goauthentik/authentik",
// Only replace issues and PR links
buildUrl: (values: BuildUrlValues) => {
return values.type === "issue" ||
values.type === "mention"
return values.type === "issue" || values.type === "mention"
? defaultBuildUrl(values)
: false;
},
@ -138,14 +137,19 @@ const createConfig = (): Config => {
],
},
theme: {
customCss: require.resolve(
"@goauthentik/docusaurus-config/css/index.css",
),
customCss: require.resolve("@goauthentik/docusaurus-config/css/index.css"),
},
} satisfies Preset.Options,
],
],
plugins: [
[
"./releases/plugin.mjs",
{
docsDirectory: path.join(__dirname, "docs"),
staticDirectory: "static",
},
],
[
"@docusaurus/plugin-content-docs",
{
@ -153,8 +157,7 @@ const createConfig = (): Config => {
path: "integrations",
routeBasePath: "integrations",
sidebarPath: "./sidebarsIntegrations.js",
editUrl:
"https://github.com/goauthentik/authentik/edit/main/website/",
editUrl: "https://github.com/goauthentik/authentik/edit/main/website/",
},
],
[

View File

@ -4,8 +4,8 @@ sidebar_label: Actual Budget
support_level: community
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
## What is Actual Budget

View File

@ -4,8 +4,8 @@ sidebar_label: Apache Guacamole™
support_level: authentik
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
## What is Apache Guacamole™

View File

@ -21,8 +21,8 @@ The following placeholders are used in this guide:
This documentation lists only the settings that you need to change from their default values. Be aware that any changes other than those explicitly mentioned in this guide could cause issues accessing your application.
:::
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs>
<TabItem value="iam" label="Classic IAM" default>

View File

@ -3,8 +3,8 @@ title: Integrate with BookStack
sidebar_label: BookStack
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
# Integrate with BookStack

View File

@ -29,8 +29,8 @@ This documentation lists only the settings that you need to change from their de
There are two ways to configure single sign-on for GitLab. You can configure it via SAML authentication or via OpenID Connect.
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs
defaultValue="saml"

View File

@ -101,8 +101,8 @@ resource "authentik_group" "grafana_viewers" {
## Grafana configuration
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs
defaultValue="docker"

View File

@ -3,8 +3,8 @@ title: Integrate with Applications
sidebar_label: Applications
---
import DocCardList from "@theme/DocCardList";
import SupportBadge from "@site/src/components/SupportBadge";
import DocCardList from "@theme/DocCardList";
# Applications

View File

@ -49,6 +49,8 @@ Paste the following block in your `local.yaml` file, after replacing the placeho
To get the value for `x509cert`, go to _System_ > _Certificates_, and download the public Signing Certificate. To avoid further problems, concat it into "string format" using e.g.: https://www.samltool.com/format_x509cert.php
<!-- prettier-ignore-start -->
```yaml
# Optionally add this for docker debug-logging
# monolog:
@ -127,4 +129,6 @@ kimai:
url: "https://kimai.company"
```
<!-- prettier-ignore-end -->
Afterwards, either [rebuild the cache](https://www.kimai.org/documentation/cache.html) or restart the docker container.

View File

@ -30,8 +30,8 @@ This documentation lists only the settings that you need to change from their de
It is possible to configure Nextcloud to use either OpenID Connect or SAML for authentication. Below are the steps to configure both methods.
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs
defaultValue="oidc"

View File

@ -41,8 +41,8 @@ To support the integration of Paperless-ngx with authentik, you need to create a
## Paperless Configuration
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs
defaultValue="docker"

View File

@ -42,8 +42,8 @@ To support the integration of Wekan with authentik, you need to create an applic
## Wekan configuration
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
<Tabs
defaultValue="docker"

8664
website/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +1,27 @@
{
"name": "@goauthentik/website-docs",
"name": "@goauthentik/docs",
"version": "0.0.0",
"private": true,
"license": "MIT",
"private": true,
"scripts": {
"build": "cp ../docker-compose.yml static/docker-compose.yml && cp ../schema.yml static/schema.yml && docusaurus gen-api-docs all && cross-env NODE_OPTIONS='--max_old_space_size=65536' docusaurus build",
"build-bundled": "cp ../schema.yml static/schema.yml && docusaurus gen-api-docs all && cross-env NODE_OPTIONS='--max_old_space_size=65536' docusaurus build",
"build:docker": "cp ../docker-compose.yml ./static/docker-compose.yml",
"build:schema": "cp -f ../schema.yml ./static/schema.yml",
"build:api": "docusaurus gen-api-docs all",
"build:docusaurus": "cross-env NODE_OPTIONS='--max_old_space_size=65536' docusaurus build",
"build-bundled": "run-s build:schema build:api build:docusaurus",
"build": "run-s build:docker build:schema build:api build:docusaurus",
"deploy": "docusaurus deploy",
"docusaurus": "docusaurus",
"lint:lockfile": "wireit",
"lint:lockfile": "echo 'Skipping lockfile linting'",
"prettier": "prettier --write .",
"prettier-check": "prettier --check .",
"start": "docusaurus start",
"serve": "docusaurus serve",
"swizzle": "docusaurus swizzle",
"test": "node --test",
"watch": "cp -f ../schema.yml ./static/schema.yml && docusaurus gen-api-docs all && docusaurus start"
"watch": "run-s build:schema build:api start"
},
"dependencies": {
"@goauthentik/docusaurus-config": "^1.0.4",
"semver": "^7.7.1",
"@docusaurus/core": "^3.7.0",
"@docusaurus/faster": "^3.7.0",
"@docusaurus/plugin-client-redirects": "^3.7.0",
@ -26,20 +29,52 @@
"@docusaurus/preset-classic": "^3.7.0",
"@docusaurus/theme-common": "^3.7.0",
"@docusaurus/theme-mermaid": "^3.7.0",
"@goauthentik/docusaurus-config": "^1.0.6",
"@goauthentik/tsconfig": "^1.0.4",
"@mdx-js/react": "^3.1.0",
"clsx": "^2.1.1",
"disqus-react": "^1.1.6",
"docusaurus-plugin-openapi-docs": "4.3.4",
"docusaurus-theme-openapi-docs": "4.3.4",
"docusaurus-plugin-openapi-docs": "^4.3.7",
"docusaurus-theme-openapi-docs": "^4.3.7",
"postcss": "^8.5.3",
"prism-react-renderer": "^2.4.1",
"react": "^18.3.1",
"react-before-after-slider-component": "^1.1.8",
"react-dom": "^18.3.1",
"react-feather": "^2.0.10",
"react-toggle": "^4.1.3",
"remark-directive": "^4.0.0",
"remark-github": "^12.0.0"
"remark-github": "^12.0.0",
"semver": "^7.7.1"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.7.0",
"@docusaurus/tsconfig": "^3.7.0",
"@docusaurus/types": "^3.7.0",
"@goauthentik/prettier-config": "^1.0.4",
"@types/lodash": "^4.17.16",
"@types/postman-collection": "^3.5.10",
"@types/react": "^18.3.13",
"@types/semver": "^7.7.0",
"cross-env": "^7.0.3",
"fast-glob": "^3.3.3",
"npm-run-all": "^4.1.5",
"prettier": "^3.5.3",
"typescript": "~5.8.2"
},
"optionalDependencies": {
"@rspack/binding-darwin-arm64": "1.3.8",
"@rspack/binding-linux-arm64-gnu": "1.3.8",
"@rspack/binding-linux-x64-gnu": "1.3.8",
"@swc/core-darwin-arm64": "1.11.24",
"@swc/core-linux-arm64-gnu": "1.11.24",
"@swc/core-linux-x64-gnu": "1.11.24",
"@swc/html-darwin-arm64": "1.11.24",
"@swc/html-linux-arm64-gnu": "1.11.24",
"@swc/html-linux-x64-gnu": "1.11.24",
"lightningcss-darwin-arm64": "1.29.3",
"lightningcss-linux-arm64-gnu": "1.29.3",
"lightningcss-linux-x64-gnu": "1.29.3"
},
"engines": {
"node": ">=22.14.0"
},
"browserslist": {
"production": [
@ -53,39 +88,16 @@
"last 1 safari version"
]
},
"devDependencies": {
"@types/semver": "^7.7.0",
"@docusaurus/module-type-aliases": "^3.3.2",
"@docusaurus/tsconfig": "^3.7.0",
"@docusaurus/types": "^3.3.2",
"@types/react": "^18.3.13",
"cross-env": "^7.0.3",
"prettier": "3.5.3",
"typescript": "~5.8.3",
"wireit": "^0.14.12"
},
"optionalDependencies": {
"@rspack/binding-darwin-arm64": "1.3.8",
"@rspack/binding-linux-arm64-gnu": "1.3.8",
"@rspack/binding-linux-x64-gnu": "1.3.8",
"lightningcss-darwin-arm64": "1.29.3",
"lightningcss-linux-arm64-gnu": "1.29.3",
"lightningcss-linux-x64-gnu": "1.29.3",
"@swc/core-darwin-arm64": "1.11.24",
"@swc/core-linux-arm64-gnu": "1.11.24",
"@swc/core-linux-x64-gnu": "1.11.24",
"@swc/html-darwin-arm64": "1.11.24",
"@swc/html-linux-arm64-gnu": "1.11.24",
"@swc/html-linux-x64-gnu": "1.11.24"
},
"wireit": {
"lint:lockfile": {
"__comment": "The lockfile-lint package does not have an option to ensure resolved hashes are set everywhere",
"shell": true,
"command": "[ -z \"$(jq -r '.packages | to_entries[] | select((.key | contains(\"node_modules\")) and (.value | has(\"resolved\") | not)) | .key' < package-lock.json)\" ]"
"prettier": "@goauthentik/prettier-config",
"overrides": {
"postman-collection": {
"@faker-js/faker": "^6.3.1"
},
"webpack-dev-server": {
"rimraf": "6.0.1"
},
"fork-ts-checker-webpack-plugin": {
"glob": "^11.0.1"
}
},
"engines": {
"node": ">=20"
}
}

View File

@ -0,0 +1,67 @@
/**
* @file Sidebar utilities.
*
* @todo This needs a revision to better align with Docusaurus's components.
*/
const html = String.raw;
/**
*
* @param {string[]} releases
* @returns
*/
export function generateVersionDropdown(releases) {
return html`<div
class="navbar__item dropdown dropdown--hoverable dropdown--right psuedo-dropdown"
>
<style>
.psuedo-dropdown .navbar__link.menu__link {
display: flex;
width: 100%;
justify-content: space-between;
font-weight: var(--ifm-font-weight-semibold);
}
.psuedo-dropdown .navbar__link.menu__link::after {
color: var(--ifm-color-emphasis-400);
filter: var(--ifm-menu-link-sublist-icon-filter);
margin-inline-end: calc(var(--ifm-navbar-padding-horizontal) * 0.5);
}
.psuedo-dropdown .dropdown__menu {
background: var(--ifm-dropdown-background-color);
box-shadow: var(--ifm-global-shadow-lw);
border: 1px solid var(--ifm-color-emphasis-200);
}
</style>
<div
aria-haspopup="true"
aria-expanded="false"
role="button"
class="navbar__link menu__link"
>
Version: Pre-Release
</div>
<ul class="dropdown__menu menu__list-item--collapsed">
${releases
.map((semver, idx) => {
const subdomain = `version-${semver.replace(".", "-")}`;
const label = `Version: ${semver}${idx === 0 ? " (Current)" : ""}`;
return html`<li>
<a
href="https://${subdomain}.goauthentik.io/docs"
target="_blank"
rel="noopener noreferrer"
class="dropdown__link menu__link"
>${label}</a
>
</li>`;
})
.join("")}
</ul>
</div>
<hr />`;
}

View File

@ -0,0 +1,46 @@
/**
* @file Docusaurus releases plugin.
*
* @import { LoadContext, Plugin } from "@docusaurus/types"
*/
import * as fs from "node:fs/promises";
import * as path from "node:path";
import { collectReleaseFiles } from "./utils.mjs";
const RELEASES_FILENAME = "releases.gen.json";
/**
* @typedef {object} ReleasesPluginOptions
* @property {string} docsDirectory
* @property {string} staticDirectory
*/
/**
* @param {LoadContext} _context
* @param {ReleasesPluginOptions} options
* @returns {Promise<Plugin>}
*/
async function releasesPlugin(_context, { docsDirectory, staticDirectory }) {
if (!staticDirectory) {
throw new Error("releases-plugin: staticDirectory is required");
}
return {
name: "releases-plugin",
async loadContent() {
console.log("🚀 releases-plugin loaded");
const releases = collectReleaseFiles(docsDirectory).map((release) => release.name);
const outputPath = path.join(staticDirectory, RELEASES_FILENAME);
await fs.mkdir(path.dirname(outputPath), { recursive: true });
await fs.writeFile(outputPath, JSON.stringify(releases, null, 2), "utf-8");
console.log(`${RELEASES_FILENAME} generated`);
},
};
}
export default releasesPlugin;

View File

@ -0,0 +1,57 @@
/**
* @file Docusaurus release utils.
*/
import FastGlob from "fast-glob";
import * as path from "node:path";
/**
*
* @param {string} releasesParentDirectory
* @returns {FastGlob.Entry[]}
*/
export function collectReleaseFiles(releasesParentDirectory) {
const releaseFiles = FastGlob.sync("releases/**/v*.{md,mdx}", {
cwd: releasesParentDirectory,
onlyFiles: true,
objectMode: true,
})
.map((fileEntry) => {
return {
...fileEntry,
path: fileEntry.path.replace(/\.mdx?$/, ""),
name: fileEntry.name.replace(/^v/, "").replace(/\.mdx?$/, ""),
};
})
.sort((a, b) => b.name.localeCompare(a.name));
return releaseFiles;
}
export const SUPPORTED_RELEASE_COUNT = 3;
/**
*
* @param {FastGlob.Entry[]} releaseFiles
*/
export function createReleaseSidebarEntries(releaseFiles) {
/**
* @type {any[]}
*/
let sidebarEntries = releaseFiles.map((fileEntry) => {
return path.join(fileEntry.path);
});
if (releaseFiles.length > SUPPORTED_RELEASE_COUNT) {
// Then we add the rest of the releases as a category.
sidebarEntries = [
...sidebarEntries.slice(0, SUPPORTED_RELEASE_COUNT),
{
type: "category",
label: "Previous versions",
items: sidebarEntries.slice(SUPPORTED_RELEASE_COUNT),
},
];
}
return sidebarEntries;
}

View File

@ -1,15 +1,19 @@
import "mdast-util-to-hast";
import "mdast-util-directive";
/**
* @file Remark plugin to transform `ak-preview` directives into preview badges.
*
* @import { Root } from "mdast";
*/
import { h } from "hastscript";
import { Root } from "mdast";
import { visit, SKIP } from "unist-util-visit";
import { SKIP, visit } from "unist-util-visit";
/**
* MDAST plugin to transform `ak-preview` directives into preview badges.
*/
function remarkPreviewDirective() {
return function (tree: Root) {
/**
* @param {Root} tree The MDAST tree to transform.
*/
return function (tree) {
visit(tree, "textDirective", function (node) {
if (node.name !== "ak-preview") return SKIP;
@ -17,10 +21,10 @@ function remarkPreviewDirective() {
const hast = h("span", {
...node.attributes,
className: "badge badge--preview",
title: `This feature is in preview and may change in the future.`,
"className": "badge badge--preview",
"title": `This feature is in preview and may change in the future.`,
"aria-description": "Preview badge",
role: "img",
"role": "img",
});
data.hName = hast.tagName;

View File

@ -1,30 +1,35 @@
import "mdast-util-to-hast";
import "mdast-util-directive";
/**
* @file Remark plugin to transform `ak-support` directives into support level badges.
*
* @import { Root } from "mdast";
*/
import { h } from "hastscript";
import { Root } from "mdast";
import { visit, SKIP } from "unist-util-visit";
import { coerce } from "semver";
import { SKIP, visit } from "unist-util-visit";
/**
* Support levels for authentik.
* @typedef {"authentik" | "community" | "vendor" | "deprecated"} SupportLevel
*/
export type SupportLevel = "authentik" | "community" | "vendor" | "deprecated";
/**
* Mapping of support levels to badge classes.
*
* @satisfies {Record<SupportLevel, string>}
*/
export const SupportLevelToLabel = {
export const SupportLevelToLabel = /** @type {const} */ ({
authentik: "authentik",
community: "Community",
vendor: "Vendor",
deprecated: "Deprecated",
} as const satisfies Record<SupportLevel, string>;
});
/**
* Type-predicate to determine if a string is a known support level.
*
* @param {string} input The string to check.
* @return {input is SupportLevel} True if the string is a known support level.
*/
export function isSupportLevel(input: string): input is SupportLevel {
export function isSupportLevel(input) {
return Object.hasOwn(SupportLevelToLabel, input);
}
@ -32,7 +37,10 @@ export function isSupportLevel(input: string): input is SupportLevel {
* MDAST plugin to transform `ak-support` directives into preview badges.
*/
function remarkSupportDirective() {
return function (tree: Root) {
/**
* @param {Root} tree The MDAST tree to transform.
*/
return function (tree) {
visit(tree, "textDirective", function (node) {
if (node.name !== "ak-support") return SKIP;
@ -52,10 +60,10 @@ function remarkSupportDirective() {
const hast = h("span", {
...node.attributes,
className: `badge badge--support-${level}`,
title: `This feature is supported at the ${label} level.`,
"className": `badge badge--support-${level}`,
"title": `This feature is supported at the ${label} level.`,
"aria-description": "Support level badge",
role: "img",
"role": "img",
});
data.hName = hast.tagName;

View File

@ -1,10 +1,11 @@
import "mdast-util-to-hast";
import "mdast-util-directive";
/**
* @file Remark plugin to transform `ak-version` directives into version badges.
*
* @import { Root } from "mdast";
*/
import { h } from "hastscript";
import { Root } from "mdast";
import { visit, SKIP } from "unist-util-visit";
import { coerce } from "semver";
import { SKIP, visit } from "unist-util-visit";
/**
* MDAST plugin to transform `ak-version` directives into version badges.
@ -22,7 +23,10 @@ import { coerce } from "semver";
* ```
*/
function remarkVersionDirective() {
return function (tree: Root) {
/**
* @param {Root} tree The MDAST tree to transform.
*/
return function (tree) {
visit(tree, "textDirective", function (node) {
if (node.name !== "ak-version") return SKIP;
@ -40,19 +44,17 @@ function remarkVersionDirective() {
const yearCutoff = new Date().getFullYear() - 2;
if (parsed.major <= yearCutoff) {
throw new Error(
`Semver version <= ${yearCutoff} is not supported: ${semver}`,
);
throw new Error(`Semver version <= ${yearCutoff} is not supported: ${semver}`);
}
const data = node.data || (node.data = {});
const hast = h("span", {
...node.attributes,
className: "badge badge--version",
title: `Available in authentik ${parsed.format()} and later`,
"className": "badge badge--version",
"title": `Available in authentik ${parsed.format()} and later`,
"aria-description": "Version badge",
role: "img",
"role": "img",
});
data.hName = hast.tagName;

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,8 @@
module.exports = {
/**
* @file Sidebar configuration for the authentik integrations.
*/
const sidebarsIntegrations = {
integrations: [
{
type: "doc",
@ -206,3 +210,5 @@ module.exports = {
},
],
};
export default sidebarsIntegrations;

View File

@ -1,8 +1,5 @@
import { SupportLevelToLabel, isSupportLevel } from "@site/remark/support-directive.mjs";
import React from "react";
import {
isSupportLevel,
SupportLevelToLabel,
} from "@site/remark/support-directive";
export interface SupportBadgeProps {
level: string;

View File

@ -18,9 +18,7 @@ export const VersionBadge: React.FC<AuthentikVersionProps> = ({ semver }) => {
const yearCutoff = new Date().getFullYear() - 2;
if (parsed.major <= yearCutoff) {
throw new Error(
`Semver version <= ${yearCutoff} is not supported: ${semver}`,
);
throw new Error(`Semver version <= ${yearCutoff} is not supported: ${semver}`);
}
return (

View File

@ -14,8 +14,7 @@ import { useDoc } from "@docusaurus/plugin-content-docs/client";
*/
export function useSyntheticTitle(): string | null {
const { metadata, frontMatter, contentTitle } = useDoc();
const shouldRender =
!frontMatter.hide_title && typeof contentTitle === "undefined";
const shouldRender = !frontMatter.hide_title && typeof contentTitle === "undefined";
if (!shouldRender) {
return null;
}

View File

@ -1,5 +1,5 @@
import React from "react";
import { Redirect } from "@docusaurus/router";
import React from "react";
function Home() {
return <Redirect to="/docs" />;

View File

@ -1,3 +1,4 @@
/// <reference types="@docusaurus/plugin-content-docs" />
/**
* @file Swizzled DocItemContent component.
*
@ -7,21 +8,17 @@
* the content of a documentation page. However, it also adds support for
* support badges, and Authentik version badges.
*/
import React from "react";
import clsx from "clsx";
import { ThemeClassNames } from "@docusaurus/theme-common";
import {
DocContextValue,
useDoc,
} from "@docusaurus/plugin-content-docs/client";
import Heading from "@theme/Heading";
import MDXContent from "@theme/MDXContent";
import type { Props } from "@theme/DocItem/Content";
import { DocFrontMatter } from "@docusaurus/plugin-content-docs";
import { useSyntheticTitle } from "@site/src/hooks/title";
import { DocContextValue, useDoc } from "@docusaurus/plugin-content-docs/client";
import { ThemeClassNames } from "@docusaurus/theme-common";
import { SupportBadge } from "@site/src/components/SupportBadge";
import { VersionBadge } from "@site/src/components/VersionBadge";
import { useSyntheticTitle } from "@site/src/hooks/title";
import type { Props } from "@theme/DocItem/Content";
import Heading from "@theme/Heading";
import MDXContent from "@theme/MDXContent";
import clsx from "clsx";
import React from "react";
interface SwizzledDocFrontMatter extends DocFrontMatter {
support_level?: string;
@ -37,12 +34,8 @@ interface SwizzledDocContextValue extends DocContextValue {
const DocItemContent: React.FC<Props> = ({ children }) => {
const syntheticTitle = useSyntheticTitle();
const { frontMatter } = useDoc() as SwizzledDocContextValue;
const {
support_level,
authentik_version,
authentik_enterprise,
authentik_preview,
} = frontMatter;
const { support_level, authentik_version, authentik_enterprise, authentik_preview } =
frontMatter;
const badges: JSX.Element[] = [];
@ -71,9 +64,7 @@ const DocItemContent: React.FC<Props> = ({ children }) => {
{badges.length ? (
<p className="badge-group">
{badges.map((badge, index) => (
<React.Fragment key={index}>
{badge}
</React.Fragment>
<React.Fragment key={index}>{badge}</React.Fragment>
))}
</p>
) : null}

View File

@ -1,13 +1,12 @@
import React, { type ReactNode } from "react";
import clsx from "clsx";
import EditThisPage from "@theme/EditThisPage";
import type { Props } from "@theme/EditMetaRow";
import LastUpdated from "@theme/LastUpdated";
import Admonition from "@theme/Admonition";
import styles from "./styles.module.css";
import Translate from "@docusaurus/Translate";
import IconNote from "@theme/Admonition/Icon/Note";
import Admonition from "@theme/Admonition";
import type { Props } from "@theme/EditMetaRow";
import EditThisPage from "@theme/EditThisPage";
import LastUpdated from "@theme/LastUpdated";
import clsx from "clsx";
import React from "react";
import styles from "./styles.module.css";
const EditMetaRow: React.FC<Props> = ({
className,
@ -40,22 +39,20 @@ const EditMetaRow: React.FC<Props> = ({
id="theme.common.contributor.footerDescription1"
description="The description for the contribution footer"
>
We welcome your knowledge and expertise. If you see
areas of the documentation that you can improve (fix a
typo, correct a technical detail, add additional
context, etc.) we would really appreciate your
contribution.
We welcome your knowledge and expertise. If you see areas of the
documentation that you can improve (fix a typo, correct a technical detail,
add additional context, etc.) we would really appreciate your contribution.
</Translate>
</p>
<div className="row">
<div className="col col--12">
<ul>
{editUrl && (
{editUrl ? (
<li>
<EditThisPage editUrl={editUrl} />
</li>
)}
) : null}
<li>
<a
@ -108,12 +105,9 @@ const EditMetaRow: React.FC<Props> = ({
<div className="row">
<div className={clsx("col", styles.lastUpdated)}>
{(lastUpdatedAt || lastUpdatedBy) && (
<LastUpdated
lastUpdatedAt={lastUpdatedAt}
lastUpdatedBy={lastUpdatedBy}
/>
)}
{lastUpdatedAt || lastUpdatedBy ? (
<LastUpdated lastUpdatedAt={lastUpdatedAt} lastUpdatedBy={lastUpdatedBy} />
) : null}
</div>
</div>
</>

View File

@ -52,8 +52,7 @@ html[data-theme="dark"] {
display: flex;
gap: var(--ifm-spacing-horizontal);
justify-content: center;
margin: calc(var(--ifm-spacing-horizontal) * 2) 0
var(--ifm-spacing-horizontal);
margin: calc(var(--ifm-spacing-horizontal) * 2) 0 var(--ifm-spacing-horizontal);
padding: 0;
}

View File

@ -1,8 +1,8 @@
import React, { type ReactNode } from "react";
import Link from "@docusaurus/Link";
import Translate from "@docusaurus/Translate";
import { ThemeClassNames } from "@docusaurus/theme-common";
import Link from "@docusaurus/Link";
import type { Props } from "@theme/EditThisPage";
import React, { type ReactNode } from "react";
export default function EditThisPage({ editUrl }: Props): ReactNode {
return (

View File

@ -1,27 +0,0 @@
export function generateVersionDropdown(allReleases) {
const releases = allReleases.filter(
(release) => typeof release === "string",
);
const latest = releases[0].replace(/releases\/\d+\/v/, "");
return `<div class="navbar__item dropdown dropdown--hoverable dropdown--right">
<div aria-haspopup="true" aria-expanded="false" role="button" class="navbar__link menu__link">
Version: ${latest}
</div>
<ul class="dropdown__menu">
${releases
.map((release) => {
const version = release.replace(/releases\/\d+\/v/, "");
const subdomain = `version-${version.replace(".", "-")}`;
const label = `Version: ${version}`;
return `<li>
<a
href="https://${subdomain}.goauthentik.io/docs"
target="_blank" rel="noopener noreferrer"
class="dropdown__link">${label}</a>
</li>`;
})
.join("")}
</ul>
</div>
<hr>`;
}

View File

@ -1,34 +1,54 @@
import test from "node:test";
/**
* @file Test suite for the sidebar configuration of the authentik integrations.
*
* @todo Enforce types.
*/
import FastGlob from "fast-glob";
import assert from "node:assert";
import test from "node:test";
import sidebar from "../sidebarsIntegrations.js";
import glob from "glob";
const getSidebarItems = () => {
/**
* @type {any[]}
*/
const allItems = [];
/**
*
* @param {any} category
*/
const mapper = (category) => {
if (!category.items) {
return;
}
category.items.forEach((item) => {
if (item.constructor === String) {
allItems.push(item);
} else {
mapper(item);
}
});
category.items.forEach(
/**
*
* @param {any} item
*/
(item) => {
if (typeof item === "string") {
allItems.push(item);
} else {
mapper(item);
}
},
);
};
sidebar.integrations.forEach(mapper);
return allItems.sort();
};
test("ensure all services have a sidebar entry", (t) => {
test("ensure all services have a sidebar entry", (_t) => {
// All services in the sidebar
const services = getSidebarItems()
.filter((entry) => entry.startsWith("services/"))
.map((entry) => entry.replace("/index", ""))
.map((entry) => entry.replace("services/", ""));
const servicesFiles = glob
.sync("integrations/**/*.+(md|mdx)")
const servicesFiles = FastGlob.sync("integrations/**/*.+(md|mdx)")
.filter((entry) => entry.startsWith("integrations/services/"))
.map((entry) => entry.replace("integrations/services/", ""))
.map((entry) => entry.replace(/\/index\.mdx?/, ""))
@ -39,19 +59,19 @@ test("ensure all services have a sidebar entry", (t) => {
});
});
test("ensure all sources have a sidebar entry", (t) => {
test("ensure all sources have a sidebar entry", (_t) => {
// All sources in the sidebar
const sources = getSidebarItems()
.filter((entry) => entry.startsWith("sources/"))
.map((entry) => entry.replace("/index", ""))
.map((entry) => entry.replace("sources/", ""));
const sourceFiles = glob
.sync("integrations/**/*.+(md|mdx)")
const sourceFiles = FastGlob.sync("integrations/**/*.+(md|mdx)")
.filter((entry) => entry.startsWith("integrations/sources/"))
.map((entry) => entry.replace("integrations/sources/", ""))
.map((entry) => entry.replace(/\/index\.mdx?/, ""))
.map((entry) => entry.replace(".md", ""))
.sort();
sourceFiles.forEach((file, idx) => {
assert.strictEqual(file, sources[idx]);
});

View File

@ -1,8 +1,32 @@
// @file TSConfig used by the docs package during development.
//
// @remarks
// While this configuration will influence the IDE experience,
// Docusaurus will instead use an internal configuration to build the site.
//
// @see https://docusaurus.io/docs/typescript-support
{
// This file is not used in compilation. It is here just for a nice editor experience.
"extends": "@docusaurus/tsconfig",
"extends": "@goauthentik/tsconfig",
"compilerOptions": {
"baseUrl": "."
"noEmit": true,
"checkJs": true,
"allowJs": true,
"composite": false,
"esModuleInterop": true,
"moduleResolution": "bundler",
"module": "esnext",
"jsx": "preserve",
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"baseUrl": ".",
"paths": {
"@site/*": ["./*"]
}
},
"exclude": [".docusaurus", "build", "node_modules"]
"exclude": [
// ---
"./out/**/*",
"./dist/**/*",
".docusaurus",
"build"
]
}

21
website/types/docusaurus.d.ts vendored Normal file
View File

@ -0,0 +1,21 @@
/**
* @file Supplemental type definitions for Docusaurus.
*
* @remarks
*
* Docusaurus uses an unconventional module resolution strategy, which can lead to
* issues when using TypeScript.
*
* The types in this file are intended to expose less visible types to TypeScript's
* project references, allowing for better type checking and autocompletion.
*/
declare module "@docusaurus/plugin-content-docs-types" {
export * from "@docusaurus/plugin-content-docs";
export * from "@docusaurus/plugin-content-docs/src/types.ts";
export * from "@docusaurus/plugin-content-docs/src/sidebars/types.ts";
}
declare module "@docusaurus/plugin-content-docs/src/sidebars/types" {
export * from "@docusaurus/plugin-content-docs/src/sidebars/types.ts";
}