refactor: 📝 Move documentation to a custom VitePress site

This commit is contained in:
Jesse Wierzbinski 2024-11-10 15:24:34 +01:00
parent 19d8680289
commit 06a8dd1c0a
No known key found for this signature in database
26 changed files with 1449 additions and 775 deletions

5
.gitignore vendored
View file

@ -183,3 +183,8 @@ config/extended_description_test.md
oclif.manifest.json
.direnv/
tsconfig.tsbuildinfo
# Vitepress Docs
*/.vitepress/dist
*/.vitepress/cache

View file

@ -73,8 +73,8 @@ const route = createRoute({
},
},
responses: {
200: {
description: "uploaded emoji",
201: {
description: "Uploaded emoji",
content: {
"application/json": {
schema: Emoji.schema,
@ -173,6 +173,6 @@ export default apiRoute((app) =>
alt,
});
return context.json(emoji.toApi(), 200);
return context.json(emoji.toApi(), 201);
}),
);

View file

@ -1,7 +1,7 @@
import type { Status as ApiStatus } from "@versia/client/types";
import { fakeRequest, getTestStatuses, getTestUsers } from "~/tests/utils";
import { run, bench } from "mitata";
import { configureLoggers } from "@/loggers";
import type { Status as ApiStatus } from "@versia/client/types";
import { bench, run } from "mitata";
import { fakeRequest, getTestStatuses, getTestUsers } from "~/tests/utils";
await configureLoggers(true);

View file

@ -91,6 +91,6 @@
"globals": ["Bun", "HTMLRewriter", "BufferEncoding"]
},
"files": {
"ignore": ["node_modules", "dist"]
"ignore": ["node_modules", "dist", "cache"]
}
}

BIN
bun.lockb

Binary file not shown.

View file

@ -23,6 +23,7 @@ type EmojiWithInstance = InferSelectModel<typeof Emojis> & {
export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
public static schema = z.object({
id: z.string(),
shortcode: z.string(),
url: z.string(),
visible_in_picker: z.boolean(),
@ -186,9 +187,8 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
);
}
public toApi(): ApiEmoji {
public toApi(): ApiEmoji & { id: string } {
return {
// @ts-expect-error ID is not in regular Mastodon API
id: this.id,
shortcode: this.data.shortcode,
static_url: proxyUrl(this.data.url) ?? "", // TODO: Add static version

105
docs/.vitepress/config.ts Normal file
View file

@ -0,0 +1,105 @@
import taskLists from "@hackmd/markdown-it-task-lists";
import implicitFigures from "markdown-it-image-figures";
import { defineConfig } from "vitepress";
import { tabsMarkdownPlugin } from "vitepress-plugin-tabs";
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "Versia Server Docs",
lang: "en-US",
description: "Documentation for Versia Server APIs",
markdown: {
config: (md): void => {
md.use(implicitFigures, {
figcaption: "alt",
copyAttrs: "^class$",
});
md.use(taskLists);
md.use(tabsMarkdownPlugin);
},
math: true,
},
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav: [
{ text: "Home", link: "/" },
{
text: "Versia Protocol",
link: "https://versia.pub",
target: "_blank",
},
],
sidebar: [
{
text: "Setup",
items: [
{
text: "Installation",
link: "/setup/installation",
},
{
text: "Database",
link: "/setup/database",
},
],
},
{
text: "CLI",
link: "/cli",
},
{
text: "API",
items: [
{
text: "Emojis",
link: "/api/emojis",
},
{
text: "Roles",
link: "/api/roles",
},
{
text: "Challenges",
link: "/api/challenges",
},
{
text: "SSO",
link: "/api/sso",
},
{
text: "Mastodon Extensions",
link: "/api/mastodon",
},
],
},
{
text: "Frontend",
items: [
{
text: "Authentication",
link: "/frontend/auth",
},
{
text: "Routes",
link: "/frontend/routes",
},
],
},
],
socialLinks: [
{ icon: "github", link: "https://github.com/versia-pub/server" },
],
search: {
provider: "local",
},
logo: "https://cdn.versia.pub/branding/icon.svg",
},
head: [["link", { rel: "icon", href: "/favicon.png", type: "image/png" }]],
titleTemplate: ":title • Versia Server Docs",
});

View file

@ -0,0 +1,14 @@
import type { Theme } from "vitepress";
import DefaultTheme from "vitepress/theme";
// https://vitepress.dev/guide/custom-theme
import { type VNode, h } from "vue";
import "./style.css";
export default {
extends: DefaultTheme,
Layout: (): VNode => {
return h(DefaultTheme.Layout, null, {
// https://vitepress.dev/guide/extending-default-theme#layout-slots
});
},
} satisfies Theme;

View file

@ -0,0 +1,138 @@
/**
* Customize default theme styling by overriding CSS variables:
* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css
*/
/**
* Colors
*
* Each colors have exact same color scale system with 3 levels of solid
* colors with different brightness, and 1 soft color.
*
* - `XXX-1`: The most solid color used mainly for colored text. It must
* satisfy the contrast ratio against when used on top of `XXX-soft`.
*
* - `XXX-2`: The color used mainly for hover state of the button.
*
* - `XXX-3`: The color for solid background, such as bg color of the button.
* It must satisfy the contrast ratio with pure white (#ffffff) text on
* top of it.
*
* - `XXX-soft`: The color used for subtle background such as custom container
* or badges. It must satisfy the contrast ratio when putting `XXX-1` colors
* on top of it.
*
* The soft color must be semi transparent alpha channel. This is crucial
* because it allows adding multiple "soft" colors on top of each other
* to create a accent, such as when having inline code block inside
* custom containers.
*
* - `default`: The color used purely for subtle indication without any
* special meanings attached to it such as bg color for menu hover state.
*
* - `brand`: Used for primary brand colors, such as link text, button with
* brand theme, etc.
*
* - `tip`: Used to indicate useful information. The default theme uses the
* brand color for this by default.
*
* - `warning`: Used to indicate warning to the users. Used in custom
* container, badges, etc.
*
* - `danger`: Used to show error, or dangerous message to the users. Used
* in custom container, badges, etc.
* -------------------------------------------------------------------------- */
:root {
--vp-c-default-1: var(--vp-c-gray-1);
--vp-c-default-2: var(--vp-c-gray-2);
--vp-c-default-3: var(--vp-c-gray-3);
--vp-c-default-soft: var(--vp-c-gray-soft);
--vp-c-brand-1: var(--vp-c-indigo-1);
--vp-c-brand-2: var(--vp-c-indigo-2);
--vp-c-brand-3: var(--vp-c-indigo-3);
--vp-c-brand-soft: var(--vp-c-indigo-soft);
--vp-c-tip-1: var(--vp-c-brand-1);
--vp-c-tip-2: var(--vp-c-brand-2);
--vp-c-tip-3: var(--vp-c-brand-3);
--vp-c-tip-soft: var(--vp-c-brand-soft);
--vp-c-warning-1: var(--vp-c-yellow-1);
--vp-c-warning-2: var(--vp-c-yellow-2);
--vp-c-warning-3: var(--vp-c-yellow-3);
--vp-c-warning-soft: var(--vp-c-yellow-soft);
--vp-c-danger-1: var(--vp-c-red-1);
--vp-c-danger-2: var(--vp-c-red-2);
--vp-c-danger-3: var(--vp-c-red-3);
--vp-c-danger-soft: var(--vp-c-red-soft);
}
/**
* Component: Button
* -------------------------------------------------------------------------- */
:root {
--vp-button-brand-border: transparent;
--vp-button-brand-text: var(--vp-c-white);
--vp-button-brand-bg: var(--vp-c-brand-3);
--vp-button-brand-hover-border: transparent;
--vp-button-brand-hover-text: var(--vp-c-white);
--vp-button-brand-hover-bg: var(--vp-c-brand-2);
--vp-button-brand-active-border: transparent;
--vp-button-brand-active-text: var(--vp-c-white);
--vp-button-brand-active-bg: var(--vp-c-brand-1);
}
/**
* Component: Home
* -------------------------------------------------------------------------- */
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(
120deg,
#e6a9fe 30%,
#bd34fe
);
--vp-home-hero-image-background-image: linear-gradient(
-45deg,
#e6a9fe 50%,
#bd34fe 50%
);
--vp-home-hero-image-filter: blur(44px);
}
@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(68px);
}
}
/**
* Component: Custom Block
* -------------------------------------------------------------------------- */
:root {
--vp-custom-block-tip-border: transparent;
--vp-custom-block-tip-text: var(--vp-c-text-1);
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
}
/**
* Component: Algolia
* -------------------------------------------------------------------------- */
.DocSearch {
--docsearch-primary-color: var(--vp-c-brand-1) !important;
}

View file

@ -8,6 +8,21 @@ This is a form of proof of work CAPTCHA, and should be mostly invisible to users
Challenges are powered by the [Altcha](https://altcha.org/) library. You may either reimplement their solution code (which is very simple), or use [`altcha-lib`](https://github.com/altcha-org/altcha-lib) to solve the challenges.
## Challenge
```typescript
type UUID = string;
interface Challenge {
id: UUID;
algorithm: "SHA-256" | "SHA-384" | "SHA-512";
challenge: string;
maxnumber?: number;
salt: string;
signature: string;
}
```
## Request Challenge
```http
@ -16,17 +31,32 @@ POST /api/v1/challenges
Generates a new challenge for the client to solve.
- **Returns:**: [`Challenge`](#challenge)
- **Authentication:**: Not required
- **Permissions:**: None
- **Version History**:
- `0.7.0`: Added.
### Example
```http
POST /api/v1/challenges
```
### Response
```ts
// 200 OK
#### `200 OK`
Challenge data.
```json
{
id: string,
algorithm: "SHA-256" | "SHA-384" | "SHA-512",
challenge: string;
maxnumber?: number;
salt: string;
signature: string;
"id":"01931621-1456-7b5b-be65-c044e6b47cbb",
"salt":"d15e43fa3709d85ce3c74644?challenge_id=01931621-1456-7b5b-be65-c044e6b47cbb&expires=1731243386",
"algorithm":"SHA-256",
"challenge":"5dc6b352632912664583940e14b9dfbdf447459d4517708ce8766a39ac040eb5",
"maxnumber":50000,
"signature":"22c3a687dc2500cbffcb022ae8474360d5c2f63a50ba376325c211bb2ca06b7f"
}
```
@ -52,4 +82,4 @@ A challenge solution is valid for 5 minutes (configurable) after the challenge i
If challenges are enabled, the following routes will require a challenge to be solved before the request can be made:
- `POST /api/v1/accounts`
Which routes require challenges may eventually be expanded or made configurable.
Routes requiring challenges may eventually be expanded or made configurable.

View file

@ -1,6 +1,24 @@
# Emoji API
An Emoji API is made available to users to manage custom emoji on the instance. We recommend using Versia Server's CLI to manage emoji, but this API is available for those who prefer to use it (both admin and non-admin users).
This API allows users to create, read, update, and delete instance custom emojis.
The **Versia Server CLI** can also be used to manage custom emojis.
## Emoji
```typescript
type UUID = string;
type URL = string;
interface Emoji {
id: UUID;
shortcode: string;
url: URL;
static_url?: URL;
visible_in_picker: boolean;
category?: string;
}
```
## Create Emoji
@ -8,29 +26,54 @@ An Emoji API is made available to users to manage custom emoji on the instance.
POST /api/v1/emojis
```
Creates a new custom emoji on the instance. If the user is an administrator, they can create global emoji that are visible to all users on the instance. Otherwise, the emoji will only be visible to the user who created it (in `/api/v1/custom_emojis`).
Upload a new custom emoji.
### Parameters
- **Returns:** [`Emoji`](#emoji)
- **Authentication:** Required
- **Permissions:** `owner:emoji`, or `emoji` if uploading a global emoji.
- **Version History**:
- `0.7.0`: Added.
- `Content-Type`: `multipart/form-data`, `application/json` or `application/x-www-form-urlencoded`. If uploading a file, use `multipart/form-data`.
### Request
- `shortcode`: string, required. The shortcode for the emoji. Must be 2-64 characters long and contain only alphanumeric characters, dashes, and underscores.
- `element`: string or file, required. The image file for the emoji. This can be a URL or a file upload.
- `alt`: string, optional. The alt text for the emoji. Defaults to the shortcode.
- `global`: boolean, optional. For administrators only. Whether the emoji should be visible to all users on the instance. Defaults to `false`.
- `category`: string, optional. The category for the emoji. Maximum 64 characters.
- `shortcode` (string, required): The shortcode for the emoji.
- 1-64 characters long, alphanumeric, and may contain dashes or underscores.
- `element` (file/string, required): The image file to upload.
- Can be a URL, or a file upload (`multipart/form-data`).
- `alt` (string): Emoji alt text.
- `category` (string): Emoji category. Can be any string up to 64 characters long.
- `global` (boolean): If set to `true`, the emoji will be visible to all users, not just the uploader.
- Requires `emoji` permission.
#### Example
```http
POST /api/v1/emojis
Content-Type: application/json
Authorization: Bearer ...
{
"shortcode": "blobfox-coffee",
"element": "https://example.com/blobfox-coffee.png",
"alt": "My emoji",
"category": "Blobmojis"
}
```
### Response
```ts
// 200 OK
#### `201 Created`
Emoji successfully uploaded.
```json
{
id: string,
shortcode: string,
url: string,
static_url: string,
visible_in_picker: boolean,
category: string | undefined,
"id": "f7b1c1b0-0b1b-4b1b-8b1b-0b1b1b1b1b1b",
"shortcode": "blobfox-coffee",
"url": "https://cdn.yourinstance.com/emojis/f7b1c1b0-0b1b-4b1b-8b1b-0b1b1b1b1b1b.png",
"static_url": "https://cdn.yourinstance.com/emojis/f7b1c1b0-0b1b-4b1b-8b1b-0b1b1b1b1b1b.png",
"visible_in_picker": true,
"category": "Blobmojis"
}
```
@ -40,19 +83,37 @@ Creates a new custom emoji on the instance. If the user is an administrator, the
GET /api/v1/emojis/:id
```
Retrieves information about a custom emoji on the instance.
Get a specific custom emoji.
- **Returns:** [`Emoji`](#emoji)
- **Authentication:** Required
- **Permissions:** `owner:emoji`, or `emoji` if viewing a global emoji.
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
GET /api/v1/emojis/f7b1c1b0-0b1b-4b1b-8b1b-0b1b1b1b1b1b
Authorization: Bearer ...
```
### Response
```ts
// 200 OK
#### `200 OK`
Custom emoji data.
```json
{
id: string,
shortcode: string,
url: string,
static_url: string,
visible_in_picker: boolean,
category: string | undefined,
"id": "f7b1c1b0-0b1b-4b1b-8b1b-0b1b1b1b1b1b",
"shortcode": "blobfox-coffee",
"url": "https://cdn.yourinstance.com/emojis/f7b1c1b0-0b1b-4b1b-8b1b-0b1b1b1b1b1b.png",
"static_url": "https://cdn.yourinstance.com/emojis/f7b1c1b0-0b1b-4b1b-8b1b-0b1b1b1b1b1b.png",
"visible_in_picker": true,
"category": "Blobmojis"
}
```
@ -62,29 +123,54 @@ Retrieves information about a custom emoji on the instance.
PATCH /api/v1/emojis/:id
```
Edits a custom emoji on the instance.
Edit an existing custom emoji.
### Parameters
- **Returns:** [`Emoji`](#emoji)
- **Authentication:** Required
- **Permissions:** `owner:emoji`, or `emoji` if editing a global emoji.
- **Version History**:
- `0.7.0`: Added.
- `Content-Type`: `application/json`, `multipart/form-data` or `application/x-www-form-urlencoded`. If uploading a file, use `multipart/form-data`.
### Request
- `shortcode`: string, optional. The new shortcode for the emoji. Must be 2-64 characters long and contain only alphanumeric characters, dashes, and underscores.
- `element`: string or file, optional. The new image file for the emoji. This can be a URL or a file upload.
- `alt`: string, optional. The new alt text for the emoji. Defaults to the shortcode.
- `global`: boolean, optional. For administrators only. Whether the emoji should be visible to all users on the instance. Defaults to `false`.
- `category`: string, optional. The new category for the emoji. Maximum 64 characters.
> [!NOTE]
> All fields are optional.
- `shortcode` (string): The shortcode for the emoji.
- 1-64 characters long, alphanumeric, and may contain dashes or underscores.
- `element` (file/string): The image file to upload.
- Can be a URL, or a file upload (`multipart/form-data`).
- `alt` (string): Emoji alt text.
- `category` (string): Emoji category. Can be any string up to 64 characters long.
- `global` (boolean): If set to `true`, the emoji will be visible to all users, not just the uploader.
- Requires `emoji` permission.
#### Example
```http
PATCH /api/v1/emojis/f7b1c1b0-0b1b-4b1b-8b1b-0b1b1b1b1b1b
Content-Type: application/json
Authorization: Bearer ...
{
"category": "Blobfoxes"
}
```
### Response
```ts
// 200 OK
#### `200 OK`
Emoji successfully edited.
```json
{
id: string,
shortcode: string,
url: string,
static_url: string,
visible_in_picker: boolean,
category: string | undefined,
"id": "f7b1c1b0-0b1b-4b1b-8b1b-0b1b1b1b1b1b",
"shortcode": "blobfox-coffee",
"url": "https://cdn.yourinstance.com/emojis/f7b1c1b0-0b1b-4b1b-8b1b-0b1b1b1b1b1b.png",
"static_url": "https://cdn.yourinstance.com/emojis/f7b1c1b0-0b1b-4b1b-8b1b-0b1b1b1b1b1b.png",
"visible_in_picker": true,
"category": "Blobfoxes"
}
```
@ -94,4 +180,25 @@ Edits a custom emoji on the instance.
DELETE /api/v1/emojis/:id
```
Deletes a custom emoji on the instance.
Delete an existing custom emoji.
- **Returns:** `204 No Content`
- **Authentication:** Required
- **Permissions:** `owner:emoji`, or `emoji` if deleting a global emoji.
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
DELETE /api/v1/emojis/f7b1c1b0-0b1b-4b1b-8b1b-0b1b1b1b1
Authorization: Bearer ...
```
### Response
#### `204 No Content`
Emoji successfully deleted.

View file

@ -1,22 +0,0 @@
# Federation API
The Federation API contains a variety of endpoints for interacting with the Versia Server remote network.
## Refetch User
```http
POST /api/v1/accounts/:id/refetch
```
Refetches the user's account from the remote network.
### Response
Returns the updated account object.
```ts
// 200 OK
{
id: string,
... // Account object
}
```

View file

@ -1,239 +0,0 @@
# Frontend API
The frontend API contains endpoints that are useful for frontend developers. These endpoints are not part of the Mastodon API, but are specific to Versia Server.
## Routes that the Frontend must implement
These routes can be set to a different URL in the Versia Server configuration, at `frontend.routes`. The frontend must implement these routes for the instance to function correctly.
- `GET /oauth/authorize`: (NOT `POST`): Identifier/password login form, submits to [`POST /api/auth/login`](#sign-in) or OpenID Connect flow.
- `GET /oauth/consent`: Consent form, submits to [`POST /oauth/authorize`](#consent)
## Get Frontend Configuration
```http
GET /api/v1/frontend/config
```
Retrieves the frontend configuration for the instance. This returns whatever the `frontend.settings` object is set to in the Versia Server configuration.
This behaves like the `/api/v1/preferences` endpoint in the Mastodon API, but is specific to the frontend. These values are arbitrary and can be used for anything.
For example, the frontend configuration could contain the following:
```json
{
"pub.versia.fe:theme": "dark",
"pub.versia.fe:custom_css": "body { background-color: black; }",
// Googly is an imaginary frontend that uses the `net.googly.frontend` namespace
"net.googly.frontend:spoiler_image": "https://example.com/spoiler.png"
}
```
Frontend developers should always namespace their keys to avoid conflicts with other keys.
### Response
```ts
// 200 OK
{
[key: string]: any;
}
```
## Sign In
Allows users to sign in to the instance. Required for the frontend to function.
```http
POST /api/auth/login
```
### Parameters
- `Content-Type`: `multipart/form-data`
- `identifier`: string, required. Either the username or the email of the user. Converted to lowercase automatically (case insensitive).
- `password`: string, required. The password of the user.
#### Query Parameters
- `client_id`: string, required. Client ID of the Mastodon API application that is making the request.
- `redirect_uri`: string, required. Redirect URI of the Mastodon API application that is making the request. Must match the saved value.
- `response_type`: string, required. Must be `code`.
- `scope`: string, required. Standard Mastodon API OAuth2 scope. Must match the saved value.
### Response
Responds with a `302 Found` redirect to `/oauth/consent` with some query parameters. The frontend should redirect the user to this URL.
This response also has a `Set-Cookie` header with a [JSON Web Token](https://jwt.io/) that contains the user's session information. This JWT is signed with the instance's secret key, and must be included in all subsequent authentication requests.
## Redirect
Redirects the user from the consent page to the redirect URI with the authorization code.
```http
POST /api/auth/redirect
```
### Query Parameters
- `client_id`: string, required. Client ID of the Mastodon API application that is making the request.
- `redirect_uri`: string, required. Redirect URI of the Mastodon API application that is making the request. Must match the saved value.
- `code`: string, required. Authorization code from the previous step.
### Response
Responds with a `302 Found` redirect to the `redirect_uri` with the authorization code as a query parameter.
## SSO Login
Allows users to sign in to the instance using an external OpenID Connect provider.
```http
POST /oauth/sso
```
### Query Parameters
- `issuer`: string, required. The issuer ID of the OpenID Connect provider as set in config.
- `client_id`: string, required. Client ID of the Mastodon API application that is making the request.
### Response
Responds with a `302 Found` redirect to the OpenID Connect provider's authorization endpoint. The frontend should redirect the user to this URL, without modification.
## SSO Callback/Redirect
> [!INFO]
> This endpoint should not be called directly by the frontend. It is an internal route.
Callback URL for the OpenID Connect provider to redirect to after the user has authenticated.
```http
GET /oauth/sso/:issuer/callback
```
### Query Parameters
- `client_id`: string, required. Client ID of the Mastodon API application that is making the request.
- `flow_id`: string, required. Flow ID of the OpenID Connect flow.
- `link`: boolean, optional. If `true`, the user is linking their account to the OpenID Connect provider.
- `user_id`: string, optional. User ID of the user that is linking their account. Required if `link` is `true`.
### Response
Responds with a `302 Found` redirect to either `/oauth/consent` or `/?oidc_account_linked=true` if the user is linking their account.
When erroring, responds with a `302 Found` redirect to `/?oidc_account_linking_error=<error_message>&oidc_account_linking_error_message=<error_description>`.
## SSO Link
Allows users to link their account to an external OpenID Connect provider.
```http
POST /api/v1/sso
```
### Parameters
This request is authenticated with the user's Mastodon API access token.
- `Content-Type`: `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`.
- `issuer`: string, required. The issuer ID of the OpenID Connect provider as set in config.
### Response
The client must redirect the user to the contents of the `link` field in the response.
```ts
// 200 OK
{
link: string;
}
```
## SSO Unlink
Allows users to unlink their account from an external OpenID Connect provider.
```http
DELETE /api/v1/sso/:issuer
```
### Parameters
This request is authenticated with the user's Mastodon API access token.
### Response
```ts
// 204 NO CONTENT
```
## SSO List
Lists all external OpenID Connect providers that the user has linked their account to.
```http
GET /api/v1/sso
```
### Parameters
This request is authenticated with the user's Mastodon API access token.
### Response
```ts
// 200 OK
{
id: string;
name: string;
icon: string;
}[];
```
## SSO Get Linked Provider Data
Gets the data of an external OpenID Connect provider that the user has linked their account to. The same data is returned as in the `/api/v1/sso` endpoint.
```http
GET /api/v1/sso/:issuer
```
### Parameters
This request is authenticated with the user's Mastodon API access token.
### Response
```ts
// 200 OK
{
id: string;
name: string;
icon: string;
}
```
## Get User By Username
Gets a user by their username.
```http
GET /api/v1/users/id?username=myCoolUser
```
### Response
Returns an account object.
```ts
// 200 OK
{
id: string;
// Account object
}

View file

@ -1,40 +0,0 @@
# Versia Server API Documentation
The Versia Server API strictly follows the latest available Mastodon API version (Glitch-Soc version). This means that the Versia Server API is a superset of the Mastodon API, with additional endpoints and features.
Some more information about the Mastodon API can be found in the [Mastodon API documentation](https://docs.joinmastodon.org/api/).
## Emoji API
For client developers. Please read [the documentation](./emojis.md).
## Roles API
For client developers. Please read [the documentation](./roles.md).
## Challenges API
For client developers. Please read [the documentation](./challenges.md).
## Moderation API
> [!WARNING]
> **Not implemented.**
For client developers. Please read [the documentation](./moderation.md).
## Federation API
For client developers. Please read [the documentation](./federation.md).
## Frontend API
For frontend developers. Please read [the documentation](./frontend.md).
## Mastodon API Extensions
Extra attributes have been added to some Mastodon API routes. Those changes are [documented in this document](./mastodon.md)
## Instance API
Extra endpoints have been added to the API to provide additional information about the instance. Please read [the documentation](./instance.md).

View file

@ -1,11 +0,0 @@
# Instance Endpoints
Extra endpoints have been added to the API to provide additional information about the instance.
## `/api/v1/instance/tos`
Returns the same output as Mastodon's `/api/v1/instance/extended_description`, but with the instance's Terms of Service. Configurable at `instance.tos_path` in config.
## `/api/v1/instance/privacy_policy`
Returns the same output as Mastodon's `/api/v1/instance/extended_description`, but with the instance's Privacy Policy. Configurable at `instance.privacy_policy_path` in config.

View file

@ -1,34 +1,302 @@
# Mastodon API Extensions
Extra attributes have been added to some Mastodon API routes. Changes are documented in this document.
Versia Server extends several Mastodon API endpoints to provide additional functionality. These endpoints are not part of the official Mastodon API, but are provided by Versia Server to enhance the user experience.
## Refetch User
```http
POST /api/v1/accounts/:id/refetch
```
Refetches the user's profile information from remote servers. Does not work for local users.
- **Returns**: [`Account`](https://docs.joinmastodon.org/entities/Account/)
- **Authentication**: Required
- **Permissions**: `read:account`
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
POST /api/v1/accounts/364fd13f-28b5-4e88-badd-ce3e533f0d02/refetch
Authorization: Bearer ...
```
### Response
#### `400 Bad Request`
The user is a local user and cannot be refetched.
#### `200 OK`
New user data.
Example from the [Mastodon API documentation](https://docs.joinmastodon.org/entities/Account/):
```json
{
"id": "23634",
"username": "noiob",
"acct": "noiob@awoo.space",
"display_name": "ikea shark fan account",
"locked": false,
"bot": false,
"created_at": "2017-02-08T02:00:53.274Z",
"note": "<p>:ms_rainbow_flag: :ms_bisexual_flagweb: :ms_nonbinary_flag: <a href=\"https://awoo.space/tags/awoo\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>awoo</span}.space <a href=\"https://awoo.space/tags/admin\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>admin</span} ~ <a href=\"https://awoo.space/tags/bi\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>bi</span} ~ <a href=\"https://awoo.space/tags/nonbinary\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>nonbinary</span} ~ compsci student ~ likes video <a href=\"https://awoo.space/tags/games\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>games</span} and weird/ old electronics and will post obsessively about both ~ avatar by <span class=\"h-card\"><a href=\"https://weirder.earth/@dzuk\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>dzuk</span}</span></p>",
"url": "https://awoo.space/@noiob",
"avatar": "https://files.mastodon.social/accounts/avatars/000/023/634/original/6ca8804dc46800ad.png",
"avatar_static": "https://files.mastodon.social/accounts/avatars/000/023/634/original/6ca8804dc46800ad.png",
"header": "https://files.mastodon.social/accounts/headers/000/023/634/original/256eb8d7ac40f49a.png",
"header_static": "https://files.mastodon.social/accounts/headers/000/023/634/original/256eb8d7ac40f49a.png",
"followers_count": 547,
"following_count": 404,
"statuses_count": 28468,
"last_status_at": "2019-11-17",
"emojis": [
{
"shortcode": "ms_rainbow_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/028/691/original/6de008d6281f4f59.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/028/691/static/6de008d6281f4f59.png",
"visible_in_picker": true
},
{
"shortcode": "ms_bisexual_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/050/744/original/02f94a5fca7eaf78.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/050/744/static/02f94a5fca7eaf78.png",
"visible_in_picker": true
},
{
"shortcode": "ms_nonbinary_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/105/099/original/8106088bd4782072.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/105/099/static/8106088bd4782072.png",
"visible_in_picker": true
}
],
"fields": [
{
"name": "Pronouns",
"value": "they/them",
"verified_at": null
},
{
"name": "Alt",
"value": "<span class=\"h-card\"><a href=\"https://cybre.space/@noiob\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>noiob</span}</span>",
"verified_at": null
},
{
"name": "Bots",
"value": "<span class=\"h-card\"><a href=\"https://botsin.space/@darksouls\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>darksouls</span}</span>, <span class=\"h-card\"><a href=\"https://botsin.space/@nierautomata\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>nierautomata</span}</span>, <span class=\"h-card\"><a href=\"https://mastodon.social/@fedi\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>fedi</span}</span>, code for <span class=\"h-card\"><a href=\"https://botsin.space/@awoobot\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>awoobot</span}</span>",
"verified_at": null
},
{
"name": "Website",
"value": "<a href=\"http://shork.xyz\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">http://</span><span class=\"\">shork.xyz</span><span class=\"invisible\"></span}",
"verified_at": "2019-11-10T10:31:10.744+00:00"
}
]
}
```
## Get User By Username
```http
GET /api/v1/accounts/id?username=:username
```
Retrieves a user by their username.
- **Returns**: [`Account`](https://docs.joinmastodon.org/entities/Account/)
- **Authentication**: Not required
- **Permissions**: `read:account`
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
GET /api/v1/accounts/id?username=bobleponge
```
### Response
#### `404 Not Found`
No user with that username was found.
#### `200 OK`
User data.
Example from the [Mastodon API documentation](https://docs.joinmastodon.org/entities/Account/):
```json
{
"id": "23634",
"username": "noiob",
"acct": "noiob@awoo.space",
"display_name": "ikea shark fan account",
"locked": false,
"bot": false,
"created_at": "2017-02-08T02:00:53.274Z",
"note": "<p>:ms_rainbow_flag: :ms_bisexual_flagweb: :ms_nonbinary_flag: <a href=\"https://awoo.space/tags/awoo\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>awoo</span}.space <a href=\"https://awoo.space/tags/admin\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>admin</span} ~ <a href=\"https://awoo.space/tags/bi\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>bi</span} ~ <a href=\"https://awoo.space/tags/nonbinary\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>nonbinary</span} ~ compsci student ~ likes video <a href=\"https://awoo.space/tags/games\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>games</span} and weird/ old electronics and will post obsessively about both ~ avatar by <span class=\"h-card\"><a href=\"https://weirder.earth/@dzuk\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>dzuk</span}</span></p>",
"url": "https://awoo.space/@noiob",
"avatar": "https://files.mastodon.social/accounts/avatars/000/023/634/original/6ca8804dc46800ad.png",
"avatar_static": "https://files.mastodon.social/accounts/avatars/000/023/634/original/6ca8804dc46800ad.png",
"header": "https://files.mastodon.social/accounts/headers/000/023/634/original/256eb8d7ac40f49a.png",
"header_static": "https://files.mastodon.social/accounts/headers/000/023/634/original/256eb8d7ac40f49a.png",
"followers_count": 547,
"following_count": 404,
"statuses_count": 28468,
"last_status_at": "2019-11-17",
"emojis": [
{
"shortcode": "ms_rainbow_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/028/691/original/6de008d6281f4f59.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/028/691/static/6de008d6281f4f59.png",
"visible_in_picker": true
},
{
"shortcode": "ms_bisexual_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/050/744/original/02f94a5fca7eaf78.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/050/744/static/02f94a5fca7eaf78.png",
"visible_in_picker": true
},
{
"shortcode": "ms_nonbinary_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/105/099/original/8106088bd4782072.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/105/099/static/8106088bd4782072.png",
"visible_in_picker": true
}
],
"fields": [
{
"name": "Pronouns",
"value": "they/them",
"verified_at": null
},
{
"name": "Alt",
"value": "<span class=\"h-card\"><a href=\"https://cybre.space/@noiob\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>noiob</span}</span>",
"verified_at": null
},
{
"name": "Bots",
"value": "<span class=\"h-card\"><a href=\"https://botsin.space/@darksouls\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>darksouls</span}</span>, <span class=\"h-card\"><a href=\"https://botsin.space/@nierautomata\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>nierautomata</span}</span>, <span class=\"h-card\"><a href=\"https://mastodon.social/@fedi\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>fedi</span}</span>, code for <span class=\"h-card\"><a href=\"https://botsin.space/@awoobot\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>awoobot</span}</span>",
"verified_at": null
},
{
"name": "Website",
"value": "<a href=\"http://shork.xyz\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">http://</span><span class=\"\">shork.xyz</span><span class=\"invisible\"></span}",
"verified_at": "2019-11-10T10:31:10.744+00:00"
}
]
}
```
## Get Instance TOS
```http
GET /api/v1/instance/tos
```
Returns the instance's Terms of Service, as configured in the instance settings.
- **Returns**: [`ExtendedDescription`](https://docs.joinmastodon.org/entities/ExtendedDescription/)
- **Authentication**: Not required
- **Permissions**: None
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
GET /api/v1/instance/tos
```
### Response
#### `200 OK`
Instance's Terms of Service.
```json
{
"updated_at": "2019-11-17T00:00:00.000Z",
"content": "<h1>TOS</h1>\n<p>These are the terms of service for this instance.</p>",
}
```
## Get Instance Privacy Policy
```http
GET /api/v1/instance/privacy_policy
```
Returns the instance's Privacy Policy, as configured in the instance settings.
- **Returns**: [`ExtendedDescription`](https://docs.joinmastodon.org/entities/ExtendedDescription/)
- **Authentication**: Not required
- **Permissions**: None
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
GET /api/v1/instance/privacy_policy
```
### Response
#### `200 OK`
Instance's Privacy Policy.
```json
{
"updated_at": "2019-11-17T00:00:00.000Z",
"content": "<h1>Privacy Policy</h1>\n<p>This is the privacy policy for this instance.</p>",
}
```
## `/api/v1/instance`
Three extra attributes have been added to the `/api/v1/instance` endpoint:
Extra attributes have been added to the `/api/v1/instance` endpoint.
```ts
{
// ...
interface SSOProvider {
id: string;
name: string;
icon?: string;
}
type ExtendedInstance = Instance & {
banner: string | null;
versia_version: string;
sso: {
forced: boolean;
providers: {
id: string;
name: string;
icon?: string;
}[];
}
providers: SSOProvider[];
};
}
```
### `banner`
The URL of the instance's banner image. `null` if there is no banner set.
The URL of the instance's banner image.
### `versia_version`
The version of the Versia Server instance.
The version of Versia Server running on the instance.
The normal `version` field is always set to `"4.3.0+glitch"` or similar, to not confuse clients that expect a Mastodon instance.
@ -44,29 +312,54 @@ Single Sign-On (SSO) settings for the instance. This object contains two fields:
## `/api/v2/instance`
Contains the same extensions as `/api/v1/instance`, except `banner` which uses the normal Mastodon API attribute.
Extra attributes have been added to the `/api/v2/instance` endpoint. These are identical to the `/api/v1/instance` endpoint, except that the `banner` attribute uses the normal Mastodon API attribute.
```ts
type ExtendedInstanceV2 = Instance & {
versia_version: string;
sso: {
forced: boolean;
providers: SSOProvider[];
};
}
```
### `versia_version`
The version of Versia Server running on the instance.
The normal `version` field is always set to `"4.3.0+glitch"` or similar, to not confuse clients that expect a Mastodon instance.
### `sso`
Single Sign-On (SSO) settings for the instance. This object contains two fields:
- `forced`: If this is enabled, normal identifier/password login is disabled and login must be done through SSO.
- `providers`: An array of external OpenID Connect providers that users can link their accounts to. Each provider object contains the following fields:
- `id`: The issuer ID of the OpenID Connect provider.
- `name`: The name of the provider.
- `icon`: The URL of the provider's icon. Optional.
## `Account`
(`/api/v1/accounts/:id`, `/api/v1/accounts/verify_credentials`, ...)
Two extra attributes have been added to all returned [`Account`](https://docs.joinmastodon.org/entities/Account/) objects.
Two extra attributes has been adding to all returned account objects:
This object is returned on routes such as `/api/v1/accounts/:id`, `/api/v1/accounts/verify_credentials`, etc.
```ts
{
// ...
roles: VersiaRoles[];
type ExtendedAccount = Account & {
roles: Role[];
uri: string;
}
```
### `roles`
An array of roles from [Versia Server Roles](./roles.md).
An array of [`Roles`](./roles.md#role) that the user has.
### `uri`
The URI of the account's Versia object (for federation). Similar to Mastodon's `uri` field on notes.
URI of the account's Versia entity (for federation). Similar to Mastodon's `uri` field on notes.
## `/api/v1/accounts/update_credentials`

View file

@ -1,271 +0,0 @@
# Moderation API
> [!WARNING]
> **NOT IMPLEMENTED**
The Versia Server project uses the Mastodon API to interact with clients. However, the moderation API is custom-made for Versia Server Server, as it allows for more fine-grained control over the server's behavior.
## Flags, ModTags and ModNotes
Flags are used by Versia Server Server to automatically attribute tags to a status or account based on rules. ModTags and ModNotes are used by moderators to manually tag and take notes on statuses and accounts.
The difference between flags and modtags is that flags are automatically attributed by the server, while modtags are manually attributed by moderators.
### Flag Types
- `content_filter`: (Statuses only) The status contains content that was filtered by the server's content filter.
- `bio_filter`: (Accounts only) The account's bio contains content that was filtered by the server's content filter.
- `emoji_filter`: The status or account contains an emoji that was filtered by the server's content filter.
- `reported`: The status or account was previously reported by a user.
- `suspended`: The status or account was previously suspended by a moderator.
- `silenced`: The status or account was previously silenced by a moderator.
### ModTag Types
ModTag do not have set types and can be anything. Versia Server Server autosuggest previously used tags when a moderator is adding a new tag to avoid duplicates.
### Data Format
```ts
type Flag = {
id: string,
// One of the following two fields will be present
flaggedStatus?: Status,
flaggedUser?: User,
flagType: string,
createdAt: string,
}
type ModTag = {
id: string,
// One of the following two fields will be present
taggedStatus?: Status,
taggedUser?: User,
mod: User,
tag: string,
createdAt: string,
}
type ModNote = {
id: string,
// One of the following two fields will be present
notedStatus?: Status,
notedUser?: User,
mod: User,
note: string,
createdAt: string,
}
```
The `User` and `Status` types are the same as the ones in the Mastodon API.
## Moderation API Routes
### `GET /api/v1/moderation/accounts/:id`
Returns full moderation data and flags for the account with the given ID.
Output format:
```ts
{
id: string, // Same ID as in account field
flags: Flag[],
modtags: ModTag[],
modnotes: ModNote[],
account: User,
}
```
### `GET /api/v1/moderation/statuses/:id`
Returns full moderation data and flags for the status with the given ID.
Output format:
```ts
{
id: string, // Same ID as in status field
flags: Flag[],
modtags: ModTag[],
modnotes: ModNote[],
status: Status,
}
```
### `POST /api/v1/moderation/accounts/:id/modtags`
Params:
- `tag`: string
Adds a modtag to the account with the given ID
### `POST /api/v1/moderation/statuses/:id/modtags`
Params:
- `tag`: string
Adds a modtag to the status with the given ID
### `POST /api/v1/moderation/accounts/:id/modnotes`
Params:
- `note`: string
Adds a modnote to the account with the given ID
### `POST /api/v1/moderation/statuses/:id/modnotes`
Params:
- `note`: string
Adds a modnote to the status with the given ID
### `DELETE /api/v1/moderation/accounts/:id/modtags/:modtag_id`
Deletes the modtag with the given ID from the account with the given ID
### `DELETE /api/v1/moderation/statuses/:id/modtags/:modtag_id`
Deletes the modtag with the given ID from the status with the given ID
### `DELETE /api/v1/moderation/accounts/:id/modnotes/:modnote_id`
Deletes the modnote with the given ID from the account with the given ID
### `DELETE /api/v1/moderation/statuses/:id/modnotes/:modnote_id`
Deletes the modnote with the given ID from the status with the given ID
### `GET /api/v1/moderation/modtags`
Returns a list of all modtags previously used by moderators
Output format:
```ts
{
tags: string[],
}
```
### `GET /api/v1/moderation/accounts/flags/search`
Allows moderators to search for accounts based on their flags, this can also include status flags
Params:
- `limit`: Number
- `min_id`: String. Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.
- `max_id`: String. All results returned will be lesser than this ID. In effect, sets an upper bound on results.
- `since_id`: String. All results returned will be greater than this ID. In effect, sets a lower bound on results.
- `flags`: String (optional). Comma-separated list of flag types to filter by. Can be left out to return accounts with at least one flag
- `flag_count`: Number (optional). Minimum number of flags to filter by
- `include_statuses`: Boolean (optional). If true, includes status flags in the search results
- `account_id`: Array of strings (optional). Filters accounts by account ID
This method returns a `Link` header the same way Mastodon does, to allow for pagination.
Output format:
```ts
{
accounts: {
account: User,
modnotes: ModNote[],
flags: Flag[],
statuses?: {
status: Status,
modnotes: ModNote[],
flags: Flag[],
}[],
}[],
}
```
### `GET /api/v1/moderation/statuses/flags/search`
Allows moderators to search for statuses based on their flags
Params:
- `limit`: Number
- `min_id`: String. Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.
- `max_id`: String. All results returned will be lesser than this ID. In effect, sets an upper bound on results.
- `since_id`: String. All results returned will be greater than this ID. In effect, sets a lower bound on results.
- `flags`: String (optional). Comma-separated list of flag types to filter by. Can be left out to return statuses with at least one flag
- `flag_count`: Number (optional). Minimum number of flags to filter by
- `account_id`: Array of strings (optional). Filters statuses by account ID
This method returns a `Link` header the same way Mastodon does, to allow for pagination.
Output format:
```ts
{
statuses: {
status: Status,
modnotes: ModNote[],
flags: Flag[],
}[],
}
```
### `GET /api/v1/moderation/accounts/modtags/search`
Allows moderators to search for accounts based on their modtags
Params:
- `limit`: Number
- `min_id`: String. Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.
- `max_id`: String. All results returned will be lesser than this ID. In effect, sets an upper bound on results.
- `since_id`: String. All results returned will be greater than this ID. In effect, sets a lower bound on results.
- `tags`: String (optional). Comma-separated list of tags to filter by. Can be left out to return accounts with at least one tag
- `tag_count`: Number (optional). Minimum number of tags to filter by
- `include_statuses`: Boolean (optional). If true, includes status tags in the search results
- `account_id`: Array of strings (optional). Filters accounts by account ID
This method returns a `Link` header the same way Mastodon does, to allow for pagination.
Output format:
```ts
{
accounts: {
account: User,
modnotes: ModNote[],
modtags: ModTag[],
statuses?: {
status: Status,
modnotes: ModNote[],
modtags: ModTag[],
}[],
}[],
}
```
### `GET /api/v1/moderation/statuses/modtags/search`
Allows moderators to search for statuses based on their modtags
Params:
- `limit`: Number
- `min_id`: String. Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.
- `max_id`: String. All results returned will be lesser than this ID. In effect, sets an upper bound on results.
- `since_id`: String. All results returned will be greater than this ID. In effect, sets a lower bound on results.
- `tags`: String (optional). Comma-separated list of tags to filter by. Can be left out to return statuses with at least one tag
- `tag_count`: Number (optional). Minimum number of tags to filter by
- `account_id`: Array of strings (optional). Filters statuses by account ID
- `include_statuses`: Boolean (optional). If true, includes status tags in the search results
This method returns a `Link` header the same way Mastodon does, to allow for pagination.
Output format:
```ts
{
statuses: {
status: Status,
modnotes: ModNote[],
modtags: ModTag[],
}[],
}
```

View file

@ -1,79 +1,82 @@
# Roles API
The Roles API lets users manage roles given to them by administrators. This API is available to all users.
> [!WARNING]
> The API for **administrators** is different (and unimplemented): this is the API for **users**.
>
> Furthermore, users can only manage roles if they have the `roles` permission, and the role they wish to manage does not have a higher priority than their highest priority role.
> This API is due to be reworked in the future. The current implementation is not final.
>
> Missing features include the ability to create and delete roles, as well as the ability to assign roles to other users.
This API allows users to create, read, update, and delete instance custom roles. Custom roles can be used to grant users specific permissions, such as managing the instance, uploading custom emojis, or moderating content.
## Priorities
Roles have a priority, which determines the order in which they are applied. Roles with higher priorities take precedence over roles with lower priorities.
Every role has a "priority" value, which determines the order in which roles are applied. Roles with higher priorities take precedence over roles with lower priorities. The default priority is `0`.
Additionally, users cannot remove or add roles with a higher priority than their highest priority role.
Additionally, users cannot edit roles with a higher priority than their highest priority role.
## Visibility
Roles can be visible or invisible. Invisible roles are not shown to users in the UI, but they can still be managed via the API.
> [!WARNING]
> All roles assigned to a user are public information and can be retrieved via the API. The visibility of a role only affects whether it is shown in the UI, which clients can choose to respect or not.
Roles can be visible or invisible. Invisible roles are not shown to users in the UI, but they can still be managed via the API. This is useful for cosmetic roles that do not grant any permissions, e.g. `#1 Most Prominent Glizzy Eater`.
## Permissions
Default permissions for anonymous users, logged-in users and admins can be set in config. These are always applied in addition to the permissions granted by roles. You may set them to empty arrays to exclusively use roles for permissions (make sure your roles are set up correctly).
Default permissions for anonymous users, logged-in users, and administrators can be set in the configuration. These permissions are always applied in addition to the permissions granted by roles. You may set them to empty arrays to exclusively use roles for permissions (make sure your roles are set up correctly).
### List of Permissions
- `Manage` permissions grant the ability to create, read, update, and delete resources.
- `View` permissions grant the ability to read resources.
- `Owner` permissions grant the ability to manage resources that the user owns.
```ts
// Last updated: 2024-06-07
// Search for "RolePermissions" in the source code (GitHub search bar) for the most up-to-date version
export enum RolePermissions {
MANAGE_NOTES = "notes",
MANAGE_OWN_NOTES = "owner:note",
VIEW_NOTES = "read:note",
VIEW_NOTE_LIKES = "read:note_likes",
VIEW_NOTE_BOOSTS = "read:note_boosts",
MANAGE_ACCOUNTS = "accounts",
MANAGE_OWN_ACCOUNT = "owner:account",
VIEW_ACCOUNT_FOLLOWS = "read:account_follows",
MANAGE_LIKES = "likes",
MANAGE_OWN_LIKES = "owner:like",
MANAGE_BOOSTS = "boosts",
MANAGE_OWN_BOOSTS = "owner:boost",
VIEW_ACCOUNTS = "read:account",
MANAGE_EMOJIS = "emojis",
VIEW_EMOJIS = "read:emoji",
MANAGE_OWN_EMOJIS = "owner:emoji",
MANAGE_MEDIA = "media",
MANAGE_OWN_MEDIA = "owner:media",
MANAGE_BLOCKS = "blocks",
MANAGE_OWN_BLOCKS = "owner:block",
MANAGE_FILTERS = "filters",
MANAGE_OWN_FILTERS = "owner:filter",
MANAGE_MUTES = "mutes",
MANAGE_OWN_MUTES = "owner:mute",
MANAGE_REPORTS = "reports",
MANAGE_OWN_REPORTS = "owner:report",
MANAGE_SETTINGS = "settings",
MANAGE_OWN_SETTINGS = "owner:settings",
MANAGE_ROLES = "roles",
MANAGE_NOTIFICATIONS = "notifications",
MANAGE_OWN_NOTIFICATIONS = "owner:notification",
MANAGE_FOLLOWS = "follows",
MANAGE_OWN_FOLLOWS = "owner:follow",
MANAGE_OWN_APPS = "owner:app",
SEARCH = "search",
VIEW_PUBLIC_TIMELINES = "public_timelines",
VIEW_PRIVATE_TIMELINES = "private_timelines",
IGNORE_RATE_LIMITS = "ignore_rate_limits",
IMPERSONATE = "impersonate",
MANAGE_INSTANCE = "instance",
MANAGE_INSTANCE_FEDERATION = "instance:federation",
MANAGE_INSTANCE_SETTINGS = "instance:settings",
OAUTH = "oauth",
}
ManageNotes: "notes",
ManageOwnNotes: "owner:note",
ViewNotes: "read:note",
ViewNoteLikes: "read:note_likes",
ViewNoteBoosts: "read:note_boosts",
ManageAccounts: "accounts",
ManageOwnAccount: "owner:account",
ViewAccountFollows: "read:account_follows",
ManageLikes: "likes",
ManageOwnLikes: "owner:like",
ManageBoosts: "boosts",
ManageOwnBoosts: "owner:boost",
ViewAccounts: "read:account",
ManageEmojis: "emojis",
ViewEmojis: "read:emoji",
ManageOwnEmojis: "owner:emoji",
ManageMedia: "media",
ManageOwnMedia: "owner:media",
ManageBlocks: "blocks",
ManageOwnBlocks: "owner:block",
ManageFilters: "filters",
ManageOwnFilters: "owner:filter",
ManageMutes: "mutes",
ManageOwnMutes: "owner:mute",
ManageReports: "reports",
ManageOwnReports: "owner:report",
ManageSettings: "settings",
ManageOwnSettings: "owner:settings",
ManageRoles: "roles",
ManageNotifications: "notifications",
ManageOwnNotifications: "owner:notification",
ManageFollows: "follows",
ManageOwnFollows: "owner:follow",
ManageOwnApps: "owner:app",
Search: "search",
ViewPublicTimelines: "public_timelines",
ViewPrimateTimelines: "private_timelines",
IgnoreRateLimits: "ignore_rate_limits",
Impersonate: "impersonate",
ManageInstance: "instance",
ManageInstanceFederation: "instance:federation",
ManageInstanceSettings: "instance:settings",
/** Users who do not have this permission will not be able to login! */
OAuth: "oauth",
```
An example usage of these permissions would be to not give the `ViewPublicTimelines` permission to anonymous users, but give it to logged-in users, in order to restrict access to public timelines.
### Manage Roles
The `roles` permission allows the user to manage roles, including adding and removing roles from themselves. This permission is required to use the Roles API.
@ -82,21 +85,29 @@ The `roles` permission allows the user to manage roles, including adding and rem
The `impersonate` permission allows the user to impersonate other users (logging in with their credentials). This is a dangerous permission and should be used with caution.
### Manage Instance
Useful for administrators who need to troubleshoot user issues.
The `instance` permission allows the user to manage the instance, including viewing logs, restarting the instance, and more.
### OAuth
### Manage Instance Federation
The `oauth` permission is required for users to log in via OAuth. Users who do not have this permission will not be able to log in via OAuth.
The `instance:federation` permission allows the user to manage the instance's federation settings, including blocking and unblocking other instances.
## Role
### Manage Instance Settings
```ts
type UUID = string;
type URL = string;
type Permission = string;
The `instance:settings` permission allows the user to manage the instance's settings, including changing the instance's name, description, and more.
### OAuth2
The `oauth` permission is required for users to log in to the instance. Users who do not have this permission will not be able to log in.
interface Role {
id: UUID;
name: string;
permissions: Permission[];
priority: number;
description?: string | null;
visible: boolean;
icon?: URL | null;
}
```
## Get Roles
@ -104,21 +115,117 @@ The `oauth` permission is required for users to log in to the instance. Users wh
GET /api/v1/roles
```
Retrieves a list of roles that the user has.
Get a list of all roles that the requesting user has.
- **Returns**: Array of [`Role`](#role)
- **Authentication**: Required
- **Permissions**: None
- **Version History**:
- `0.7.0`: Added.
### Example
```http
GET /api/v1/roles
Authorization: Bearer ...
```
### Response
```ts
// 200 OK
#### `200 OK`
All roles owned by the user.
```json
[
{
id: string;
name: string;
permissions: RolePermissions[];
priority: number;
description: string | null;
visible: boolean;
icon: string | null
}[];
"id": "default",
"name": "Default",
"permissions": [
"owner:note",
"read:note",
"read:note_likes",
"read:note_boosts",
"owner:account",
"read:account_follows",
"owner:like",
"owner:boost",
"read:account",
"owner:emoji",
"read:emoji",
"owner:media",
"owner:block",
"owner:filter",
"owner:mute",
"owner:report",
"owner:settings",
"owner:notification",
"owner:follow",
"owner:app",
"search",
"public_timelines",
"private_timelines",
"oauth"
],
"priority": 0,
"description": "Default role for all users",
"visible": false,
"icon": null
},
{
"id": "admin",
"name": "Admin",
"permissions": [
"owner:note",
"read:note",
"read:note_likes",
"read:note_boosts",
"owner:account",
"read:account_follows",
"owner:like",
"owner:boost",
"read:account",
"owner:emoji",
"read:emoji",
"owner:media",
"owner:block",
"owner:filter",
"owner:mute",
"owner:report",
"owner:settings",
"owner:notification",
"owner:follow",
"owner:app",
"search",
"public_timelines",
"private_timelines",
"oauth",
"notes",
"accounts",
"likes",
"boosts",
"emojis",
"media",
"blocks",
"filters",
"mutes",
"reports",
"settings",
"roles",
"notifications",
"follows",
"impersonate",
"ignore_rate_limits",
"instance",
"instance:federation",
"instance:settings"
],
"priority": 2147483647,
"description": "Default role for all administrators",
"visible": false,
"icon": null
}
]
```
## Get Role
@ -127,36 +234,94 @@ Retrieves a list of roles that the user has.
GET /api/v1/roles/:id
```
Retrieves information about a role.
Get a specific role's data.
- **Returns**: [`Role`](#role)
- **Authentication**: Required
- **Permissions**: None
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
GET /api/v1/roles/default
Authorization: Bearer ...
```
### Response
```ts
// 200 OK
#### `200 OK`
Role data.
```json
{
id: string;
name: string;
permissions: RolePermissions[];
priority: number;
description: string | null;
visible: boolean;
icon: string | null
"id": "default",
"name": "Default",
"permissions": [
"owner:note",
"read:note",
"read:note_likes",
"read:note_boosts",
"owner:account",
"read:account_follows",
"owner:like",
"owner:boost",
"read:account",
"owner:emoji",
"read:emoji",
"owner:media",
"owner:block",
"owner:filter",
"owner:mute",
"owner:report",
"owner:settings",
"owner:notification",
"owner:follow",
"owner:app",
"search",
"public_timelines",
"private_timelines",
"oauth"
],
"priority": 0,
"description": "Default role for all users",
"visible": false,
"icon": null
}
```
## Add Role
## Assign Role
```http
POST /api/v1/roles/:id
```
Adds the role with the given ID to the user making the request.
Assign a role to the user making the request.
- **Returns**: `204 No Content`
- **Authentication**: Required
- **Permissions**: `roles`
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
POST /api/v1/roles/364fd13f-28b5-4e88-badd-ce3e533f0d02
Authorization: Bearer ...
```
### Response
```ts
// 204 No Content
```
#### `204 No Content`
Role successfully assigned.
## Remove Role
@ -164,10 +329,25 @@ Adds the role with the given ID to the user making the request.
DELETE /api/v1/roles/:id
```
Removes the role with the given ID from the user making the request.
Remove a role from the user making the request.
- **Returns**: `204 No Content`
- **Authentication**: Required
- **Permissions**: `roles`
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
DELETE /api/v1/roles/364fd13f-28b5-4e88-badd-ce3e533f0d
Authorization: Bearer ...
```
### Response
```ts
// 204 No Content
```
#### `204 No Content`
Role successfully removed.

164
docs/api/sso.md Normal file
View file

@ -0,0 +1,164 @@
# SSO API
The SSO API is used to link, unlink, and list external OpenID Connect providers that the user has linked their account to.
## SSO Provider
```ts
interface SSOProvider {
id: string;
name: string;
icon: string;
}
```
## SSO Link
```http
POST /api/v1/sso
```
Allows users to link their account to an external OpenID Connect provider.
- **Returns**: Link to redirect the user to the external provider.
- **Authentication**: Required
- **Permissions**: `oauth`
- **Version History**:
- `0.6.0`: Added.
- `0.7.0`: Permissions added.
### Request
- `issuer` (string, required): The issuer ID of the OpenID Connect provider as set in config.
#### Example
```http
POST /api/v1/sso
Authorization: Bearer ...
Content-Type: application/json
{
"issuer": "google"
}
```
### Response
#### `200 OK`
Link to redirect the user to the external provider's page.
```json
{
"link": "https://accounts.google.com/o/oauth2/auth?client_id=..."
}
```
## SSO Unlink
```http
DELETE /api/v1/sso/:issuer
```
Allows users to unlink their account from an external OpenID Connect provider.
- **Returns**: `204 No Content`
- **Authentication**: Required
- **Permissions**: `oauth`
- **Version History**:
- `0.6.0`: Added.
- `0.7.0`: Permissions added.
### Request
#### Example
```http
DELETE /api/v1/sso/google
Authorization: Bearer ...
```
### Response
#### `204 No Content`
Account successfully unlinked.
## List Connected Providers
```http
GET /api/v1/sso
```
Lists all external OpenID Connect providers that the user has linked their account to.
- **Returns**: Array of [`SSOProvider`](#ssoprovider) objects.
- **Authentication**: Required
- **Permissions**: `oauth`
- **Version History**:
- `0.6.0`: Added.
- `0.7.0`: Permissions added.
### Request
#### Example
```http
GET /api/v1/sso
Authorization: Bearer ...
```
### Response
#### `200 OK`
Array of [`SSOProvider`](#ssoprovider) objects.
```json
[
{
"id": "google",
"name": "Google",
"icon": "https://cdn.example.com/google.png"
}
]
```
## Get Linked Provider Data
```http
GET /api/v1/sso/:issuer
```
Gets the data of an external OpenID Connect provider that the user has linked their account to.
- **Returns**: [`SSOProvider`](#ssoprovider) object.
- **Authentication**: Required
- **Permissions**: `oauth`
- **Version History**:
- `0.6.0`: Added.
- `0.7.0`: Permissions added.
### Request
#### Example
```http
GET /api/v1/sso/google
Authorization: Bearer ...
```
### Response
#### `200 OK`
[`SSOProvider`](#ssoprovider) object.
```json
{
"id": "google",
"name": "Google",
"icon": "https://cdn.example.com/google.png"
}
```

View file

@ -12,6 +12,7 @@ bun cli help
# Source installs
bun run dist/cli.js help
# Docker
# Replace `versia` with the name of your container
docker compose exec -it versia /bin/sh /app/entrypoint.sh cli help
```
@ -19,6 +20,6 @@ You can use the `help` command to see a list of available commands. These includ
## Scripting with the CLI
Some CLI commands that return data as tables can be used in scripts. To convert them to JSON or CSV, some commands allow you to specify a `--format` flag that can be either `"json"` or `"csv"`. See `bun cli help` or `bun cli <command> -h` for more information.
Some CLI commands that return data as tables can be used in scripts. To convert them to JSON or CSV, some commands allow you to specify a `--format` flag that can be either `"json"` or `"csv"`. See `cli help` or `cli <command> -h` for more information.
Flags can be used in any order and anywhere in the script (except for the `bun cli` command itself). The command arguments themselves must be in the correct order, however.Z
Flags can be used in any order and anywhere in the script (except for the `cli` command itself).

139
docs/frontend/auth.md Normal file
View file

@ -0,0 +1,139 @@
# Frontend Authentication
Multiple API routes are exposed for authentication, to be used by frontend developers.
> [!INFO]
>
> These are different from the Client API routes, which are used by clients to interact with the Mastodon API.
A frontend is a web application that is designed to be the primary user interface for an instance. It is used also used by clients to perform authentication.
## Get Frontend Configuration
```http
GET /api/v1/frontend/config
```
Retrieves the frontend configuration for the instance. This returns whatever the `frontend.settings` object is set to in the Versia Server configuration.
This behaves like the `/api/v1/preferences` endpoint in the Mastodon API, but is specific to the frontend. These values are arbitrary and can be used for anything.
Frontend developers should always namespace their keys to avoid conflicts with other keys.
- **Returns**: Object with arbitrary keys and values.
- **Authentication**: Not required
- **Permissions**: None
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
GET /api/v1/frontend/config
```
### Response
#### `200 OK`
Frontend configuration.
```json
{
"pub.versia.fe:theme": "dark",
"pub.versia.fe:custom_css": "body { background-color: black; }",
"net.googly.frontend:spoiler_image": "https://example.com/spoiler.png"
}
```
## Sign In
```http
POST /api/auth/login
```
Allows users to sign in to the instance. This is the first step in the authentication process.
- **Returns**: `302 Found` with a `Location` header to redirect the user to the next step, as well as a `Set-Cookie` header with the session JWT.
- **Authentication**: Not required
- **Permissions**: None
- **Version History**:
- `0.7.0`: First documented.
### Request
- `identifier` (string, required): The username or email of the user. Case-insensitive.
- `password` (string, required): The password of the user.
#### Query Parameters
- `client_id` (string, required): Client ID of the [application](https://docs.joinmastodon.org/entities/Application/) that is making the request.
- `redirect_uri` (string, required): Redirect URI of the [application](https://docs.joinmastodon.org/entities/Application/) that is making the request. Must match the saved value.
- `response_type` (string, required): Must be `code`.
- `scope` (string, required): OAuth2 scopes. Must match the value indicated in the [application](https://docs.joinmastodon.org/entities/Application/).
#### Example
```http
POST /api/auth/login?client_id=123&redirect_uri=https%3A%2F%2Fexample.com%2Fauth&response_type=code&scope=read%20write
Content-Type: application/json
{
"identifier": "bobjones@gmail.com",
"password": "hunter2"
}
```
### Response
#### `302 Found`
Redirects the user to the consent page with some query parameters. The frontend should redirect the user to this URL.
This response also has a `Set-Cookie` header with a [JSON Web Token](https://jwt.io/) that contains the user's session information. This JWT is signed with the instance's secret key, and must be included in all subsequent authentication requests.
```http
HTTP/2.0 302 Found
Location: /oauth/consent?client_id=123&redirect_uri=https%3A%2F%2Fexample.com%2Fauth&response_type=code&scope=read%20write
Set-Cookie: jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
```
## SSO Sign In
```http
POST /oauth/sso
```
Allows users to sign in to the instance using an external OpenID Connect provider.
- **Returns**: `302 Found` with a `Location` header to redirect the user to the next step.
- **Authentication**: Not required
- **Permissions**: None
- **Version History**:
- `0.7.0`: First documented.
### Request
#### Query Parameters
- `client_id` (string, required): Client ID of the [application](https://docs.joinmastodon.org/entities/Application/) that is making the request.
- `issuer` (string, required): The ID of the OpenID Connect provider, as found in `/api/{v1,v2}/instance`.
#### Example
```http
POST /oauth/sso?client_id=123&issuer=google
```
### Response
#### `302 Found`
Redirects the user to the OpenID Connect provider's login page.
```http
HTTP/2.0 302 Found
Location: https://accounts.google.com/o/oauth2/auth?client_id=123&redirect_uri=https%3A%2F%2Fexample.com%2Fauth&response_type=code&scope=openid%20email&state=123
```

53
docs/frontend/routes.md Normal file
View file

@ -0,0 +1,53 @@
# Frontend Routes
Frontend implementors must implement these routes for correct operation of the instance.
The location of these routes can be configured in the Versia Server configuration at `frontend.routes`:
## Login Form
```http
GET /oauth/authorize
```
This route should display a login form for the user to enter their username and password, as well as a list of OpenID providers to use if available.
The form should submit to [`POST /api/auth/login`](./auth.md#sign-in), or to the OpenID Connect flow.
Configurable in the Versia Server configuration at `frontend.routes.login`.
## Consent Form
```http
GET /oauth/consent
```
This route should display a consent form for the user to approve the requested application permissions, after logging in.
The form should submit an OpenID Connect authorization request at `POST /oauth/authorize`, with the correct [application](https://docs.joinmastodon.org/entities/Application/) data (client ID, redirect URI, etc.). Do not forget the JWT cookie.
### Submission Example
```http
POST /oauth/authorize
Content-Type: application/json
Cookie: jwt=...
{
"client_id": "client_id",
"response_type": "code",
"redirect_uri": "https://example.com/callback",
"scope": "read write",
"state": "state123",
"code_challenge": "code_challenge",
"code_challenge_method": "S256",
"response_type": "code"
}
```
### Submission Response
```http
HTTP/2.0 302 Found
Location: https://example.com/callback?code=code&state=state123
```

18
docs/index.md Normal file
View file

@ -0,0 +1,18 @@
---
layout: home
hero:
name: Versia Server Docs
features:
- icon: 🛠️
title: Installation
details: Details on how to install Versia Server
link: ./setup/installation
- icon: 🖥
title: API Reference
details: Writing your own client? Check out the API reference
link: ./api/emojis
- icon: 📚
title: Frontend Building
details: Information on developing your own frontend
link: ./frontend/routes
---

View file

@ -67,10 +67,10 @@ git clone https://github.com/versia-pub/server.git
bun install
```
1. Set up a PostgreSQL database (you need a special extension, please look at [the database documentation](database.md))
1. Set up a PostgreSQL database (you need a special extension, please look at [the database documentation](./database.md))
2. (If you want search)
Create a [Sonic](https://github.com/valeriansaliou/sonic) instance (using Docker is recommended). For a [`docker-compose`] file, copy the `sonic` service from the [`docker-compose.yml`](../docker-compose.yml) file. Don't forget to fill in the `config.cfg` for Sonic!
Create a [Sonic](https://github.com/valeriansaliou/sonic) instance (using Docker is recommended). For a [`docker-compose`] file, copy the `sonic` service from the [`docker-compose.yml`](https://github.com/versia-pub/server/blob/main/docker-compose.yml) file. Don't forget to fill in the `config.cfg` for Sonic!
1. Build everything:
@ -80,21 +80,23 @@ bun run build
4. Copy the `config.example.toml` file to `config.toml` inside `dist/config/` and fill in the values (you can leave most things to the default, but you will need to configure things such as the database connection)
CD to the `dist/` directory: `cd dist`
5. Move to the `dist/` directory
You may now start the server with `bun run cli/index.js start`. It lives in the `dist/` directory, all the other code can be removed from this point onwards.
```bash
cd dist
```
You may now start the server with `bun run cli/index.js start`. All other code not in the `dist/` directory can be removed.
## Running the Server
Database migrations are run automatically on startup.
You may use the environment variables `NO_COLORS=true` and `NO_FANCY_DATES=true` to disable colors and date formatting in the console logs: the file logs will never have colors or fancy dates.
Please see the [CLI documentation](cli.md) for more information on how to use the CLI.
Please see the [CLI documentation](../cli/index.md) for more information on how to use the CLI.
## Updating the server
Updating the server is as simple as pulling the latest changes from the repository and running `bun prod-build` again. You may need to run `bun install` again if there are new dependencies.
Updating the server is as simple as pulling the latest changes from the repository and running `bun run build` again. You may need to run `bun install` again if there are new dependencies.
For Docker, you can run `docker-compose pull` to update the Docker images.

View file

@ -39,7 +39,10 @@
"prune": "ts-prune | grep -v server/ | grep -v dist/ | grep -v '(used in module)'",
"schema:generate": "bun run packages/config-manager/json-schema.ts > config/config.schema.json && bun run packages/plugin-kit/json-schema.ts > packages/plugin-kit/manifest.schema.json",
"check": "bunx tsc -p .",
"test": "find . -name \"*.test.ts\" -not -path \"./node_modules/*\" | xargs -I {} sh -c 'bun test {} || exit 255'"
"test": "find . -name \"*.test.ts\" -not -path \"./node_modules/*\" | xargs -I {} sh -c 'bun test {} || exit 255'",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
},
"trustedDependencies": [
"@biomejs/biome",
@ -88,10 +91,15 @@
"@types/pg": "^8.11.10",
"@types/qs": "^6.9.17",
"drizzle-kit": "^0.28.0",
"markdown-it-image-figures": "^2.1.1",
"markdown-it-mathjax3": "^4.3.2",
"oclif": "^4.15.20",
"ts-prune": "^0.10.3",
"typescript": "^5.6.3",
"vitepress": "^1.5.0",
"vitepress-plugin-tabs": "^0.5.0",
"vitepress-sidebar": "^1.29.0",
"vue": "^3.5.12",
"zod-to-json-schema": "^3.23.5"
},
"peerDependencies": {