diff --git a/README.md b/README.md
index cf49a77e..f427ef50 100644
--- a/README.md
+++ b/README.md
@@ -49,6 +49,7 @@ Some cool features of AnythingLLM
- 100% Cloud deployment ready.
- "Bring your own LLM" model. _still in progress - openai support only currently_
- Extremely efficient cost-saving measures for managing very large documents. You'll never pay to embed a massive document or transcript more than once. 90% more cost effective than other document chatbot solutions.
+- Full Developer API for custom integrations!
### Technical Overview
This monorepo consists of three main sections:
diff --git a/frontend/public/anything-llm-dark.png b/frontend/public/anything-llm-dark.png
new file mode 100644
index 00000000..a2948438
Binary files /dev/null and b/frontend/public/anything-llm-dark.png differ
diff --git a/frontend/public/anything-llm-light.png b/frontend/public/anything-llm-light.png
new file mode 100644
index 00000000..341d21b6
Binary files /dev/null and b/frontend/public/anything-llm-light.png differ
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index df74f73e..b6cfc7eb 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -14,6 +14,7 @@ const AdminWorkspaces = lazy(() => import("./pages/Admin/Workspaces"));
const AdminChats = lazy(() => import("./pages/Admin/Chats"));
const AdminSystem = lazy(() => import("./pages/Admin/System"));
const AdminAppearance = lazy(() => import("./pages/Admin/Appearance"));
+const AdminApiKeys = lazy(() => import("./pages/Admin/ApiKeys"));
export default function App() {
return (
@@ -52,6 +53,10 @@ export default function App() {
path="/admin/appearance"
element={}
/>
+ }
+ />
diff --git a/frontend/src/components/AdminSidebar/index.jsx b/frontend/src/components/AdminSidebar/index.jsx
index b185d4f9..a583cb32 100644
--- a/frontend/src/components/AdminSidebar/index.jsx
+++ b/frontend/src/components/AdminSidebar/index.jsx
@@ -3,6 +3,7 @@ import {
BookOpen,
Eye,
GitHub,
+ Key,
Mail,
Menu,
MessageSquare,
@@ -82,6 +83,11 @@ export default function AdminSidebar() {
btnText="Appearance"
icon={}
/>
+ }
+ />
@@ -242,6 +248,11 @@ export function SidebarMobileHeader() {
btnText="Appearance"
icon={}
/>
+ }
+ />
diff --git a/frontend/src/components/Modals/Settings/ApiKey/index.jsx b/frontend/src/components/Modals/Settings/ApiKey/index.jsx
new file mode 100644
index 00000000..7f023180
--- /dev/null
+++ b/frontend/src/components/Modals/Settings/ApiKey/index.jsx
@@ -0,0 +1,198 @@
+import { useEffect, useState } from "react";
+import System from "../../../../models/system";
+import PreLoader from "../../../Preloader";
+import paths from "../../../../utils/paths";
+import showToast from "../../../../utils/toast";
+import { CheckCircle, Copy, RefreshCcw, Trash } from "react-feather";
+
+export default function ApiKey() {
+ const [loading, setLoading] = useState(true);
+ const [generating, setGenerating] = useState(false);
+ const [copied, setCopied] = useState(false);
+ const [deleting, setDeleting] = useState(false);
+ const [apiKey, setApiKey] = useState(null);
+
+ useEffect(() => {
+ async function fetchExistingApiKey() {
+ const { apiKey: _apiKey } = await System.getApiKey();
+ setApiKey(_apiKey);
+ setLoading(false);
+ }
+ fetchExistingApiKey();
+ }, []);
+
+ const generateApiKey = async () => {
+ setGenerating(true);
+ const isRefresh = !!apiKey;
+ const { apiKey: newApiKey, error } = await System.generateApiKey();
+ if (!!error) {
+ showToast(error, "error");
+ } else {
+ showToast(
+ isRefresh ? "API key regenerated!" : "API key generated!",
+ "info"
+ );
+ setApiKey(newApiKey);
+ }
+ setGenerating(false);
+ };
+
+ const removeApiKey = async () => {
+ setDeleting(true);
+ const ok = await System.deleteApiKey();
+ if (ok) {
+ showToast("API key deleted from instance.", "info");
+ setApiKey(null);
+ } else {
+ showToast("API key could not be deleted.", "error");
+ }
+ setDeleting(false);
+ };
+
+ const copyToClipboard = async () => {
+ window.navigator.clipboard.writeText(apiKey.secret);
+ showToast("API key copied to clipboard!", "info");
+ setCopied(true);
+ setTimeout(() => {
+ setCopied(false);
+ }, 1200);
+ };
+
+ if (loading) {
+ return (
+
+
+
+
+ Generate an API Key for your AnythingLLM instance.
+
+
+
+
+
+ );
+ }
+
+ if (!apiKey) {
+ return (
+
+
+
+
+ Generate an API Key for your AnythingLLM instance.
+
+
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/Modals/Settings/index.jsx b/frontend/src/components/Modals/Settings/index.jsx
index 591b06a6..a531c507 100644
--- a/frontend/src/components/Modals/Settings/index.jsx
+++ b/frontend/src/components/Modals/Settings/index.jsx
@@ -8,6 +8,7 @@ import useUser from "../../../hooks/useUser";
import VectorDBSelection from "./VectorDbs";
import LLMSelection from "./LLMSelection";
import Appearance from "./Appearance";
+import ApiKey from "./ApiKey";
export const TABS = {
llm: LLMSelection,
@@ -16,6 +17,7 @@ export const TABS = {
multiuser: MultiUserMode,
vectordb: VectorDBSelection,
appearance: Appearance,
+ apikey: ApiKey,
};
const noop = () => false;
diff --git a/frontend/src/components/Sidebar/SettingsOverlay/index.jsx b/frontend/src/components/Sidebar/SettingsOverlay/index.jsx
index 794968f3..f71f8921 100644
--- a/frontend/src/components/Sidebar/SettingsOverlay/index.jsx
+++ b/frontend/src/components/Sidebar/SettingsOverlay/index.jsx
@@ -7,6 +7,7 @@ import {
Database,
MessageSquare,
Eye,
+ Key,
} from "react-feather";
import SystemSettingsModal, {
useSystemSettingsModal,
@@ -127,6 +128,12 @@ export default function SettingsOverlay() {
isActive={tab === "multiuser"}
onClick={() => selectTab("multiuser")}
/>
+
}
+ isActive={tab === "apikey"}
+ onClick={() => selectTab("apikey")}
+ />
>
)}
diff --git a/frontend/src/models/admin.js b/frontend/src/models/admin.js
index e98a1870..1c78f07a 100644
--- a/frontend/src/models/admin.js
+++ b/frontend/src/models/admin.js
@@ -232,6 +232,51 @@ const Admin = {
return { success: false, error: e.message };
});
},
+
+ // API Keys
+ getApiKeys: async function () {
+ return fetch(`${API_BASE}/admin/api-keys`, {
+ method: "GET",
+ headers: baseHeaders(),
+ })
+ .then((res) => {
+ if (!res.ok) {
+ throw new Error(res.statusText || "Error fetching api keys.");
+ }
+ return res.json();
+ })
+ .catch((e) => {
+ console.error(e);
+ return { apiKeys: [], error: e.message };
+ });
+ },
+ generateApiKey: async function () {
+ return fetch(`${API_BASE}/admin/generate-api-key`, {
+ method: "POST",
+ headers: baseHeaders(),
+ })
+ .then((res) => {
+ if (!res.ok) {
+ throw new Error(res.statusText || "Error generating api key.");
+ }
+ return res.json();
+ })
+ .catch((e) => {
+ console.error(e);
+ return { apiKey: null, error: e.message };
+ });
+ },
+ deleteApiKey: async function (apiKeyId = "") {
+ return fetch(`${API_BASE}/admin/delete-api-key/${apiKeyId}`, {
+ method: "DELETE",
+ headers: baseHeaders(),
+ })
+ .then((res) => res.ok)
+ .catch((e) => {
+ console.error(e);
+ return false;
+ });
+ },
};
export default Admin;
diff --git a/frontend/src/models/system.js b/frontend/src/models/system.js
index 2405d283..bcab69f3 100644
--- a/frontend/src/models/system.js
+++ b/frontend/src/models/system.js
@@ -220,6 +220,49 @@ const System = {
return { success: false, error: e.message };
});
},
+ getApiKey: async function () {
+ return fetch(`${API_BASE}/system/api-key`, {
+ method: "GET",
+ headers: baseHeaders(),
+ })
+ .then((res) => {
+ if (!res.ok) {
+ throw new Error(res.statusText || "Error fetching api key.");
+ }
+ return res.json();
+ })
+ .catch((e) => {
+ console.error(e);
+ return { apiKey: null, error: e.message };
+ });
+ },
+ generateApiKey: async function () {
+ return fetch(`${API_BASE}/system/generate-api-key`, {
+ method: "POST",
+ headers: baseHeaders(),
+ })
+ .then((res) => {
+ if (!res.ok) {
+ throw new Error(res.statusText || "Error generating api key.");
+ }
+ return res.json();
+ })
+ .catch((e) => {
+ console.error(e);
+ return { apiKey: null, error: e.message };
+ });
+ },
+ deleteApiKey: async function () {
+ return fetch(`${API_BASE}/system/api-key`, {
+ method: "DELETE",
+ headers: baseHeaders(),
+ })
+ .then((res) => res.ok)
+ .catch((e) => {
+ console.error(e);
+ return false;
+ });
+ },
};
export default System;
diff --git a/frontend/src/pages/Admin/ApiKeys/ApiKeyRow/index.jsx b/frontend/src/pages/Admin/ApiKeys/ApiKeyRow/index.jsx
new file mode 100644
index 00000000..b6645f3b
--- /dev/null
+++ b/frontend/src/pages/Admin/ApiKeys/ApiKeyRow/index.jsx
@@ -0,0 +1,69 @@
+import { useEffect, useRef, useState } from "react";
+import Admin from "../../../../models/admin";
+import showToast from "../../../../utils/toast";
+
+export default function ApiKeyRow({ apiKey }) {
+ const rowRef = useRef(null);
+ const [copied, setCopied] = useState(false);
+ const handleDelete = async () => {
+ if (
+ !window.confirm(
+ `Are you sure you want to deactivate this api key?\nAfter you do this it will not longer be useable.\n\nThis action is irreversible.`
+ )
+ )
+ return false;
+ if (rowRef?.current) {
+ rowRef.current.remove();
+ }
+ await Admin.deleteApiKey(apiKey.id);
+ showToast("API Key permanently deleted", "info");
+ };
+ const copyApiKey = () => {
+ if (!apiKey) return false;
+ window.navigator.clipboard.writeText(apiKey.secret);
+ showToast("API Key copied to clipboard", "success");
+ setCopied(true);
+ };
+
+ useEffect(() => {
+ function resetStatus() {
+ if (!copied) return false;
+ setTimeout(() => {
+ setCopied(false);
+ }, 3000);
+ }
+ resetStatus();
+ }, [copied]);
+
+ return (
+ <>
+
+
+ {apiKey.secret}
+ |
+
+ {apiKey.createdBy?.username || "unknown user"}
+ |
+ {apiKey.createdAt} |
+
+
+
+ |
+
+ >
+ );
+}
diff --git a/frontend/src/pages/Admin/ApiKeys/NewApiKeyModal/index.jsx b/frontend/src/pages/Admin/ApiKeys/NewApiKeyModal/index.jsx
new file mode 100644
index 00000000..ae59eff7
--- /dev/null
+++ b/frontend/src/pages/Admin/ApiKeys/NewApiKeyModal/index.jsx
@@ -0,0 +1,118 @@
+import React, { useEffect, useState } from "react";
+import { X } from "react-feather";
+import Admin from "../../../../models/admin";
+import paths from "../../../../utils/paths";
+const DIALOG_ID = `new-api-key-modal`;
+
+function hideModal() {
+ document.getElementById(DIALOG_ID)?.close();
+}
+
+export const NewApiKeyModalId = DIALOG_ID;
+export default function NewApiKeyModal() {
+ const [apiKey, setApiKey] = useState(null);
+ const [error, setError] = useState(null);
+ const [copied, setCopied] = useState(false);
+
+ const handleCreate = async (e) => {
+ setError(null);
+ e.preventDefault();
+ const { apiKey: newApiKey, error } = await Admin.generateApiKey();
+ if (!!newApiKey) setApiKey(newApiKey);
+ setError(error);
+ };
+ const copyApiKey = () => {
+ if (!apiKey) return false;
+ window.navigator.clipboard.writeText(apiKey.secret);
+ setCopied(true);
+ };
+ useEffect(() => {
+ function resetStatus() {
+ if (!copied) return false;
+ setTimeout(() => {
+ setCopied(false);
+ }, 3000);
+ }
+ resetStatus();
+ }, [copied]);
+
+ return (
+
+ );
+}
diff --git a/frontend/src/pages/Admin/ApiKeys/index.jsx b/frontend/src/pages/Admin/ApiKeys/index.jsx
new file mode 100644
index 00000000..f685b6c6
--- /dev/null
+++ b/frontend/src/pages/Admin/ApiKeys/index.jsx
@@ -0,0 +1,109 @@
+import { useEffect, useState } from "react";
+import Sidebar, { SidebarMobileHeader } from "../../../components/AdminSidebar";
+import { isMobile } from "react-device-detect";
+import * as Skeleton from "react-loading-skeleton";
+import "react-loading-skeleton/dist/skeleton.css";
+import { PlusCircle } from "react-feather";
+import usePrefersDarkMode from "../../../hooks/usePrefersDarkMode";
+import Admin from "../../../models/admin";
+import ApiKeyRow from "./ApiKeyRow";
+import NewApiKeyModal, { NewApiKeyModalId } from "./NewApiKeyModal";
+import paths from "../../../utils/paths";
+
+export default function AdminApiKeys() {
+ return (
+
+ {!isMobile &&
}
+
+ {isMobile &&
}
+
+
+
+
+ API Keys
+
+
+
+
+ API keys allow the holder to programmatically access and manage
+ this AnythingLLM instance.
+
+
+ Read the API documentation →
+
+
+
+
+
+
+
+ );
+}
+
+function ApiKeysContainer() {
+ const darkMode = usePrefersDarkMode();
+ const [loading, setLoading] = useState(true);
+ const [apiKeys, setApiKeys] = useState([]);
+ useEffect(() => {
+ async function fetchExistingKeys() {
+ const { apiKeys: foundKeys } = await Admin.getApiKeys();
+ setApiKeys(foundKeys);
+ setLoading(false);
+ }
+ fetchExistingKeys();
+ }, []);
+
+ if (loading) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
+
+ API Key
+ |
+
+ Created By
+ |
+
+ Created
+ |
+
+ Actions
+ |
+
+
+
+ {apiKeys.map((apiKey) => (
+
+ ))}
+
+
+ );
+}
diff --git a/frontend/src/utils/paths.js b/frontend/src/utils/paths.js
index 707d4976..4e1ab138 100644
--- a/frontend/src/utils/paths.js
+++ b/frontend/src/utils/paths.js
@@ -30,6 +30,9 @@ export default {
exports: () => {
return `${API_BASE.replace("/api", "")}/system/data-exports`;
},
+ apiDocs: () => {
+ return `${API_BASE}/docs`;
+ },
admin: {
system: () => {
return `/admin/system-preferences`;
@@ -49,5 +52,8 @@ export default {
appearance: () => {
return "/admin/appearance";
},
+ apiKeys: () => {
+ return "/admin/api-keys";
+ },
},
};
diff --git a/server/endpoints/admin.js b/server/endpoints/admin.js
index d27ccf23..e7201100 100644
--- a/server/endpoints/admin.js
+++ b/server/endpoints/admin.js
@@ -1,3 +1,4 @@
+const { ApiKey } = require("../models/apiKeys");
const { Document } = require("../models/documents");
const { Invite } = require("../models/invite");
const { SystemSettings } = require("../models/systemSettings");
@@ -8,8 +9,6 @@ const { WorkspaceChats } = require("../models/workspaceChats");
const { getVectorDbClass } = require("../utils/helpers");
const { userFromSession, reqBody } = require("../utils/http");
const { validatedRequest } = require("../utils/middleware/validatedRequest");
-const { setupLogoUploads } = require("../utils/files/multer");
-const { handleLogoUploads } = setupLogoUploads();
function adminEndpoints(app) {
if (!app) return;
@@ -345,6 +344,72 @@ function adminEndpoints(app) {
}
}
);
+
+ app.get("/admin/api-keys", [validatedRequest], async (request, response) => {
+ try {
+ const user = await userFromSession(request, response);
+ if (!user || user?.role !== "admin") {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ const apiKeys = await ApiKey.whereWithUser("id IS NOT NULL");
+ return response.status(200).json({
+ apiKeys,
+ error: null,
+ });
+ } catch (error) {
+ console.error(error);
+ response.status(500).json({
+ apiKey: null,
+ error: "Could not find an API Keys.",
+ });
+ }
+ });
+
+ app.post(
+ "/admin/generate-api-key",
+ [validatedRequest],
+ async (request, response) => {
+ try {
+ const user = await userFromSession(request, response);
+ if (!user || user?.role !== "admin") {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ const { apiKey, error } = await ApiKey.create(user.id);
+ return response.status(200).json({
+ apiKey,
+ error,
+ });
+ } catch (e) {
+ console.error(e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
+
+ app.delete(
+ "/admin/delete-api-key/:id",
+ [validatedRequest],
+ async (request, response) => {
+ try {
+ const { id } = request.params;
+ const user = await userFromSession(request, response);
+ if (!user || user?.role !== "admin") {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ await ApiKey.delete(`id = ${id}`);
+ return response.status(200).end();
+ } catch (e) {
+ console.error(e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
}
module.exports = { adminEndpoints };
diff --git a/server/endpoints/api/admin/index.js b/server/endpoints/api/admin/index.js
new file mode 100644
index 00000000..bc39e24e
--- /dev/null
+++ b/server/endpoints/api/admin/index.js
@@ -0,0 +1,642 @@
+const { Invite } = require("../../../models/invite");
+const { SystemSettings } = require("../../../models/systemSettings");
+const { User } = require("../../../models/user");
+const { Workspace } = require("../../../models/workspace");
+const { WorkspaceChats } = require("../../../models/workspaceChats");
+const { multiUserMode, reqBody } = require("../../../utils/http");
+const { validApiKey } = require("../../../utils/middleware/validApiKey");
+
+function apiAdminEndpoints(app) {
+ if (!app) return;
+
+ app.get("/v1/admin/is-multi-user-mode", [validApiKey], (_, response) => {
+ /*
+ #swagger.tags = ['Admin']
+ #swagger.description = 'Check to see if the instance is in multi-user-mode first. Methods are disabled until multi user mode is enabled via the UI.'
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ "isMultiUser": true
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ const isMultiUser = multiUserMode(response);
+ response.status(200).json({ isMultiUser });
+ });
+
+ app.get("/v1/admin/users", [validApiKey], async (request, response) => {
+ /*
+ #swagger.tags = ['Admin']
+ #swagger.description = 'Check to see if the instance is in multi-user-mode first. Methods are disabled until multi user mode is enabled via the UI.'
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ "users": [
+ {
+ username: "sample-sam",
+ role: 'default',
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ #swagger.responses[401] = {
+ description: "Instance is not in Multi-User mode. Method denied",
+ }
+ */
+ try {
+ if (!multiUserMode(response)) {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ const users = (await User.where()).map((user) => {
+ const { password, ...rest } = user;
+ return rest;
+ });
+ response.status(200).json({ users });
+ } catch (e) {
+ console.error(e);
+ response.sendStatus(500).end();
+ }
+ });
+
+ app.post("/v1/admin/users/new", [validApiKey], async (request, response) => {
+ /*
+ #swagger.tags = ['Admin']
+ #swagger.description = 'Create a new user with username and password. Methods are disabled until multi user mode is enabled via the UI.'
+ #swagger.requestBody = {
+ description: 'Key pair object that will define the new user to add to the system.',
+ required: true,
+ type: 'object',
+ content: {
+ "application/json": {
+ example: {
+ username: "sample-sam",
+ password: 'hunter2',
+ role: 'default | admin'
+ }
+ }
+ }
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ user: {
+ id: 1,
+ username: 'sample-sam',
+ role: 'default',
+ },
+ error: null,
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ #swagger.responses[401] = {
+ description: "Instance is not in Multi-User mode. Method denied",
+ }
+ */
+ try {
+ if (!multiUserMode(response)) {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ const newUserParams = reqBody(request);
+ const { user: newUser, error } = await User.create(newUserParams);
+ response.status(200).json({ user: newUser, error });
+ } catch (e) {
+ console.error(e);
+ response.sendStatus(500).end();
+ }
+ });
+
+ app.post("/v1/admin/users/:id", [validApiKey], async (request, response) => {
+ /*
+ #swagger.tags = ['Admin']
+ #swagger.path = '/v1/admin/users/{id}'
+ #swagger.parameters['id'] = {
+ in: 'path',
+ description: 'id of the user in the database.',
+ required: true,
+ type: 'string'
+ }
+ #swagger.description = 'Update existing user settings. Methods are disabled until multi user mode is enabled via the UI.'
+ #swagger.requestBody = {
+ description: 'Key pair object that will update the found user. All fields are optional and will not update unless specified.',
+ required: true,
+ type: 'object',
+ content: {
+ "application/json": {
+ example: {
+ username: "sample-sam",
+ password: 'hunter2',
+ role: 'default | admin',
+ suspended: 0,
+ }
+ }
+ }
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ success: true,
+ error: null,
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ #swagger.responses[401] = {
+ description: "Instance is not in Multi-User mode. Method denied",
+ }
+ */
+ try {
+ if (!multiUserMode(response)) {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ const { id } = request.params;
+ const updates = reqBody(request);
+ const { success, error } = await User.update(id, updates);
+ response.status(200).json({ success, error });
+ } catch (e) {
+ console.error(e);
+ response.sendStatus(500).end();
+ }
+ });
+
+ app.delete(
+ "/v1/admin/users/:id",
+ [validApiKey],
+ async (request, response) => {
+ /*
+ #swagger.tags = ['Admin']
+ #swagger.description = 'Delete existing user by id. Methods are disabled until multi user mode is enabled via the UI.'
+ #swagger.path = '/v1/admin/users/{id}'
+ #swagger.parameters['id'] = {
+ in: 'path',
+ description: 'id of the user in the database.',
+ required: true,
+ type: 'string'
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ success: true,
+ error: null,
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ #swagger.responses[401] = {
+ description: "Instance is not in Multi-User mode. Method denied",
+ }
+ */
+ try {
+ if (!multiUserMode(response)) {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ const { id } = request.params;
+ await User.delete(`id = ${id}`);
+ response.status(200).json({ success: true, error: null });
+ } catch (e) {
+ console.error(e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
+
+ app.get("/v1/admin/invites", [validApiKey], async (request, response) => {
+ /*
+ #swagger.tags = ['Admin']
+ #swagger.description = 'List all existing invitations to instance regardless of status. Methods are disabled until multi user mode is enabled via the UI.'
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ "invites": [
+ {
+ id: 1,
+ status: "pending",
+ code: 'abc-123',
+ claimedBy: null
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ #swagger.responses[401] = {
+ description: "Instance is not in Multi-User mode. Method denied",
+ }
+ */
+ try {
+ if (!multiUserMode(response)) {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ const invites = await Invite.whereWithUsers();
+ response.status(200).json({ invites });
+ } catch (e) {
+ console.error(e);
+ response.sendStatus(500).end();
+ }
+ });
+
+ app.post("/v1/admin/invite/new", [validApiKey], async (request, response) => {
+ /*
+ #swagger.tags = ['Admin']
+ #swagger.description = 'Create a new invite code for someone to use to register with instance. Methods are disabled until multi user mode is enabled via the UI.'
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ invite: {
+ id: 1,
+ status: "pending",
+ code: 'abc-123',
+ },
+ error: null,
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ #swagger.responses[401] = {
+ description: "Instance is not in Multi-User mode. Method denied",
+ }
+ */
+ try {
+ if (!multiUserMode(response)) {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ const { invite, error } = await Invite.create();
+ response.status(200).json({ invite, error });
+ } catch (e) {
+ console.error(e);
+ response.sendStatus(500).end();
+ }
+ });
+
+ app.delete(
+ "/v1/admin/invite/:id",
+ [validApiKey],
+ async (request, response) => {
+ /*
+ #swagger.tags = ['Admin']
+ #swagger.description = 'Deactivates (soft-delete) invite by id. Methods are disabled until multi user mode is enabled via the UI.'
+ #swagger.path = '/v1/admin/invite/{id}'
+ #swagger.parameters['id'] = {
+ in: 'path',
+ description: 'id of the invite in the database.',
+ required: true,
+ type: 'string'
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ success: true,
+ error: null,
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ #swagger.responses[401] = {
+ description: "Instance is not in Multi-User mode. Method denied",
+ }
+ */
+ try {
+ if (!multiUserMode(response)) {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ const { id } = request.params;
+ const { success, error } = await Invite.deactivate(id);
+ response.status(200).json({ success, error });
+ } catch (e) {
+ console.error(e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
+
+ app.post(
+ "/v1/admin/workspaces/:workspaceId/update-users",
+ [validApiKey],
+ async (request, response) => {
+ /*
+ #swagger.tags = ['Admin']
+ #swagger.path = '/v1/admin/workspaces/{workspaceId}/update-users'
+ #swagger.parameters['workspaceId'] = {
+ in: 'path',
+ description: 'id of the workspace in the database.',
+ required: true,
+ type: 'string'
+ }
+ #swagger.description = 'Overwrite workspace permissions to only be accessible by the given user ids and admins. Methods are disabled until multi user mode is enabled via the UI.'
+ #swagger.requestBody = {
+ description: 'Entire array of user ids who can access the workspace. All fields are optional and will not update unless specified.',
+ required: true,
+ type: 'object',
+ content: {
+ "application/json": {
+ example: {
+ userIds: [1,2,4,12],
+ }
+ }
+ }
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ success: true,
+ error: null,
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ #swagger.responses[401] = {
+ description: "Instance is not in Multi-User mode. Method denied",
+ }
+ */
+ try {
+ if (!multiUserMode(response)) {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ const { workspaceId } = request.params;
+ const { userIds } = reqBody(request);
+ const { success, error } = await Workspace.updateUsers(
+ workspaceId,
+ userIds
+ );
+ response.status(200).json({ success, error });
+ } catch (e) {
+ console.error(e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
+
+ app.post(
+ "/v1/admin/workspace-chats",
+ [validApiKey],
+ async (request, response) => {
+ /*
+ #swagger.tags = ['Admin']
+ #swagger.description = 'All chats in the system ordered by most recent. Methods are disabled until multi user mode is enabled via the UI.'
+ #swagger.requestBody = {
+ description: 'Page offset to show of workspace chats. All fields are optional and will not update unless specified.',
+ required: false,
+ type: 'integer',
+ content: {
+ "application/json": {
+ example: {
+ offset: 2,
+ }
+ }
+ }
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ success: true,
+ error: null,
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ #swagger.responses[401] = {
+ description: "Instance is not in Multi-User mode. Method denied",
+ }
+ */
+ try {
+ if (!multiUserMode(response)) {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ const { offset = 0 } = reqBody(request);
+ const chats = await WorkspaceChats.whereWithData(`id >= ${offset}`, 20);
+ const hasPages = (await WorkspaceChats.count()) > 20;
+ response.status(200).json({ chats: chats.reverse(), hasPages });
+ } catch (e) {
+ console.error(e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
+
+ app.get("/v1/admin/preferences", [validApiKey], async (request, response) => {
+ /*
+ #swagger.tags = ['Admin']
+ #swagger.description = 'Show all multi-user preferences for instance. Methods are disabled until multi user mode is enabled via the UI.'
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ settings: {
+ users_can_delete_workspaces: true,
+ limit_user_messages: false,
+ message_limit: 10,
+ }
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ #swagger.responses[401] = {
+ description: "Instance is not in Multi-User mode. Method denied",
+ }
+ */
+ try {
+ if (!multiUserMode(response)) {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ const settings = {
+ users_can_delete_workspaces:
+ (await SystemSettings.get(`label = 'users_can_delete_workspaces'`))
+ ?.value === "true",
+ limit_user_messages:
+ (await SystemSettings.get(`label = 'limit_user_messages'`))?.value ===
+ "true",
+ message_limit:
+ Number(
+ (await SystemSettings.get(`label = 'message_limit'`))?.value
+ ) || 10,
+ };
+ response.status(200).json({ settings });
+ } catch (e) {
+ console.error(e);
+ response.sendStatus(500).end();
+ }
+ });
+
+ app.post(
+ "/v1/admin/preferences",
+ [validApiKey],
+ async (request, response) => {
+ /*
+ #swagger.tags = ['Admin']
+ #swagger.description = 'Update multi-user preferences for instance. Methods are disabled until multi user mode is enabled via the UI.'
+ #swagger.requestBody = {
+ description: 'Object with setting key and new value to set. All keys are optional and will not update unless specified.',
+ required: true,
+ type: 'object',
+ content: {
+ "application/json": {
+ example: {
+ users_can_delete_workspaces: false,
+ limit_user_messages: true,
+ message_limit: 5,
+ }
+ }
+ }
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ success: true,
+ error: null,
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ #swagger.responses[401] = {
+ description: "Instance is not in Multi-User mode. Method denied",
+ }
+ */
+ try {
+ if (!multiUserMode(response)) {
+ response.sendStatus(401).end();
+ return;
+ }
+
+ const updates = reqBody(request);
+ await SystemSettings.updateSettings(updates);
+ response.status(200).json({ success: true, error: null });
+ } catch (e) {
+ console.error(e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
+}
+
+module.exports = { apiAdminEndpoints };
diff --git a/server/endpoints/api/auth/index.js b/server/endpoints/api/auth/index.js
new file mode 100644
index 00000000..e58420b0
--- /dev/null
+++ b/server/endpoints/api/auth/index.js
@@ -0,0 +1,33 @@
+const { validApiKey } = require("../../../utils/middleware/validApiKey");
+
+function apiAuthEndpoints(app) {
+ if (!app) return;
+
+ app.get("/v1/auth", [validApiKey], (_, response) => {
+ /*
+ #swagger.tags = ['Authentication']
+ #swagger.description = 'Verify the attached Authentication header contains a valid API token.'
+ #swagger.responses[200] = {
+ description: 'Valid auth token was found.',
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ authenticated: true,
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ response.status(200).json({ authenticated: true });
+ });
+}
+
+module.exports = { apiAuthEndpoints };
diff --git a/server/endpoints/api/document/index.js b/server/endpoints/api/document/index.js
new file mode 100644
index 00000000..28de98a7
--- /dev/null
+++ b/server/endpoints/api/document/index.js
@@ -0,0 +1,194 @@
+const { Telemetry } = require("../../../models/telemetry");
+const { validApiKey } = require("../../../utils/middleware/validApiKey");
+const { setupMulter } = require("../../../utils/files/multer");
+const {
+ checkPythonAppAlive,
+ acceptedFileTypes,
+ processDocument,
+} = require("../../../utils/files/documentProcessor");
+const { viewLocalFiles } = require("../../../utils/files");
+const { handleUploads } = setupMulter();
+
+function apiDocumentEndpoints(app) {
+ if (!app) return;
+
+ app.post(
+ "/v1/document/upload",
+ [validApiKey],
+ handleUploads.single("file"),
+ async (request, response) => {
+ /*
+ #swagger.tags = ['Documents']
+ #swagger.description = 'Upload a new file to AnythingLLM to be parsed and prepared for embedding.'
+
+ #swagger.requestBody = {
+ description: 'File to be uploaded.',
+ required: true,
+ type: 'file',
+ content: {
+ "multipart/form-data": {
+ schema: {
+ type: 'object',
+ properties: {
+ file: {
+ type: 'string',
+ format: 'binary',
+ }
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ success: true,
+ error: null,
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ const { originalname } = request.file;
+ const processingOnline = await checkPythonAppAlive();
+
+ if (!processingOnline) {
+ response
+ .status(500)
+ .json({
+ success: false,
+ error: `Python processing API is not online. Document ${originalname} will not be processed automatically.`,
+ })
+ .end();
+ }
+
+ const { success, reason } = await processDocument(originalname);
+ if (!success) {
+ response.status(500).json({ success: false, error: reason }).end();
+ }
+
+ console.log(
+ `Document ${originalname} uploaded processed and successfully. It is now available in documents.`
+ );
+ await Telemetry.sendTelemetry("document_uploaded");
+ response.status(200).json({ success: true, error: null });
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
+
+ app.get("/v1/documents", [validApiKey], async (_, response) => {
+ /*
+ #swagger.tags = ['Documents']
+ #swagger.description = 'List of all locally-stored documents in instance'
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ "localFiles": {
+ "name": "documents",
+ "type": "folder",
+ items: [
+ {
+ "name": "my-stored-document.json",
+ "type": "file",
+ "id": "bb07c334-4dab-4419-9462-9d00065a49a1",
+ "url": "file://my-stored-document.txt",
+ "title": "my-stored-document.txt",
+ "cached": false
+ },
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ const localFiles = await viewLocalFiles();
+ response.status(200).json({ localFiles });
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ });
+
+ app.get(
+ "/v1/document/accepted-file-types",
+ [validApiKey],
+ async (_, response) => {
+ /*
+ #swagger.tags = ['Documents']
+ #swagger.description = 'Check available filetypes and MIMEs that can be uploaded.'
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ "types": {
+ "application/mbox": [
+ ".mbox"
+ ],
+ "application/pdf": [
+ ".pdf"
+ ],
+ "application/vnd.oasis.opendocument.text": [
+ ".odt"
+ ],
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": [
+ ".docx"
+ ],
+ "text/plain": [
+ ".txt",
+ ".md"
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ const types = await acceptedFileTypes();
+ if (!types) {
+ response.sendStatus(404).end();
+ return;
+ }
+
+ response.status(200).json({ types });
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
+}
+
+module.exports = { apiDocumentEndpoints };
diff --git a/server/endpoints/api/index.js b/server/endpoints/api/index.js
new file mode 100644
index 00000000..e5149ad7
--- /dev/null
+++ b/server/endpoints/api/index.js
@@ -0,0 +1,21 @@
+const { useSwagger } = require("../../swagger/utils");
+const { apiAdminEndpoints } = require("./admin");
+const { apiAuthEndpoints } = require("./auth");
+const { apiDocumentEndpoints } = require("./document");
+const { apiSystemEndpoints } = require("./system");
+const { apiWorkspaceEndpoints } = require("./workspace");
+
+// All endpoints must be documented and pass through the validApiKey Middleware.
+// How to JSDoc an endpoint
+// https://www.npmjs.com/package/swagger-autogen#openapi-3x
+function developerEndpoints(app, router) {
+ if (!router) return;
+ useSwagger(app);
+ apiAuthEndpoints(router);
+ apiAdminEndpoints(router);
+ apiSystemEndpoints(router);
+ apiWorkspaceEndpoints(router);
+ apiDocumentEndpoints(router);
+}
+
+module.exports = { developerEndpoints };
diff --git a/server/endpoints/api/system/index.js b/server/endpoints/api/system/index.js
new file mode 100644
index 00000000..dd5f59b7
--- /dev/null
+++ b/server/endpoints/api/system/index.js
@@ -0,0 +1,153 @@
+const { SystemSettings } = require("../../../models/systemSettings");
+const { getVectorDbClass } = require("../../../utils/helpers");
+const { dumpENV, updateENV } = require("../../../utils/helpers/updateENV");
+const { reqBody } = require("../../../utils/http");
+const { validApiKey } = require("../../../utils/middleware/validApiKey");
+
+function apiSystemEndpoints(app) {
+ if (!app) return;
+
+ app.get("/v1/system/env-dump", async (_, response) => {
+ /*
+ #swagger.tags = ['System Settings']
+ #swagger.description = 'Dump all settings to file storage'
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ if (process.env.NODE_ENV !== "production")
+ return response.sendStatus(200).end();
+ await dumpENV();
+ response.sendStatus(200).end();
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ });
+
+ app.get("/v1/system", [validApiKey], async (_, response) => {
+ /*
+ #swagger.tags = ['System Settings']
+ #swagger.description = 'Get all current system settings that are defined.'
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ "settings": {
+ "VectorDB": "pinecone",
+ "PineConeEnvironment": "us-west4-gcp-free",
+ "PineConeKey": true,
+ "PineConeIndex": "my-pinecone-index",
+ "LLMProvider": "azure",
+ "[KEY_NAME]": "KEY_VALUE",
+ }
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ const settings = await SystemSettings.currentSettings();
+ response.status(200).json({ settings });
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ });
+
+ app.get("/v1/system/vector-count", [validApiKey], async (_, response) => {
+ /*
+ #swagger.tags = ['System Settings']
+ #swagger.description = 'Number of all vectors in connected vector database'
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ "vectorCount": 5450
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ const VectorDb = getVectorDbClass();
+ const vectorCount = await VectorDb.totalIndicies();
+ response.status(200).json({ vectorCount });
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ });
+
+ app.post(
+ "/v1/system/update-env",
+ [validApiKey],
+ async (request, response) => {
+ /*
+ #swagger.tags = ['System Settings']
+ #swagger.description = 'Update a system setting or preference.'
+ #swagger.requestBody = {
+ description: 'Key pair object that matches a valid setting and value. Get keys from GET /v1/system or refer to codebase.',
+ required: true,
+ type: 'object',
+ content: {
+ "application/json": {
+ example: {
+ VectorDB: "lancedb",
+ AnotherKey: "updatedValue"
+ }
+ }
+ }
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ newValues: {"[ENV_KEY]": 'Value'},
+ error: 'error goes here, otherwise null'
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ const body = reqBody(request);
+ const { newValues, error } = updateENV(body);
+ if (process.env.NODE_ENV === "production") await dumpENV();
+ response.status(200).json({ newValues, error });
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
+}
+
+module.exports = { apiSystemEndpoints };
diff --git a/server/endpoints/api/workspace/index.js b/server/endpoints/api/workspace/index.js
new file mode 100644
index 00000000..35a16edc
--- /dev/null
+++ b/server/endpoints/api/workspace/index.js
@@ -0,0 +1,430 @@
+const { Document } = require("../../../models/documents");
+const { Telemetry } = require("../../../models/telemetry");
+const { DocumentVectors } = require("../../../models/vectors");
+const { Workspace } = require("../../../models/workspace");
+const { WorkspaceChats } = require("../../../models/workspaceChats");
+const { convertToChatHistory } = require("../../../utils/chats");
+const { getVectorDbClass } = require("../../../utils/helpers");
+const { multiUserMode, reqBody } = require("../../../utils/http");
+const { validApiKey } = require("../../../utils/middleware/validApiKey");
+
+function apiWorkspaceEndpoints(app) {
+ if (!app) return;
+
+ app.post("/v1/workspace/new", [validApiKey], async (request, response) => {
+ /*
+ #swagger.tags = ['Workspaces']
+ #swagger.description = 'Create a new workspace'
+ #swagger.requestBody = {
+ description: 'JSON object containing new display name of workspace.',
+ required: true,
+ type: 'object',
+ content: {
+ "application/json": {
+ example: {
+ name: "My New Workspace",
+ }
+ }
+ }
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ workspace: {
+ "id": 79,
+ "name": "Sample workspace",
+ "slug": "sample-workspace",
+ "createdAt": "2023-08-17 00:45:03",
+ "openAiTemp": null,
+ "lastUpdatedAt": "2023-08-17 00:45:03",
+ "openAiHistory": 20,
+ "openAiPrompt": null
+ },
+ message: 'Workspace created'
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ const { name = null } = reqBody(request);
+ const { workspace, message } = await Workspace.new(name);
+ await Telemetry.sendTelemetry("workspace_created", {
+ multiUserMode: multiUserMode(response),
+ LLMSelection: process.env.LLM_PROVIDER || "openai",
+ VectorDbSelection: process.env.VECTOR_DB || "pinecone",
+ });
+ response.status(200).json({ workspace, message });
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ });
+
+ app.get("/v1/workspaces", [validApiKey], async (request, response) => {
+ /*
+ #swagger.tags = ['Workspaces']
+ #swagger.description = 'List all current workspaces'
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ workspaces: [
+ {
+ "id": 79,
+ "name": "Sample workspace",
+ "slug": "sample-workspace",
+ "createdAt": "2023-08-17 00:45:03",
+ "openAiTemp": null,
+ "lastUpdatedAt": "2023-08-17 00:45:03",
+ "openAiHistory": 20,
+ "openAiPrompt": null
+ }
+ ],
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ const workspaces = await Workspace.where();
+ response.status(200).json({ workspaces });
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ });
+
+ app.get("/v1/workspace/:slug", [validApiKey], async (request, response) => {
+ /*
+ #swagger.tags = ['Workspaces']
+ #swagger.description = 'Get a workspace by its unique slug.'
+ #swagger.path = '/v1/workspace/{slug}'
+ #swagger.parameters['slug'] = {
+ in: 'path',
+ description: 'Unique slug of workspace to find',
+ required: true,
+ type: 'string'
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ workspace: {
+ "id": 79,
+ "name": "My workspace",
+ "slug": "my-workspace-123",
+ "createdAt": "2023-08-17 00:45:03",
+ "openAiTemp": null,
+ "lastUpdatedAt": "2023-08-17 00:45:03",
+ "openAiHistory": 20,
+ "openAiPrompt": null,
+ "documents": []
+ }
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ const { slug } = request.params;
+ const workspace = await Workspace.get(`slug = '${slug}'`);
+ response.status(200).json({ workspace });
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ });
+
+ app.delete(
+ "/v1/workspace/:slug",
+ [validApiKey],
+ async (request, response) => {
+ /*
+ #swagger.tags = ['Workspaces']
+ #swagger.description = 'Deletes a workspace by its slug.'
+ #swagger.path = '/v1/workspace/{slug}'
+ #swagger.parameters['slug'] = {
+ in: 'path',
+ description: 'Unique slug of workspace to delete',
+ required: true,
+ type: 'string'
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ const { slug = "" } = request.params;
+ const VectorDb = getVectorDbClass();
+ const workspace = await Workspace.get(`slug = '${slug}'`);
+
+ if (!workspace) {
+ response.sendStatus(400).end();
+ return;
+ }
+
+ await Workspace.delete(`slug = '${slug.toLowerCase()}'`);
+ await DocumentVectors.deleteForWorkspace(workspace.id);
+ await Document.delete(`workspaceId = ${Number(workspace.id)}`);
+ await WorkspaceChats.delete(`workspaceId = ${Number(workspace.id)}`);
+ try {
+ await VectorDb["delete-namespace"]({ namespace: slug });
+ } catch (e) {
+ console.error(e.message);
+ }
+ response.sendStatus(200).end();
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
+
+ app.post(
+ "/v1/workspace/:slug/update",
+ [validApiKey],
+ async (request, response) => {
+ /*
+ #swagger.tags = ['Workspaces']
+ #swagger.description = 'Update workspace settings by its unique slug.'
+ #swagger.path = '/v1/workspace/{slug}/update'
+ #swagger.parameters['slug'] = {
+ in: 'path',
+ description: 'Unique slug of workspace to find',
+ required: true,
+ type: 'string'
+ }
+ #swagger.requestBody = {
+ description: 'JSON object containing new settings to update a workspace. All keys are optional and will not update unless provided',
+ required: true,
+ type: 'object',
+ content: {
+ "application/json": {
+ example: {
+ "name": 'Updated Workspace Name',
+ "openAiTemp": 0.2,
+ "openAiHistory": 20,
+ "openAiPrompt": "Respond to all inquires and questions in binary - do not respond in any other format."
+ }
+ }
+ }
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ workspace: {
+ "id": 79,
+ "name": "My workspace",
+ "slug": "my-workspace-123",
+ "createdAt": "2023-08-17 00:45:03",
+ "openAiTemp": null,
+ "lastUpdatedAt": "2023-08-17 00:45:03",
+ "openAiHistory": 20,
+ "openAiPrompt": null,
+ "documents": []
+ },
+ message: null,
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ const { slug = null } = request.params;
+ const data = reqBody(request);
+ const currWorkspace = await Workspace.get(`slug = '${slug}'`);
+
+ if (!currWorkspace) {
+ response.sendStatus(400).end();
+ return;
+ }
+
+ const { workspace, message } = await Workspace.update(
+ currWorkspace.id,
+ data
+ );
+ response.status(200).json({ workspace, message });
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
+
+ app.get(
+ "/v1/workspace/:slug/chats",
+ [validApiKey],
+ async (request, response) => {
+ /*
+ #swagger.tags = ['Workspaces']
+ #swagger.description = 'Get a workspaces chats regardless of user by its unique slug.'
+ #swagger.path = '/v1/workspace/{slug}/chats'
+ #swagger.parameters['slug'] = {
+ in: 'path',
+ description: 'Unique slug of workspace to find',
+ required: true,
+ type: 'string'
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ history: [
+ {
+ "role": "user",
+ "content": "What is AnythingLLM?",
+ "sentAt": 1692851630
+ },
+ {
+ "role": "assistant",
+ "content": "AnythingLLM is a platform that allows you to convert notes, PDFs, and other source materials into a chatbot. It ensures privacy, cites its answers, and allows multiple people to interact with the same documents simultaneously. It is particularly useful for businesses to enhance the visibility and readability of various written communications such as SOPs, contracts, and sales calls. You can try it out with a free trial to see if it meets your business needs.",
+ "sources": [{"source": "object about source document and snippets used"}]
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ const { slug } = request.params;
+ const workspace = await Workspace.get(`slug = '${slug}'`);
+
+ if (!workspace) {
+ response.sendStatus(400).end();
+ return;
+ }
+
+ const history = await WorkspaceChats.forWorkspace(workspace.id);
+ response.status(200).json({ history: convertToChatHistory(history) });
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
+
+ app.post(
+ "/v1/workspace/:slug/update-embeddings",
+ [validApiKey],
+ async (request, response) => {
+ /*
+ #swagger.tags = ['Workspaces']
+ #swagger.description = 'Add or remove documents from a workspace by its unique slug.'
+ #swagger.path = '/v1/workspace/{slug}/update-embeddings'
+ #swagger.parameters['slug'] = {
+ in: 'path',
+ description: 'Unique slug of workspace to find',
+ required: true,
+ type: 'string'
+ }
+ #swagger.requestBody = {
+ description: 'JSON object of additions and removals of documents to add to update a workspace. The value should be the folder + filename with the exclusions of the top-level documents path.',
+ required: true,
+ type: 'object',
+ content: {
+ "application/json": {
+ example: {
+ adds: [],
+ deletes: ["custom-documents/anythingllm-hash.json"]
+ }
+ }
+ }
+ }
+ #swagger.responses[200] = {
+ content: {
+ "application/json": {
+ schema: {
+ type: 'object',
+ example: {
+ workspace: {
+ "id": 79,
+ "name": "My workspace",
+ "slug": "my-workspace-123",
+ "createdAt": "2023-08-17 00:45:03",
+ "openAiTemp": null,
+ "lastUpdatedAt": "2023-08-17 00:45:03",
+ "openAiHistory": 20,
+ "openAiPrompt": null,
+ "documents": []
+ },
+ message: null,
+ }
+ }
+ }
+ }
+ }
+ #swagger.responses[403] = {
+ schema: {
+ "$ref": "#/definitions/InvalidAPIKey"
+ }
+ }
+ */
+ try {
+ const { slug = null } = request.params;
+ const { adds = [], deletes = [] } = reqBody(request);
+ const currWorkspace = await Workspace.get(`slug = '${slug}'`);
+
+ if (!currWorkspace) {
+ response.sendStatus(400).end();
+ return;
+ }
+
+ await Document.removeDocuments(currWorkspace, deletes);
+ await Document.addDocuments(currWorkspace, adds);
+ const updatedWorkspace = await Workspace.get(`slug = '${slug}'`);
+ response.status(200).json({ workspace: updatedWorkspace });
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ }
+ );
+}
+
+module.exports = { apiWorkspaceEndpoints };
diff --git a/server/endpoints/system.js b/server/endpoints/system.js
index 72b525e0..85416f4c 100644
--- a/server/endpoints/system.js
+++ b/server/endpoints/system.js
@@ -36,6 +36,7 @@ const {
} = require("../utils/files/logo");
const { Telemetry } = require("../models/telemetry");
const { WelcomeMessages } = require("../models/welcomeMessages");
+const { ApiKey } = require("../models/apiKeys");
function systemEndpoints(app) {
if (!app) return;
@@ -58,57 +59,7 @@ function systemEndpoints(app) {
app.get("/setup-complete", async (_, response) => {
try {
- const llmProvider = process.env.LLM_PROVIDER || "openai";
- const vectorDB = process.env.VECTOR_DB || "pinecone";
- const results = {
- CanDebug: !!!process.env.NO_DEBUG,
- RequiresAuth: !!process.env.AUTH_TOKEN,
- AuthToken: !!process.env.AUTH_TOKEN,
- JWTSecret: !!process.env.JWT_SECRET,
- StorageDir: process.env.STORAGE_DIR,
- MultiUserMode: await SystemSettings.isMultiUserMode(),
- VectorDB: vectorDB,
- ...(vectorDB === "pinecone"
- ? {
- PineConeEnvironment: process.env.PINECONE_ENVIRONMENT,
- PineConeKey: !!process.env.PINECONE_API_KEY,
- PineConeIndex: process.env.PINECONE_INDEX,
- }
- : {}),
- ...(vectorDB === "chroma"
- ? {
- ChromaEndpoint: process.env.CHROMA_ENDPOINT,
- }
- : {}),
- ...(vectorDB === "weaviate"
- ? {
- WeaviateEndpoint: process.env.WEAVIATE_ENDPOINT,
- WeaviateApiKey: process.env.WEAVIATE_API_KEY,
- }
- : {}),
- ...(vectorDB === "qdrant"
- ? {
- QdrantEndpoint: process.env.QDRANT_ENDPOINT,
- QdrantApiKey: process.env.QDRANT_API_KEY,
- }
- : {}),
- LLMProvider: llmProvider,
- ...(llmProvider === "openai"
- ? {
- OpenAiKey: !!process.env.OPEN_AI_KEY,
- OpenAiModelPref: process.env.OPEN_MODEL_PREF || "gpt-3.5-turbo",
- }
- : {}),
-
- ...(llmProvider === "azure"
- ? {
- AzureOpenAiEndpoint: process.env.AZURE_OPENAI_ENDPOINT,
- AzureOpenAiKey: !!process.env.AZURE_OPENAI_KEY,
- AzureOpenAiModelPref: process.env.OPEN_MODEL_PREF,
- AzureOpenAiEmbeddingModelPref: process.env.EMBEDDING_MODEL_PREF,
- }
- : {}),
- };
+ const results = await SystemSettings.currentSettings();
response.status(200).json({ results });
} catch (e) {
console.log(e.message, e);
@@ -526,6 +477,65 @@ function systemEndpoints(app) {
}
}
);
+
+ app.get("/system/api-key", [validatedRequest], async (_, response) => {
+ try {
+ if (response.locals.multiUserMode) {
+ return response.sendStatus(401).end();
+ }
+
+ const apiKey = await ApiKey.get("id IS NOT NULL");
+ return response.status(200).json({
+ apiKey,
+ error: null,
+ });
+ } catch (error) {
+ console.error(error);
+ response.status(500).json({
+ apiKey: null,
+ error: "Could not find an API Key.",
+ });
+ }
+ });
+
+ app.post(
+ "/system/generate-api-key",
+ [validatedRequest],
+ async (_, response) => {
+ try {
+ if (response.locals.multiUserMode) {
+ return response.sendStatus(401).end();
+ }
+
+ await ApiKey.delete();
+ const { apiKey, error } = await ApiKey.create();
+ return response.status(200).json({
+ apiKey,
+ error,
+ });
+ } catch (error) {
+ console.error(error);
+ response.status(500).json({
+ apiKey: null,
+ error: "Error generating api key.",
+ });
+ }
+ }
+ );
+
+ app.delete("/system/api-key", [validatedRequest], async (_, response) => {
+ try {
+ if (response.locals.multiUserMode) {
+ return response.sendStatus(401).end();
+ }
+
+ await ApiKey.delete();
+ return response.status(200).end();
+ } catch (error) {
+ console.error(error);
+ response.status(500).end();
+ }
+ });
}
module.exports = { systemEndpoints };
diff --git a/server/index.js b/server/index.js
index 590cc133..e2f54a3a 100644
--- a/server/index.js
+++ b/server/index.js
@@ -17,6 +17,7 @@ const { adminEndpoints } = require("./endpoints/admin");
const { inviteEndpoints } = require("./endpoints/invite");
const { utilEndpoints } = require("./endpoints/utils");
const { Telemetry } = require("./models/telemetry");
+const { developerEndpoints } = require("./endpoints/api");
const app = express();
const apiRouter = express.Router();
const FILE_LIMIT = "3GB";
@@ -38,6 +39,7 @@ chatEndpoints(apiRouter);
adminEndpoints(apiRouter);
inviteEndpoints(apiRouter);
utilEndpoints(apiRouter);
+developerEndpoints(app, apiRouter);
apiRouter.post("/v/:command", async (request, response) => {
try {
diff --git a/server/models/apiKeys.js b/server/models/apiKeys.js
new file mode 100644
index 00000000..01f66576
--- /dev/null
+++ b/server/models/apiKeys.js
@@ -0,0 +1,133 @@
+const { Telemetry } = require("./telemetry");
+
+const ApiKey = {
+ tablename: "api_keys",
+ writable: [],
+ colsInit: `
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ secret TEXT UNIQUE,
+ createdBy INTEGER DEFAULT NULL,
+ createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
+ lastUpdatedAt TEXT DEFAULT CURRENT_TIMESTAMP
+ `,
+ migrateTable: async function () {
+ const { checkForMigrations } = require("../utils/database");
+ console.log(`\x1b[34m[MIGRATING]\x1b[0m Checking for ApiKey migrations`);
+ const db = await this.db(false);
+ await checkForMigrations(this, db);
+ },
+ migrations: function () {
+ return [];
+ },
+ makeSecret: () => {
+ const uuidAPIKey = require("uuid-apikey");
+ return uuidAPIKey.create().apiKey;
+ },
+ db: async function (tracing = true) {
+ const sqlite3 = require("sqlite3").verbose();
+ const { open } = require("sqlite");
+
+ const db = await open({
+ filename: `${
+ !!process.env.STORAGE_DIR ? `${process.env.STORAGE_DIR}/` : "storage/"
+ }anythingllm.db`,
+ driver: sqlite3.Database,
+ });
+
+ await db.exec(
+ `PRAGMA foreign_keys = ON;CREATE TABLE IF NOT EXISTS ${this.tablename} (${this.colsInit})`
+ );
+
+ if (tracing) db.on("trace", (sql) => console.log(sql));
+ return db;
+ },
+ create: async function (createdByUserId = null) {
+ const db = await this.db();
+ const { id, success, message } = await db
+ .run(`INSERT INTO ${this.tablename} (secret, createdBy) VALUES(?, ?)`, [
+ this.makeSecret(),
+ createdByUserId,
+ ])
+ .then((res) => {
+ return { id: res.lastID, success: true, message: null };
+ })
+ .catch((error) => {
+ return { id: null, success: false, message: error.message };
+ });
+
+ if (!success) {
+ db.close();
+ console.error("FAILED TO CREATE API KEY.", message);
+ return { apiKey: null, error: message };
+ }
+
+ const apiKey = await db.get(
+ `SELECT * FROM ${this.tablename} WHERE id = ${id} `
+ );
+ db.close();
+ await Telemetry.sendTelemetry("api_key_created");
+ return { apiKey, error: null };
+ },
+ get: async function (clause = "") {
+ const db = await this.db();
+ const result = await db
+ .get(
+ `SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : clause}`
+ )
+ .then((res) => res || null);
+ if (!result) return null;
+ db.close();
+ return { ...result };
+ },
+ count: async function (clause = null) {
+ const db = await this.db();
+ const { count } = await db.get(
+ `SELECT COUNT(*) as count FROM ${this.tablename} ${
+ clause ? `WHERE ${clause}` : ""
+ } `
+ );
+ db.close();
+
+ return count;
+ },
+ delete: async function (clause = "") {
+ const db = await this.db();
+ await db.get(
+ `DELETE FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""}`
+ );
+ db.close();
+
+ return true;
+ },
+ where: async function (clause = "", limit = null) {
+ const db = await this.db();
+ const results = await db.all(
+ `SELECT * FROM ${this.tablename} ${clause ? `WHERE ${clause}` : ""} ${
+ !!limit ? `LIMIT ${limit}` : ""
+ }`
+ );
+ db.close();
+
+ return results;
+ },
+ whereWithUser: async function (clause = "", limit = null) {
+ const { User } = require("./user");
+ const apiKeys = await this.where(clause, limit);
+
+ for (const apiKey of apiKeys) {
+ if (!apiKey.createdBy) continue;
+ const user = await User.get(`id = ${apiKey.createdBy}`);
+ if (!user) continue;
+
+ apiKey.createdBy = {
+ id: user.id,
+ username: user.username,
+ role: user.role,
+ };
+ }
+
+ return apiKeys;
+ },
+};
+
+module.exports = { ApiKey };
diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js
index bc46dfe1..7e235630 100644
--- a/server/models/systemSettings.js
+++ b/server/models/systemSettings.js
@@ -1,3 +1,7 @@
+process.env.NODE_ENV === "development"
+ ? require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` })
+ : require("dotenv").config();
+
const SystemSettings = {
supportedFields: [
"multi_user_mode",
@@ -45,6 +49,59 @@ const SystemSettings = {
if (tracing) db.on("trace", (sql) => console.log(sql));
return db;
},
+ currentSettings: async function () {
+ const llmProvider = process.env.LLM_PROVIDER || "openai";
+ const vectorDB = process.env.VECTOR_DB || "pinecone";
+ return {
+ CanDebug: !!!process.env.NO_DEBUG,
+ RequiresAuth: !!process.env.AUTH_TOKEN,
+ AuthToken: !!process.env.AUTH_TOKEN,
+ JWTSecret: !!process.env.JWT_SECRET,
+ StorageDir: process.env.STORAGE_DIR,
+ MultiUserMode: await this.isMultiUserMode(),
+ VectorDB: vectorDB,
+ ...(vectorDB === "pinecone"
+ ? {
+ PineConeEnvironment: process.env.PINECONE_ENVIRONMENT,
+ PineConeKey: !!process.env.PINECONE_API_KEY,
+ PineConeIndex: process.env.PINECONE_INDEX,
+ }
+ : {}),
+ ...(vectorDB === "chroma"
+ ? {
+ ChromaEndpoint: process.env.CHROMA_ENDPOINT,
+ }
+ : {}),
+ ...(vectorDB === "weaviate"
+ ? {
+ WeaviateEndpoint: process.env.WEAVIATE_ENDPOINT,
+ WeaviateApiKey: process.env.WEAVIATE_API_KEY,
+ }
+ : {}),
+ ...(vectorDB === "qdrant"
+ ? {
+ QdrantEndpoint: process.env.QDRANT_ENDPOINT,
+ QdrantApiKey: process.env.QDRANT_API_KEY,
+ }
+ : {}),
+ LLMProvider: llmProvider,
+ ...(llmProvider === "openai"
+ ? {
+ OpenAiKey: !!process.env.OPEN_AI_KEY,
+ OpenAiModelPref: process.env.OPEN_MODEL_PREF || "gpt-3.5-turbo",
+ }
+ : {}),
+
+ ...(llmProvider === "azure"
+ ? {
+ AzureOpenAiEndpoint: process.env.AZURE_OPENAI_ENDPOINT,
+ AzureOpenAiKey: !!process.env.AZURE_OPENAI_KEY,
+ AzureOpenAiModelPref: process.env.OPEN_MODEL_PREF,
+ AzureOpenAiEmbeddingModelPref: process.env.EMBEDDING_MODEL_PREF,
+ }
+ : {}),
+ };
+ },
get: async function (clause = "") {
const db = await this.db();
const result = await db
diff --git a/server/nodemon.json b/server/nodemon.json
new file mode 100644
index 00000000..d778fe53
--- /dev/null
+++ b/server/nodemon.json
@@ -0,0 +1,6 @@
+{
+ "events": {
+ "start": "yarn swagger",
+ "restart": "yarn swagger"
+ }
+}
\ No newline at end of file
diff --git a/server/package.json b/server/package.json
index 15bbff6f..c1763380 100644
--- a/server/package.json
+++ b/server/package.json
@@ -10,9 +10,10 @@
"node": ">=18.12.1"
},
"scripts": {
- "dev": "NODE_ENV=development nodemon --ignore documents --ignore vector-cache --ignore storage --trace-warnings index.js",
+ "dev": "NODE_ENV=development nodemon --ignore documents --ignore vector-cache --ignore storage --ignore swagger --trace-warnings index.js",
"start": "NODE_ENV=production node index.js",
- "lint": "yarn prettier --write ./endpoints ./models ./utils index.js"
+ "lint": "yarn prettier --write ./endpoints ./models ./utils index.js",
+ "swagger": "node ./swagger/init.js"
},
"dependencies": {
"@azure/openai": "^1.0.0-beta.3",
@@ -41,6 +42,8 @@
"slugify": "^1.6.6",
"sqlite": "^4.2.1",
"sqlite3": "^5.1.6",
+ "swagger-autogen": "^2.23.5",
+ "swagger-ui-express": "^5.0.0",
"uuid": "^9.0.0",
"uuid-apikey": "^1.5.3",
"vectordb": "0.1.12",
@@ -50,4 +53,4 @@
"nodemon": "^2.0.22",
"prettier": "^2.4.1"
}
-}
+}
\ No newline at end of file
diff --git a/server/swagger/dark-swagger.css b/server/swagger/dark-swagger.css
new file mode 100644
index 00000000..574e1d95
--- /dev/null
+++ b/server/swagger/dark-swagger.css
@@ -0,0 +1,1722 @@
+@media only screen and (prefers-color-scheme: dark) {
+
+ a {
+ color: #8c8cfa;
+ }
+
+ ::-webkit-scrollbar-track-piece {
+ background-color: rgba(255, 255, 255, .2) !important;
+ }
+
+ ::-webkit-scrollbar-track {
+ background-color: rgba(255, 255, 255, .3) !important;
+ }
+
+ ::-webkit-scrollbar-thumb {
+ background-color: rgba(255, 255, 255, .5) !important;
+ }
+
+ embed[type="application/pdf"] {
+ filter: invert(90%);
+ }
+
+ html {
+ background: #1f1f1f !important;
+ box-sizing: border-box;
+ filter: contrast(100%) brightness(100%) saturate(100%);
+ overflow-y: scroll;
+ }
+
+ body {
+ background: #1f1f1f;
+ background-color: #1f1f1f;
+ background-image: none !important;
+ }
+
+ button,
+ input,
+ select,
+ textarea {
+ background-color: #1f1f1f;
+ color: #bfbfbf;
+ }
+
+ font,
+ html {
+ color: #bfbfbf;
+ }
+
+ .swagger-ui,
+ .swagger-ui section h3 {
+ color: #b5bac9;
+ }
+
+ .swagger-ui a {
+ background-color: transparent;
+ }
+
+ .swagger-ui mark {
+ background-color: #664b00;
+ color: #bfbfbf;
+ }
+
+ .swagger-ui legend {
+ color: inherit;
+ }
+
+ .swagger-ui .debug * {
+ outline: #e6da99 solid 1px;
+ }
+
+ .swagger-ui .debug-white * {
+ outline: #fff solid 1px;
+ }
+
+ .swagger-ui .debug-black * {
+ outline: #bfbfbf solid 1px;
+ }
+
+ .swagger-ui .debug-grid {
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTRDOTY4N0U2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTRDOTY4N0Q2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3NjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3NzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsBS+GMAAAAjSURBVHjaYvz//z8DLsD4gcGXiYEAGBIKGBne//fFpwAgwAB98AaF2pjlUQAAAABJRU5ErkJggg==) 0 0;
+ }
+
+ .swagger-ui .debug-grid-16 {
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODYyRjhERDU2N0YyMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODYyRjhERDQ2N0YyMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3QTY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3QjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvCS01IAAABMSURBVHjaYmR4/5+BFPBfAMFm/MBgx8RAGWCn1AAmSg34Q6kBDKMGMDCwICeMIemF/5QawEipAWwUhwEjMDvbAWlWkvVBwu8vQIABAEwBCph8U6c0AAAAAElFTkSuQmCC) 0 0;
+ }
+
+ .swagger-ui .debug-grid-8-solid {
+ background: url(data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAAAAD/4QMxaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzExMSA3OS4xNTgzMjUsIDIwMTUvMDkvMTAtMDE6MTA6MjAgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE1IChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkIxMjI0OTczNjdCMzExRTZCMkJDRTI0MDgxMDAyMTcxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkIxMjI0OTc0NjdCMzExRTZCMkJDRTI0MDgxMDAyMTcxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QjEyMjQ5NzE2N0IzMTFFNkIyQkNFMjQwODEwMDIxNzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QjEyMjQ5NzI2N0IzMTFFNkIyQkNFMjQwODEwMDIxNzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAbGhopHSlBJiZBQi8vL0JHPz4+P0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHAR0pKTQmND8oKD9HPzU/R0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0f/wAARCAAIAAgDASIAAhEBAxEB/8QAWQABAQAAAAAAAAAAAAAAAAAAAAYBAQEAAAAAAAAAAAAAAAAAAAIEEAEBAAMBAAAAAAAAAAAAAAABADECA0ERAAEDBQAAAAAAAAAAAAAAAAARITFBUWESIv/aAAwDAQACEQMRAD8AoOnTV1QTD7JJshP3vSM3P//Z) 0 0 #1c1c21;
+ }
+
+ .swagger-ui .debug-grid-16-solid {
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzY3MkJEN0U2N0M1MTFFNkIyQkNFMjQwODEwMDIxNzEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzY3MkJEN0Y2N0M1MTFFNkIyQkNFMjQwODEwMDIxNzEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3QzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3RDY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pve6J3kAAAAzSURBVHjaYvz//z8D0UDsMwMjSRoYP5Gq4SPNbRjVMEQ1fCRDg+in/6+J1AJUxsgAEGAA31BAJMS0GYEAAAAASUVORK5CYII=) 0 0 #1c1c21;
+ }
+
+ .swagger-ui .b--black {
+ border-color: #000;
+ }
+
+ .swagger-ui .b--near-black {
+ border-color: #121212;
+ }
+
+ .swagger-ui .b--dark-gray {
+ border-color: #333;
+ }
+
+ .swagger-ui .b--mid-gray {
+ border-color: #545454;
+ }
+
+ .swagger-ui .b--gray {
+ border-color: #787878;
+ }
+
+ .swagger-ui .b--silver {
+ border-color: #999;
+ }
+
+ .swagger-ui .b--light-silver {
+ border-color: #6e6e6e;
+ }
+
+ .swagger-ui .b--moon-gray {
+ border-color: #4d4d4d;
+ }
+
+ .swagger-ui .b--light-gray {
+ border-color: #2b2b2b;
+ }
+
+ .swagger-ui .b--near-white {
+ border-color: #242424;
+ }
+
+ .swagger-ui .b--white {
+ border-color: #1c1c21;
+ }
+
+ .swagger-ui .b--white-90 {
+ border-color: rgba(28, 28, 33, .9);
+ }
+
+ .swagger-ui .b--white-80 {
+ border-color: rgba(28, 28, 33, .8);
+ }
+
+ .swagger-ui .b--white-70 {
+ border-color: rgba(28, 28, 33, .7);
+ }
+
+ .swagger-ui .b--white-60 {
+ border-color: rgba(28, 28, 33, .6);
+ }
+
+ .swagger-ui .b--white-50 {
+ border-color: rgba(28, 28, 33, .5);
+ }
+
+ .swagger-ui .b--white-40 {
+ border-color: rgba(28, 28, 33, .4);
+ }
+
+ .swagger-ui .b--white-30 {
+ border-color: rgba(28, 28, 33, .3);
+ }
+
+ .swagger-ui .b--white-20 {
+ border-color: rgba(28, 28, 33, .2);
+ }
+
+ .swagger-ui .b--white-10 {
+ border-color: rgba(28, 28, 33, .1);
+ }
+
+ .swagger-ui .b--white-05 {
+ border-color: rgba(28, 28, 33, .05);
+ }
+
+ .swagger-ui .b--white-025 {
+ border-color: rgba(28, 28, 33, .024);
+ }
+
+ .swagger-ui .b--white-0125 {
+ border-color: rgba(28, 28, 33, .01);
+ }
+
+ .swagger-ui .b--black-90 {
+ border-color: rgba(0, 0, 0, .9);
+ }
+
+ .swagger-ui .b--black-80 {
+ border-color: rgba(0, 0, 0, .8);
+ }
+
+ .swagger-ui .b--black-70 {
+ border-color: rgba(0, 0, 0, .7);
+ }
+
+ .swagger-ui .b--black-60 {
+ border-color: rgba(0, 0, 0, .6);
+ }
+
+ .swagger-ui .b--black-50 {
+ border-color: rgba(0, 0, 0, .5);
+ }
+
+ .swagger-ui .b--black-40 {
+ border-color: rgba(0, 0, 0, .4);
+ }
+
+ .swagger-ui .b--black-30 {
+ border-color: rgba(0, 0, 0, .3);
+ }
+
+ .swagger-ui .b--black-20 {
+ border-color: rgba(0, 0, 0, .2);
+ }
+
+ .swagger-ui .b--black-10 {
+ border-color: rgba(0, 0, 0, .1);
+ }
+
+ .swagger-ui .b--black-05 {
+ border-color: rgba(0, 0, 0, .05);
+ }
+
+ .swagger-ui .b--black-025 {
+ border-color: rgba(0, 0, 0, .024);
+ }
+
+ .swagger-ui .b--black-0125 {
+ border-color: rgba(0, 0, 0, .01);
+ }
+
+ .swagger-ui .b--dark-red {
+ border-color: #bc2f36;
+ }
+
+ .swagger-ui .b--red {
+ border-color: #c83932;
+ }
+
+ .swagger-ui .b--light-red {
+ border-color: #ab3c2b;
+ }
+
+ .swagger-ui .b--orange {
+ border-color: #cc6e33;
+ }
+
+ .swagger-ui .b--purple {
+ border-color: #5e2ca5;
+ }
+
+ .swagger-ui .b--light-purple {
+ border-color: #672caf;
+ }
+
+ .swagger-ui .b--dark-pink {
+ border-color: #ab2b81;
+ }
+
+ .swagger-ui .b--hot-pink {
+ border-color: #c03086;
+ }
+
+ .swagger-ui .b--pink {
+ border-color: #8f2464;
+ }
+
+ .swagger-ui .b--light-pink {
+ border-color: #721d4d;
+ }
+
+ .swagger-ui .b--dark-green {
+ border-color: #1c6e50;
+ }
+
+ .swagger-ui .b--green {
+ border-color: #279b70;
+ }
+
+ .swagger-ui .b--light-green {
+ border-color: #228762;
+ }
+
+ .swagger-ui .b--navy {
+ border-color: #0d1d35;
+ }
+
+ .swagger-ui .b--dark-blue {
+ border-color: #20497e;
+ }
+
+ .swagger-ui .b--blue {
+ border-color: #4380d0;
+ }
+
+ .swagger-ui .b--light-blue {
+ border-color: #20517e;
+ }
+
+ .swagger-ui .b--lightest-blue {
+ border-color: #143a52;
+ }
+
+ .swagger-ui .b--washed-blue {
+ border-color: #0c312d;
+ }
+
+ .swagger-ui .b--washed-green {
+ border-color: #0f3d2c;
+ }
+
+ .swagger-ui .b--washed-red {
+ border-color: #411010;
+ }
+
+ .swagger-ui .b--transparent {
+ border-color: transparent;
+ }
+
+ .swagger-ui .b--gold,
+ .swagger-ui .b--light-yellow,
+ .swagger-ui .b--washed-yellow,
+ .swagger-ui .b--yellow {
+ border-color: #664b00;
+ }
+
+ .swagger-ui .shadow-1 {
+ box-shadow: rgba(0, 0, 0, .2) 0 0 4px 2px;
+ }
+
+ .swagger-ui .shadow-2 {
+ box-shadow: rgba(0, 0, 0, .2) 0 0 8px 2px;
+ }
+
+ .swagger-ui .shadow-3 {
+ box-shadow: rgba(0, 0, 0, .2) 2px 2px 4px 2px;
+ }
+
+ .swagger-ui .shadow-4 {
+ box-shadow: rgba(0, 0, 0, .2) 2px 2px 8px 0;
+ }
+
+ .swagger-ui .shadow-5 {
+ box-shadow: rgba(0, 0, 0, .2) 4px 4px 8px 0;
+ }
+
+ @media screen and (min-width: 30em) {
+ .swagger-ui .shadow-1-ns {
+ box-shadow: rgba(0, 0, 0, .2) 0 0 4px 2px;
+ }
+
+ .swagger-ui .shadow-2-ns {
+ box-shadow: rgba(0, 0, 0, .2) 0 0 8px 2px;
+ }
+
+ .swagger-ui .shadow-3-ns {
+ box-shadow: rgba(0, 0, 0, .2) 2px 2px 4px 2px;
+ }
+
+ .swagger-ui .shadow-4-ns {
+ box-shadow: rgba(0, 0, 0, .2) 2px 2px 8px 0;
+ }
+
+ .swagger-ui .shadow-5-ns {
+ box-shadow: rgba(0, 0, 0, .2) 4px 4px 8px 0;
+ }
+ }
+
+ @media screen and (max-width: 60em) and (min-width: 30em) {
+ .swagger-ui .shadow-1-m {
+ box-shadow: rgba(0, 0, 0, .2) 0 0 4px 2px;
+ }
+
+ .swagger-ui .shadow-2-m {
+ box-shadow: rgba(0, 0, 0, .2) 0 0 8px 2px;
+ }
+
+ .swagger-ui .shadow-3-m {
+ box-shadow: rgba(0, 0, 0, .2) 2px 2px 4px 2px;
+ }
+
+ .swagger-ui .shadow-4-m {
+ box-shadow: rgba(0, 0, 0, .2) 2px 2px 8px 0;
+ }
+
+ .swagger-ui .shadow-5-m {
+ box-shadow: rgba(0, 0, 0, .2) 4px 4px 8px 0;
+ }
+ }
+
+ @media screen and (min-width: 60em) {
+ .swagger-ui .shadow-1-l {
+ box-shadow: rgba(0, 0, 0, .2) 0 0 4px 2px;
+ }
+
+ .swagger-ui .shadow-2-l {
+ box-shadow: rgba(0, 0, 0, .2) 0 0 8px 2px;
+ }
+
+ .swagger-ui .shadow-3-l {
+ box-shadow: rgba(0, 0, 0, .2) 2px 2px 4px 2px;
+ }
+
+ .swagger-ui .shadow-4-l {
+ box-shadow: rgba(0, 0, 0, .2) 2px 2px 8px 0;
+ }
+
+ .swagger-ui .shadow-5-l {
+ box-shadow: rgba(0, 0, 0, .2) 4px 4px 8px 0;
+ }
+ }
+
+ .swagger-ui .black-05 {
+ color: rgba(191, 191, 191, .05);
+ }
+
+ .swagger-ui .bg-black-05 {
+ background-color: rgba(0, 0, 0, .05);
+ }
+
+ .swagger-ui .black-90,
+ .swagger-ui .hover-black-90:focus,
+ .swagger-ui .hover-black-90:hover {
+ color: rgba(191, 191, 191, .9);
+ }
+
+ .swagger-ui .black-80,
+ .swagger-ui .hover-black-80:focus,
+ .swagger-ui .hover-black-80:hover {
+ color: rgba(191, 191, 191, .8);
+ }
+
+ .swagger-ui .black-70,
+ .swagger-ui .hover-black-70:focus,
+ .swagger-ui .hover-black-70:hover {
+ color: rgba(191, 191, 191, .7);
+ }
+
+ .swagger-ui .black-60,
+ .swagger-ui .hover-black-60:focus,
+ .swagger-ui .hover-black-60:hover {
+ color: rgba(191, 191, 191, .6);
+ }
+
+ .swagger-ui .black-50,
+ .swagger-ui .hover-black-50:focus,
+ .swagger-ui .hover-black-50:hover {
+ color: rgba(191, 191, 191, .5);
+ }
+
+ .swagger-ui .black-40,
+ .swagger-ui .hover-black-40:focus,
+ .swagger-ui .hover-black-40:hover {
+ color: rgba(191, 191, 191, .4);
+ }
+
+ .swagger-ui .black-30,
+ .swagger-ui .hover-black-30:focus,
+ .swagger-ui .hover-black-30:hover {
+ color: rgba(191, 191, 191, .3);
+ }
+
+ .swagger-ui .black-20,
+ .swagger-ui .hover-black-20:focus,
+ .swagger-ui .hover-black-20:hover {
+ color: rgba(191, 191, 191, .2);
+ }
+
+ .swagger-ui .black-10,
+ .swagger-ui .hover-black-10:focus,
+ .swagger-ui .hover-black-10:hover {
+ color: rgba(191, 191, 191, .1);
+ }
+
+ .swagger-ui .hover-white-90:focus,
+ .swagger-ui .hover-white-90:hover,
+ .swagger-ui .white-90 {
+ color: rgba(255, 255, 255, .9);
+ }
+
+ .swagger-ui .hover-white-80:focus,
+ .swagger-ui .hover-white-80:hover,
+ .swagger-ui .white-80 {
+ color: rgba(255, 255, 255, .8);
+ }
+
+ .swagger-ui .hover-white-70:focus,
+ .swagger-ui .hover-white-70:hover,
+ .swagger-ui .white-70 {
+ color: rgba(255, 255, 255, .7);
+ }
+
+ .swagger-ui .hover-white-60:focus,
+ .swagger-ui .hover-white-60:hover,
+ .swagger-ui .white-60 {
+ color: rgba(255, 255, 255, .6);
+ }
+
+ .swagger-ui .hover-white-50:focus,
+ .swagger-ui .hover-white-50:hover,
+ .swagger-ui .white-50 {
+ color: rgba(255, 255, 255, .5);
+ }
+
+ .swagger-ui .hover-white-40:focus,
+ .swagger-ui .hover-white-40:hover,
+ .swagger-ui .white-40 {
+ color: rgba(255, 255, 255, .4);
+ }
+
+ .swagger-ui .hover-white-30:focus,
+ .swagger-ui .hover-white-30:hover,
+ .swagger-ui .white-30 {
+ color: rgba(255, 255, 255, .3);
+ }
+
+ .swagger-ui .hover-white-20:focus,
+ .swagger-ui .hover-white-20:hover,
+ .swagger-ui .white-20 {
+ color: rgba(255, 255, 255, .2);
+ }
+
+ .swagger-ui .hover-white-10:focus,
+ .swagger-ui .hover-white-10:hover,
+ .swagger-ui .white-10 {
+ color: rgba(255, 255, 255, .1);
+ }
+
+ .swagger-ui .hover-moon-gray:focus,
+ .swagger-ui .hover-moon-gray:hover,
+ .swagger-ui .moon-gray {
+ color: #ccc;
+ }
+
+ .swagger-ui .hover-light-gray:focus,
+ .swagger-ui .hover-light-gray:hover,
+ .swagger-ui .light-gray {
+ color: #ededed;
+ }
+
+ .swagger-ui .hover-near-white:focus,
+ .swagger-ui .hover-near-white:hover,
+ .swagger-ui .near-white {
+ color: #f5f5f5;
+ }
+
+ .swagger-ui .dark-red,
+ .swagger-ui .hover-dark-red:focus,
+ .swagger-ui .hover-dark-red:hover {
+ color: #e6999d;
+ }
+
+ .swagger-ui .hover-red:focus,
+ .swagger-ui .hover-red:hover,
+ .swagger-ui .red {
+ color: #e69d99;
+ }
+
+ .swagger-ui .hover-light-red:focus,
+ .swagger-ui .hover-light-red:hover,
+ .swagger-ui .light-red {
+ color: #e6a399;
+ }
+
+ .swagger-ui .hover-orange:focus,
+ .swagger-ui .hover-orange:hover,
+ .swagger-ui .orange {
+ color: #e6b699;
+ }
+
+ .swagger-ui .gold,
+ .swagger-ui .hover-gold:focus,
+ .swagger-ui .hover-gold:hover {
+ color: #e6d099;
+ }
+
+ .swagger-ui .hover-yellow:focus,
+ .swagger-ui .hover-yellow:hover,
+ .swagger-ui .yellow {
+ color: #e6da99;
+ }
+
+ .swagger-ui .hover-light-yellow:focus,
+ .swagger-ui .hover-light-yellow:hover,
+ .swagger-ui .light-yellow {
+ color: #ede6b6;
+ }
+
+ .swagger-ui .hover-purple:focus,
+ .swagger-ui .hover-purple:hover,
+ .swagger-ui .purple {
+ color: #b99ae4;
+ }
+
+ .swagger-ui .hover-light-purple:focus,
+ .swagger-ui .hover-light-purple:hover,
+ .swagger-ui .light-purple {
+ color: #bb99e6;
+ }
+
+ .swagger-ui .dark-pink,
+ .swagger-ui .hover-dark-pink:focus,
+ .swagger-ui .hover-dark-pink:hover {
+ color: #e699cc;
+ }
+
+ .swagger-ui .hot-pink,
+ .swagger-ui .hover-hot-pink:focus,
+ .swagger-ui .hover-hot-pink:hover,
+ .swagger-ui .hover-pink:focus,
+ .swagger-ui .hover-pink:hover,
+ .swagger-ui .pink {
+ color: #e699c7;
+ }
+
+ .swagger-ui .hover-light-pink:focus,
+ .swagger-ui .hover-light-pink:hover,
+ .swagger-ui .light-pink {
+ color: #edb6d5;
+ }
+
+ .swagger-ui .dark-green,
+ .swagger-ui .green,
+ .swagger-ui .hover-dark-green:focus,
+ .swagger-ui .hover-dark-green:hover,
+ .swagger-ui .hover-green:focus,
+ .swagger-ui .hover-green:hover {
+ color: #99e6c9;
+ }
+
+ .swagger-ui .hover-light-green:focus,
+ .swagger-ui .hover-light-green:hover,
+ .swagger-ui .light-green {
+ color: #a1e8ce;
+ }
+
+ .swagger-ui .hover-navy:focus,
+ .swagger-ui .hover-navy:hover,
+ .swagger-ui .navy {
+ color: #99b8e6;
+ }
+
+ .swagger-ui .blue,
+ .swagger-ui .dark-blue,
+ .swagger-ui .hover-blue:focus,
+ .swagger-ui .hover-blue:hover,
+ .swagger-ui .hover-dark-blue:focus,
+ .swagger-ui .hover-dark-blue:hover {
+ color: #99bae6;
+ }
+
+ .swagger-ui .hover-light-blue:focus,
+ .swagger-ui .hover-light-blue:hover,
+ .swagger-ui .light-blue {
+ color: #a9cbea;
+ }
+
+ .swagger-ui .hover-lightest-blue:focus,
+ .swagger-ui .hover-lightest-blue:hover,
+ .swagger-ui .lightest-blue {
+ color: #d6e9f5;
+ }
+
+ .swagger-ui .hover-washed-blue:focus,
+ .swagger-ui .hover-washed-blue:hover,
+ .swagger-ui .washed-blue {
+ color: #f7fdfc;
+ }
+
+ .swagger-ui .hover-washed-green:focus,
+ .swagger-ui .hover-washed-green:hover,
+ .swagger-ui .washed-green {
+ color: #ebfaf4;
+ }
+
+ .swagger-ui .hover-washed-yellow:focus,
+ .swagger-ui .hover-washed-yellow:hover,
+ .swagger-ui .washed-yellow {
+ color: #fbf9ef;
+ }
+
+ .swagger-ui .hover-washed-red:focus,
+ .swagger-ui .hover-washed-red:hover,
+ .swagger-ui .washed-red {
+ color: #f9e7e7;
+ }
+
+ .swagger-ui .color-inherit,
+ .swagger-ui .hover-inherit:focus,
+ .swagger-ui .hover-inherit:hover {
+ color: inherit;
+ }
+
+ .swagger-ui .bg-black-90,
+ .swagger-ui .hover-bg-black-90:focus,
+ .swagger-ui .hover-bg-black-90:hover {
+ background-color: rgba(0, 0, 0, .9);
+ }
+
+ .swagger-ui .bg-black-80,
+ .swagger-ui .hover-bg-black-80:focus,
+ .swagger-ui .hover-bg-black-80:hover {
+ background-color: rgba(0, 0, 0, .8);
+ }
+
+ .swagger-ui .bg-black-70,
+ .swagger-ui .hover-bg-black-70:focus,
+ .swagger-ui .hover-bg-black-70:hover {
+ background-color: rgba(0, 0, 0, .7);
+ }
+
+ .swagger-ui .bg-black-60,
+ .swagger-ui .hover-bg-black-60:focus,
+ .swagger-ui .hover-bg-black-60:hover {
+ background-color: rgba(0, 0, 0, .6);
+ }
+
+ .swagger-ui .bg-black-50,
+ .swagger-ui .hover-bg-black-50:focus,
+ .swagger-ui .hover-bg-black-50:hover {
+ background-color: rgba(0, 0, 0, .5);
+ }
+
+ .swagger-ui .bg-black-40,
+ .swagger-ui .hover-bg-black-40:focus,
+ .swagger-ui .hover-bg-black-40:hover {
+ background-color: rgba(0, 0, 0, .4);
+ }
+
+ .swagger-ui .bg-black-30,
+ .swagger-ui .hover-bg-black-30:focus,
+ .swagger-ui .hover-bg-black-30:hover {
+ background-color: rgba(0, 0, 0, .3);
+ }
+
+ .swagger-ui .bg-black-20,
+ .swagger-ui .hover-bg-black-20:focus,
+ .swagger-ui .hover-bg-black-20:hover {
+ background-color: rgba(0, 0, 0, .2);
+ }
+
+ .swagger-ui .bg-white-90,
+ .swagger-ui .hover-bg-white-90:focus,
+ .swagger-ui .hover-bg-white-90:hover {
+ background-color: rgba(28, 28, 33, .9);
+ }
+
+ .swagger-ui .bg-white-80,
+ .swagger-ui .hover-bg-white-80:focus,
+ .swagger-ui .hover-bg-white-80:hover {
+ background-color: rgba(28, 28, 33, .8);
+ }
+
+ .swagger-ui .bg-white-70,
+ .swagger-ui .hover-bg-white-70:focus,
+ .swagger-ui .hover-bg-white-70:hover {
+ background-color: rgba(28, 28, 33, .7);
+ }
+
+ .swagger-ui .bg-white-60,
+ .swagger-ui .hover-bg-white-60:focus,
+ .swagger-ui .hover-bg-white-60:hover {
+ background-color: rgba(28, 28, 33, .6);
+ }
+
+ .swagger-ui .bg-white-50,
+ .swagger-ui .hover-bg-white-50:focus,
+ .swagger-ui .hover-bg-white-50:hover {
+ background-color: rgba(28, 28, 33, .5);
+ }
+
+ .swagger-ui .bg-white-40,
+ .swagger-ui .hover-bg-white-40:focus,
+ .swagger-ui .hover-bg-white-40:hover {
+ background-color: rgba(28, 28, 33, .4);
+ }
+
+ .swagger-ui .bg-white-30,
+ .swagger-ui .hover-bg-white-30:focus,
+ .swagger-ui .hover-bg-white-30:hover {
+ background-color: rgba(28, 28, 33, .3);
+ }
+
+ .swagger-ui .bg-white-20,
+ .swagger-ui .hover-bg-white-20:focus,
+ .swagger-ui .hover-bg-white-20:hover {
+ background-color: rgba(28, 28, 33, .2);
+ }
+
+ .swagger-ui .bg-black,
+ .swagger-ui .hover-bg-black:focus,
+ .swagger-ui .hover-bg-black:hover {
+ background-color: #000;
+ }
+
+ .swagger-ui .bg-near-black,
+ .swagger-ui .hover-bg-near-black:focus,
+ .swagger-ui .hover-bg-near-black:hover {
+ background-color: #121212;
+ }
+
+ .swagger-ui .bg-dark-gray,
+ .swagger-ui .hover-bg-dark-gray:focus,
+ .swagger-ui .hover-bg-dark-gray:hover {
+ background-color: #333;
+ }
+
+ .swagger-ui .bg-mid-gray,
+ .swagger-ui .hover-bg-mid-gray:focus,
+ .swagger-ui .hover-bg-mid-gray:hover {
+ background-color: #545454;
+ }
+
+ .swagger-ui .bg-gray,
+ .swagger-ui .hover-bg-gray:focus,
+ .swagger-ui .hover-bg-gray:hover {
+ background-color: #787878;
+ }
+
+ .swagger-ui .bg-silver,
+ .swagger-ui .hover-bg-silver:focus,
+ .swagger-ui .hover-bg-silver:hover {
+ background-color: #999;
+ }
+
+ .swagger-ui .bg-white,
+ .swagger-ui .hover-bg-white:focus,
+ .swagger-ui .hover-bg-white:hover {
+ background-color: #1c1c21;
+ }
+
+ .swagger-ui .bg-transparent,
+ .swagger-ui .hover-bg-transparent:focus,
+ .swagger-ui .hover-bg-transparent:hover {
+ background-color: transparent;
+ }
+
+ .swagger-ui .bg-dark-red,
+ .swagger-ui .hover-bg-dark-red:focus,
+ .swagger-ui .hover-bg-dark-red:hover {
+ background-color: #bc2f36;
+ }
+
+ .swagger-ui .bg-red,
+ .swagger-ui .hover-bg-red:focus,
+ .swagger-ui .hover-bg-red:hover {
+ background-color: #c83932;
+ }
+
+ .swagger-ui .bg-light-red,
+ .swagger-ui .hover-bg-light-red:focus,
+ .swagger-ui .hover-bg-light-red:hover {
+ background-color: #ab3c2b;
+ }
+
+ .swagger-ui .bg-orange,
+ .swagger-ui .hover-bg-orange:focus,
+ .swagger-ui .hover-bg-orange:hover {
+ background-color: #cc6e33;
+ }
+
+ .swagger-ui .bg-gold,
+ .swagger-ui .bg-light-yellow,
+ .swagger-ui .bg-washed-yellow,
+ .swagger-ui .bg-yellow,
+ .swagger-ui .hover-bg-gold:focus,
+ .swagger-ui .hover-bg-gold:hover,
+ .swagger-ui .hover-bg-light-yellow:focus,
+ .swagger-ui .hover-bg-light-yellow:hover,
+ .swagger-ui .hover-bg-washed-yellow:focus,
+ .swagger-ui .hover-bg-washed-yellow:hover,
+ .swagger-ui .hover-bg-yellow:focus,
+ .swagger-ui .hover-bg-yellow:hover {
+ background-color: #664b00;
+ }
+
+ .swagger-ui .bg-purple,
+ .swagger-ui .hover-bg-purple:focus,
+ .swagger-ui .hover-bg-purple:hover {
+ background-color: #5e2ca5;
+ }
+
+ .swagger-ui .bg-light-purple,
+ .swagger-ui .hover-bg-light-purple:focus,
+ .swagger-ui .hover-bg-light-purple:hover {
+ background-color: #672caf;
+ }
+
+ .swagger-ui .bg-dark-pink,
+ .swagger-ui .hover-bg-dark-pink:focus,
+ .swagger-ui .hover-bg-dark-pink:hover {
+ background-color: #ab2b81;
+ }
+
+ .swagger-ui .bg-hot-pink,
+ .swagger-ui .hover-bg-hot-pink:focus,
+ .swagger-ui .hover-bg-hot-pink:hover {
+ background-color: #c03086;
+ }
+
+ .swagger-ui .bg-pink,
+ .swagger-ui .hover-bg-pink:focus,
+ .swagger-ui .hover-bg-pink:hover {
+ background-color: #8f2464;
+ }
+
+ .swagger-ui .bg-light-pink,
+ .swagger-ui .hover-bg-light-pink:focus,
+ .swagger-ui .hover-bg-light-pink:hover {
+ background-color: #721d4d;
+ }
+
+ .swagger-ui .bg-dark-green,
+ .swagger-ui .hover-bg-dark-green:focus,
+ .swagger-ui .hover-bg-dark-green:hover {
+ background-color: #1c6e50;
+ }
+
+ .swagger-ui .bg-green,
+ .swagger-ui .hover-bg-green:focus,
+ .swagger-ui .hover-bg-green:hover {
+ background-color: #279b70;
+ }
+
+ .swagger-ui .bg-light-green,
+ .swagger-ui .hover-bg-light-green:focus,
+ .swagger-ui .hover-bg-light-green:hover {
+ background-color: #228762;
+ }
+
+ .swagger-ui .bg-navy,
+ .swagger-ui .hover-bg-navy:focus,
+ .swagger-ui .hover-bg-navy:hover {
+ background-color: #0d1d35;
+ }
+
+ .swagger-ui .bg-dark-blue,
+ .swagger-ui .hover-bg-dark-blue:focus,
+ .swagger-ui .hover-bg-dark-blue:hover {
+ background-color: #20497e;
+ }
+
+ .swagger-ui .bg-blue,
+ .swagger-ui .hover-bg-blue:focus,
+ .swagger-ui .hover-bg-blue:hover {
+ background-color: #4380d0;
+ }
+
+ .swagger-ui .bg-light-blue,
+ .swagger-ui .hover-bg-light-blue:focus,
+ .swagger-ui .hover-bg-light-blue:hover {
+ background-color: #20517e;
+ }
+
+ .swagger-ui .bg-lightest-blue,
+ .swagger-ui .hover-bg-lightest-blue:focus,
+ .swagger-ui .hover-bg-lightest-blue:hover {
+ background-color: #143a52;
+ }
+
+ .swagger-ui .bg-washed-blue,
+ .swagger-ui .hover-bg-washed-blue:focus,
+ .swagger-ui .hover-bg-washed-blue:hover {
+ background-color: #0c312d;
+ }
+
+ .swagger-ui .bg-washed-green,
+ .swagger-ui .hover-bg-washed-green:focus,
+ .swagger-ui .hover-bg-washed-green:hover {
+ background-color: #0f3d2c;
+ }
+
+ .swagger-ui .bg-washed-red,
+ .swagger-ui .hover-bg-washed-red:focus,
+ .swagger-ui .hover-bg-washed-red:hover {
+ background-color: #411010;
+ }
+
+ .swagger-ui .bg-inherit,
+ .swagger-ui .hover-bg-inherit:focus,
+ .swagger-ui .hover-bg-inherit:hover {
+ background-color: inherit;
+ }
+
+ .swagger-ui .shadow-hover {
+ transition: all .5s cubic-bezier(.165, .84, .44, 1) 0s;
+ }
+
+ .swagger-ui .shadow-hover::after {
+ border-radius: inherit;
+ box-shadow: rgba(0, 0, 0, .2) 0 0 16px 2px;
+ content: "";
+ height: 100%;
+ left: 0;
+ opacity: 0;
+ position: absolute;
+ top: 0;
+ transition: opacity .5s cubic-bezier(.165, .84, .44, 1) 0s;
+ width: 100%;
+ z-index: -1;
+ }
+
+ .swagger-ui .bg-animate,
+ .swagger-ui .bg-animate:focus,
+ .swagger-ui .bg-animate:hover {
+ transition: background-color .15s ease-in-out 0s;
+ }
+
+ .swagger-ui .nested-links a {
+ color: #99bae6;
+ transition: color .15s ease-in 0s;
+ }
+
+ .swagger-ui .nested-links a:focus,
+ .swagger-ui .nested-links a:hover {
+ color: #a9cbea;
+ transition: color .15s ease-in 0s;
+ }
+
+ .swagger-ui .opblock-tag {
+ border-bottom: 1px solid rgba(58, 64, 80, .3);
+ color: #b5bac9;
+ transition: all .2s ease 0s;
+ }
+
+ .swagger-ui .opblock-tag svg,
+ .swagger-ui section.models h4 svg {
+ transition: all .4s ease 0s;
+ }
+
+ .swagger-ui .opblock {
+ border: 1px solid #000;
+ border-radius: 4px;
+ box-shadow: rgba(0, 0, 0, .19) 0 0 3px;
+ margin: 0 0 15px;
+ }
+
+ .swagger-ui .opblock .tab-header .tab-item.active h4 span::after {
+ background: gray;
+ }
+
+ .swagger-ui .opblock.is-open .opblock-summary {
+ border-bottom: 1px solid #000;
+ }
+
+ .swagger-ui .opblock .opblock-section-header {
+ background: rgba(28, 28, 33, .8);
+ box-shadow: rgba(0, 0, 0, .1) 0 1px 2px;
+ }
+
+ .swagger-ui .opblock .opblock-section-header>label>span {
+ padding: 0 10px 0 0;
+ }
+
+ .swagger-ui .opblock .opblock-summary-method {
+ background: #000;
+ color: #fff;
+ text-shadow: rgba(0, 0, 0, .1) 0 1px 0;
+ }
+
+ .swagger-ui .opblock.opblock-post {
+ background: rgba(72, 203, 144, .1);
+ border-color: #48cb90;
+ }
+
+ .swagger-ui .opblock.opblock-post .opblock-summary-method,
+ .swagger-ui .opblock.opblock-post .tab-header .tab-item.active h4 span::after {
+ background: #48cb90;
+ }
+
+ .swagger-ui .opblock.opblock-post .opblock-summary {
+ border-color: #48cb90;
+ }
+
+ .swagger-ui .opblock.opblock-put {
+ background: rgba(213, 157, 88, .1);
+ border-color: #d59d58;
+ }
+
+ .swagger-ui .opblock.opblock-put .opblock-summary-method,
+ .swagger-ui .opblock.opblock-put .tab-header .tab-item.active h4 span::after {
+ background: #d59d58;
+ }
+
+ .swagger-ui .opblock.opblock-put .opblock-summary {
+ border-color: #d59d58;
+ }
+
+ .swagger-ui .opblock.opblock-delete {
+ background: rgba(200, 50, 50, .1);
+ border-color: #c83232;
+ }
+
+ .swagger-ui .opblock.opblock-delete .opblock-summary-method,
+ .swagger-ui .opblock.opblock-delete .tab-header .tab-item.active h4 span::after {
+ background: #c83232;
+ }
+
+ .swagger-ui .opblock.opblock-delete .opblock-summary {
+ border-color: #c83232;
+ }
+
+ .swagger-ui .opblock.opblock-get {
+ background: rgba(42, 105, 167, .1);
+ border-color: #2a69a7;
+ }
+
+ .swagger-ui .opblock.opblock-get .opblock-summary-method,
+ .swagger-ui .opblock.opblock-get .tab-header .tab-item.active h4 span::after {
+ background: #2a69a7;
+ }
+
+ .swagger-ui .opblock.opblock-get .opblock-summary {
+ border-color: #2a69a7;
+ }
+
+ .swagger-ui .opblock.opblock-patch {
+ background: rgba(92, 214, 188, .1);
+ border-color: #5cd6bc;
+ }
+
+ .swagger-ui .opblock.opblock-patch .opblock-summary-method,
+ .swagger-ui .opblock.opblock-patch .tab-header .tab-item.active h4 span::after {
+ background: #5cd6bc;
+ }
+
+ .swagger-ui .opblock.opblock-patch .opblock-summary {
+ border-color: #5cd6bc;
+ }
+
+ .swagger-ui .opblock.opblock-head {
+ background: rgba(140, 63, 207, .1);
+ border-color: #8c3fcf;
+ }
+
+ .swagger-ui .opblock.opblock-head .opblock-summary-method,
+ .swagger-ui .opblock.opblock-head .tab-header .tab-item.active h4 span::after {
+ background: #8c3fcf;
+ }
+
+ .swagger-ui .opblock.opblock-head .opblock-summary {
+ border-color: #8c3fcf;
+ }
+
+ .swagger-ui .opblock.opblock-options {
+ background: rgba(36, 89, 143, .1);
+ border-color: #24598f;
+ }
+
+ .swagger-ui .opblock.opblock-options .opblock-summary-method,
+ .swagger-ui .opblock.opblock-options .tab-header .tab-item.active h4 span::after {
+ background: #24598f;
+ }
+
+ .swagger-ui .opblock.opblock-options .opblock-summary {
+ border-color: #24598f;
+ }
+
+ .swagger-ui .opblock.opblock-deprecated {
+ background: rgba(46, 46, 46, .1);
+ border-color: #2e2e2e;
+ opacity: .6;
+ }
+
+ .swagger-ui .opblock.opblock-deprecated .opblock-summary-method,
+ .swagger-ui .opblock.opblock-deprecated .tab-header .tab-item.active h4 span::after {
+ background: #2e2e2e;
+ }
+
+ .swagger-ui .opblock.opblock-deprecated .opblock-summary {
+ border-color: #2e2e2e;
+ }
+
+ .swagger-ui .filter .operation-filter-input {
+ border: 2px solid #2b3446;
+ }
+
+ .swagger-ui .tab li:first-of-type::after {
+ background: rgba(0, 0, 0, .2);
+ }
+
+ .swagger-ui .download-contents {
+ background: #7c8192;
+ color: #fff;
+ }
+
+ .swagger-ui .scheme-container {
+ background: #1c1c21;
+ box-shadow: rgba(0, 0, 0, .15) 0 1px 2px 0;
+ }
+
+ .swagger-ui .loading-container .loading::before {
+ animation: 1s linear 0s infinite normal none running rotation, .5s ease 0s 1 normal none running opacity;
+ border-color: rgba(0, 0, 0, .6) rgba(84, 84, 84, .1) rgba(84, 84, 84, .1);
+ }
+
+ .swagger-ui .response-control-media-type--accept-controller select {
+ border-color: #196619;
+ }
+
+ .swagger-ui .response-control-media-type__accept-message {
+ color: #99e699;
+ }
+
+ .swagger-ui .version-pragma__message code {
+ background-color: #3b3b3b;
+ }
+
+ .swagger-ui .btn {
+ background: 0 0;
+ border: 2px solid gray;
+ box-shadow: rgba(0, 0, 0, .1) 0 1px 2px;
+ color: #b5bac9;
+ }
+
+ .swagger-ui .btn:hover {
+ box-shadow: rgba(0, 0, 0, .3) 0 0 5px;
+ }
+
+ .swagger-ui .btn.authorize,
+ .swagger-ui .btn.cancel {
+ background-color: transparent;
+ border-color: #a72a2a;
+ color: #e69999;
+ }
+
+ .swagger-ui .btn.cancel:hover {
+ background-color: #a72a2a;
+ color: #fff;
+ }
+
+ .swagger-ui .btn.authorize {
+ border-color: #48cb90;
+ color: #9ce3c3;
+ }
+
+ .swagger-ui .btn.authorize svg {
+ fill: #9ce3c3;
+ }
+
+ .btn.authorize.unlocked:hover {
+ background-color: #48cb90;
+ color: #fff;
+ }
+
+ .btn.authorize.unlocked:hover svg {
+ fill: #fbfbfb;
+ }
+
+ .swagger-ui .btn.execute {
+ background-color: #5892d5;
+ border-color: #5892d5;
+ color: #fff;
+ }
+
+ .swagger-ui .copy-to-clipboard {
+ background: #7c8192;
+ }
+
+ .swagger-ui .copy-to-clipboard button {
+ background: url("data:image/svg+xml;charset=utf-8,") 50% center no-repeat;
+ }
+
+ .swagger-ui select {
+ background: url("data:image/svg+xml;charset=utf-8,") right 10px center/20px no-repeat #212121;
+ background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMS4wICg0MDM1YTRmYjQ5LCAyMDIwLTA1LTAxKSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iZG93bmxvYWQuc3ZnIgogICBpZD0ic3ZnNCIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMjAgMjAiPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTEwIj4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZGVmcwogICAgIGlkPSJkZWZzOCIgLz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ic3ZnNCIKICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIgogICAgIGlua3NjYXBlOndpbmRvdy15PSItOSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTkiCiAgICAgaW5rc2NhcGU6Y3k9IjEwIgogICAgIGlua3NjYXBlOmN4PSIxMCIKICAgICBpbmtzY2FwZTp6b29tPSI0MS41IgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpZD0ibmFtZWR2aWV3NiIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMDAxIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTkyMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIgogICAgIGd1aWRldG9sZXJhbmNlPSIxMCIKICAgICBncmlkdG9sZXJhbmNlPSIxMCIKICAgICBvYmplY3R0b2xlcmFuY2U9IjEwIgogICAgIGJvcmRlcm9wYWNpdHk9IjEiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIiAvPgogIDxwYXRoCiAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZiIKICAgICBpZD0icGF0aDIiCiAgICAgZD0iTTEzLjQxOCA3Ljg1OWEuNjk1LjY5NSAwIDAxLjk3OCAwIC42OC42OCAwIDAxMCAuOTY5bC0zLjkwOCAzLjgzYS42OTcuNjk3IDAgMDEtLjk3OSAwbC0zLjkwOC0zLjgzYS42OC42OCAwIDAxMC0uOTY5LjY5NS42OTUgMCAwMS45NzggMEwxMCAxMWwzLjQxOC0zLjE0MXoiIC8+Cjwvc3ZnPgo=) right 10px center/20px no-repeat #1c1c21;
+ border: 2px solid #41444e;
+ }
+
+ .swagger-ui select[multiple] {
+ background: #212121;
+ }
+
+ .swagger-ui button.invalid,
+ .swagger-ui input[type=email].invalid,
+ .swagger-ui input[type=file].invalid,
+ .swagger-ui input[type=password].invalid,
+ .swagger-ui input[type=search].invalid,
+ .swagger-ui input[type=text].invalid,
+ .swagger-ui select.invalid,
+ .swagger-ui textarea.invalid {
+ background: #390e0e;
+ border-color: #c83232;
+ }
+
+ .swagger-ui input[type=email],
+ .swagger-ui input[type=file],
+ .swagger-ui input[type=password],
+ .swagger-ui input[type=search],
+ .swagger-ui input[type=text],
+ .swagger-ui textarea {
+ background: #1c1c21;
+ border: 1px solid #404040;
+ }
+
+ .swagger-ui textarea {
+ background: rgba(28, 28, 33, .8);
+ color: #b5bac9;
+ }
+
+ .swagger-ui input[disabled],
+ .swagger-ui select[disabled] {
+ background-color: #1f1f1f;
+ color: #bfbfbf;
+ }
+
+ .swagger-ui textarea[disabled] {
+ background-color: #41444e;
+ color: #fff;
+ }
+
+ .swagger-ui select[disabled] {
+ border-color: #878787;
+ }
+
+ .swagger-ui textarea:focus {
+ border: 2px solid #2a69a7;
+ }
+
+ .swagger-ui .checkbox input[type=checkbox]+label>.item {
+ background: #303030;
+ box-shadow: #303030 0 0 0 2px;
+ }
+
+ .swagger-ui .checkbox input[type=checkbox]:checked+label>.item {
+ background: url("data:image/svg+xml;charset=utf-8,") 50% center no-repeat #303030;
+ }
+
+ .swagger-ui .dialog-ux .backdrop-ux {
+ background: rgba(0, 0, 0, .8);
+ }
+
+ .swagger-ui .dialog-ux .modal-ux {
+ background: #1c1c21;
+ border: 1px solid #2e2e2e;
+ box-shadow: rgba(0, 0, 0, .2) 0 10px 30px 0;
+ }
+
+ .swagger-ui .dialog-ux .modal-ux-header .close-modal {
+ background: 0 0;
+ }
+
+ .swagger-ui .model .deprecated span,
+ .swagger-ui .model .deprecated td {
+ color: #bfbfbf !important;
+ }
+
+ .swagger-ui .model-toggle::after {
+ background: url("data:image/svg+xml;charset=utf-8,") 50% center/100% no-repeat;
+ }
+
+ .swagger-ui .model-hint {
+ background: rgba(0, 0, 0, .7);
+ color: #ebebeb;
+ }
+
+ .swagger-ui section.models {
+ border: 1px solid rgba(58, 64, 80, .3);
+ }
+
+ .swagger-ui section.models.is-open h4 {
+ border-bottom: 1px solid rgba(58, 64, 80, .3);
+ }
+
+ .swagger-ui section.models .model-container {
+ background: rgba(0, 0, 0, .05);
+ }
+
+ .swagger-ui section.models .model-container:hover {
+ background: rgba(0, 0, 0, .07);
+ }
+
+ .swagger-ui .model-box {
+ background: rgba(0, 0, 0, .1);
+ }
+
+ .swagger-ui .prop-type {
+ color: #aaaad4;
+ }
+
+ .swagger-ui table thead tr td,
+ .swagger-ui table thead tr th {
+ border-bottom: 1px solid rgba(58, 64, 80, .2);
+ color: #b5bac9;
+ }
+
+ .swagger-ui .parameter__name.required::after {
+ color: rgba(230, 153, 153, .6);
+ }
+
+ .swagger-ui .topbar .download-url-wrapper .select-label {
+ color: #f0f0f0;
+ }
+
+ .swagger-ui .topbar .download-url-wrapper .download-url-button {
+ background: #63a040;
+ color: #fff;
+ }
+
+ .swagger-ui .info .title small {
+ background: #7c8492;
+ }
+
+ .swagger-ui .info .title small.version-stamp {
+ background-color: #7a9b27;
+ }
+
+ .swagger-ui .auth-container .errors {
+ background-color: #350d0d;
+ color: #b5bac9;
+ }
+
+ .swagger-ui .errors-wrapper {
+ background: rgba(200, 50, 50, .1);
+ border: 2px solid #c83232;
+ }
+
+ .swagger-ui .markdown code,
+ .swagger-ui .renderedmarkdown code {
+ background: rgba(0, 0, 0, .05);
+ color: #c299e6;
+ }
+
+ .swagger-ui .model-toggle:after {
+ background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMS4wICg0MDM1YTRmYjQ5LCAyMDIwLTA1LTAxKSIKICAgc29kaXBvZGk6ZG9jbmFtZT0iZG93bmxvYWQyLnN2ZyIKICAgaWQ9InN2ZzQiCiAgIHZlcnNpb249IjEuMSIKICAgaGVpZ2h0PSIyNCIKICAgd2lkdGg9IjI0Ij4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGExMCI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGRlZnMKICAgICBpZD0iZGVmczgiIC8+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9InN2ZzQiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTkiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9Ii05IgogICAgIGlua3NjYXBlOmN5PSIxMiIKICAgICBpbmtzY2FwZTpjeD0iMTIiCiAgICAgaW5rc2NhcGU6em9vbT0iMzQuNTgzMzMzIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpZD0ibmFtZWR2aWV3NiIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMDAxIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTkyMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIgogICAgIGd1aWRldG9sZXJhbmNlPSIxMCIKICAgICBncmlkdG9sZXJhbmNlPSIxMCIKICAgICBvYmplY3R0b2xlcmFuY2U9IjEwIgogICAgIGJvcmRlcm9wYWNpdHk9IjEiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIiAvPgogIDxwYXRoCiAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZiIKICAgICBpZD0icGF0aDIiCiAgICAgZD0iTTEwIDZMOC41OSA3LjQxIDEzLjE3IDEybC00LjU4IDQuNTlMMTAgMThsNi02eiIgLz4KPC9zdmc+Cg==) 50% no-repeat;
+ }
+
+ /* arrows for each operation and request are now white */
+ .arrow,
+ #large-arrow-up {
+ fill: #fff;
+ }
+
+ #unlocked {
+ fill: #fff;
+ }
+
+ ::-webkit-scrollbar-track {
+ background-color: #646464 !important;
+ }
+
+ ::-webkit-scrollbar-thumb {
+ background-color: #242424 !important;
+ border: 2px solid #3e4346 !important;
+ }
+
+ ::-webkit-scrollbar-button:vertical:start:decrement {
+ background: linear-gradient(130deg, #696969 40%, rgba(255, 0, 0, 0) 41%), linear-gradient(230deg, #696969 40%, transparent 41%), linear-gradient(0deg, #696969 40%, transparent 31%);
+ background-color: #b6b6b6;
+ }
+
+ ::-webkit-scrollbar-button:vertical:end:increment {
+ background: linear-gradient(310deg, #696969 40%, transparent 41%), linear-gradient(50deg, #696969 40%, transparent 41%), linear-gradient(180deg, #696969 40%, transparent 31%);
+ background-color: #b6b6b6;
+ }
+
+ ::-webkit-scrollbar-button:horizontal:end:increment {
+ background: linear-gradient(210deg, #696969 40%, transparent 41%), linear-gradient(330deg, #696969 40%, transparent 41%), linear-gradient(90deg, #696969 30%, transparent 31%);
+ background-color: #b6b6b6;
+ }
+
+ ::-webkit-scrollbar-button:horizontal:start:decrement {
+ background: linear-gradient(30deg, #696969 40%, transparent 41%), linear-gradient(150deg, #696969 40%, transparent 41%), linear-gradient(270deg, #696969 30%, transparent 31%);
+ background-color: #b6b6b6;
+ }
+
+ ::-webkit-scrollbar-button,
+ ::-webkit-scrollbar-track-piece {
+ background-color: #3e4346 !important;
+ }
+
+ .swagger-ui .black,
+ .swagger-ui .checkbox,
+ .swagger-ui .dark-gray,
+ .swagger-ui .download-url-wrapper .loading,
+ .swagger-ui .errors-wrapper .errors small,
+ .swagger-ui .fallback,
+ .swagger-ui .filter .loading,
+ .swagger-ui .gray,
+ .swagger-ui .hover-black:focus,
+ .swagger-ui .hover-black:hover,
+ .swagger-ui .hover-dark-gray:focus,
+ .swagger-ui .hover-dark-gray:hover,
+ .swagger-ui .hover-gray:focus,
+ .swagger-ui .hover-gray:hover,
+ .swagger-ui .hover-light-silver:focus,
+ .swagger-ui .hover-light-silver:hover,
+ .swagger-ui .hover-mid-gray:focus,
+ .swagger-ui .hover-mid-gray:hover,
+ .swagger-ui .hover-near-black:focus,
+ .swagger-ui .hover-near-black:hover,
+ .swagger-ui .hover-silver:focus,
+ .swagger-ui .hover-silver:hover,
+ .swagger-ui .light-silver,
+ .swagger-ui .markdown pre,
+ .swagger-ui .mid-gray,
+ .swagger-ui .model .property,
+ .swagger-ui .model .property.primitive,
+ .swagger-ui .model-title,
+ .swagger-ui .near-black,
+ .swagger-ui .parameter__extension,
+ .swagger-ui .parameter__in,
+ .swagger-ui .prop-format,
+ .swagger-ui .renderedmarkdown pre,
+ .swagger-ui .response-col_links .response-undocumented,
+ .swagger-ui .response-col_status .response-undocumented,
+ .swagger-ui .silver,
+ .swagger-ui section.models h4,
+ .swagger-ui section.models h5,
+ .swagger-ui span.token-not-formatted,
+ .swagger-ui span.token-string,
+ .swagger-ui table.headers .header-example,
+ .swagger-ui table.model tr.description,
+ .swagger-ui table.model tr.extension {
+ color: #bfbfbf;
+ }
+
+ .swagger-ui .hover-white:focus,
+ .swagger-ui .hover-white:hover,
+ .swagger-ui .info .title small pre,
+ .swagger-ui .topbar a,
+ .swagger-ui .white {
+ color: #fff;
+ }
+
+ .swagger-ui .bg-black-10,
+ .swagger-ui .hover-bg-black-10:focus,
+ .swagger-ui .hover-bg-black-10:hover,
+ .swagger-ui .stripe-dark:nth-child(2n + 1) {
+ background-color: rgba(0, 0, 0, .1);
+ }
+
+ .swagger-ui .bg-white-10,
+ .swagger-ui .hover-bg-white-10:focus,
+ .swagger-ui .hover-bg-white-10:hover,
+ .swagger-ui .stripe-light:nth-child(2n + 1) {
+ background-color: rgba(28, 28, 33, .1);
+ }
+
+ .swagger-ui .bg-light-silver,
+ .swagger-ui .hover-bg-light-silver:focus,
+ .swagger-ui .hover-bg-light-silver:hover,
+ .swagger-ui .striped--light-silver:nth-child(2n + 1) {
+ background-color: #6e6e6e;
+ }
+
+ .swagger-ui .bg-moon-gray,
+ .swagger-ui .hover-bg-moon-gray:focus,
+ .swagger-ui .hover-bg-moon-gray:hover,
+ .swagger-ui .striped--moon-gray:nth-child(2n + 1) {
+ background-color: #4d4d4d;
+ }
+
+ .swagger-ui .bg-light-gray,
+ .swagger-ui .hover-bg-light-gray:focus,
+ .swagger-ui .hover-bg-light-gray:hover,
+ .swagger-ui .striped--light-gray:nth-child(2n + 1) {
+ background-color: #2b2b2b;
+ }
+
+ .swagger-ui .bg-near-white,
+ .swagger-ui .hover-bg-near-white:focus,
+ .swagger-ui .hover-bg-near-white:hover,
+ .swagger-ui .striped--near-white:nth-child(2n + 1) {
+ background-color: #242424;
+ }
+
+ .swagger-ui .opblock-tag:hover,
+ .swagger-ui section.models h4:hover {
+ background: rgba(0, 0, 0, .02);
+ }
+
+ .swagger-ui .checkbox p,
+ .swagger-ui .dialog-ux .modal-ux-content h4,
+ .swagger-ui .dialog-ux .modal-ux-content p,
+ .swagger-ui .dialog-ux .modal-ux-header h3,
+ .swagger-ui .errors-wrapper .errors h4,
+ .swagger-ui .errors-wrapper hgroup h4,
+ .swagger-ui .info .base-url,
+ .swagger-ui .info .title,
+ .swagger-ui .info h1,
+ .swagger-ui .info h2,
+ .swagger-ui .info h3,
+ .swagger-ui .info h4,
+ .swagger-ui .info h5,
+ .swagger-ui .info li,
+ .swagger-ui .info p,
+ .swagger-ui .info table,
+ .swagger-ui .loading-container .loading::after,
+ .swagger-ui .model,
+ .swagger-ui .opblock .opblock-section-header h4,
+ .swagger-ui .opblock .opblock-section-header>label,
+ .swagger-ui .opblock .opblock-summary-description,
+ .swagger-ui .opblock .opblock-summary-operation-id,
+ .swagger-ui .opblock .opblock-summary-path,
+ .swagger-ui .opblock .opblock-summary-path__deprecated,
+ .swagger-ui .opblock-description-wrapper,
+ .swagger-ui .opblock-description-wrapper h4,
+ .swagger-ui .opblock-description-wrapper p,
+ .swagger-ui .opblock-external-docs-wrapper,
+ .swagger-ui .opblock-external-docs-wrapper h4,
+ .swagger-ui .opblock-external-docs-wrapper p,
+ .swagger-ui .opblock-tag small,
+ .swagger-ui .opblock-title_normal,
+ .swagger-ui .opblock-title_normal h4,
+ .swagger-ui .opblock-title_normal p,
+ .swagger-ui .parameter__name,
+ .swagger-ui .parameter__type,
+ .swagger-ui .response-col_links,
+ .swagger-ui .response-col_status,
+ .swagger-ui .responses-inner h4,
+ .swagger-ui .responses-inner h5,
+ .swagger-ui .scheme-container .schemes>label,
+ .swagger-ui .scopes h2,
+ .swagger-ui .servers>label,
+ .swagger-ui .tab li,
+ .swagger-ui label,
+ .swagger-ui select,
+ .swagger-ui table.headers td {
+ color: #b5bac9;
+ }
+
+ .swagger-ui .download-url-wrapper .failed,
+ .swagger-ui .filter .failed,
+ .swagger-ui .model-deprecated-warning,
+ .swagger-ui .parameter__deprecated,
+ .swagger-ui .parameter__name.required span,
+ .swagger-ui table.model tr.property-row .star {
+ color: #e69999;
+ }
+
+ .swagger-ui .opblock-body pre.microlight,
+ .swagger-ui textarea.curl {
+ background: #41444e;
+ border-radius: 4px;
+ color: #fff;
+ }
+
+ .swagger-ui .expand-methods svg,
+ .swagger-ui .expand-methods:hover svg {
+ fill: #bfbfbf;
+ }
+
+ .swagger-ui .auth-container,
+ .swagger-ui .dialog-ux .modal-ux-header {
+ border-bottom: 1px solid #2e2e2e;
+ }
+
+ .swagger-ui .topbar .download-url-wrapper .select-label select,
+ .swagger-ui .topbar .download-url-wrapper input[type=text] {
+ border: 2px solid #63a040;
+ }
+
+ .swagger-ui .info a,
+ .swagger-ui .info a:hover,
+ .swagger-ui .scopes h2 a {
+ color: #99bde6;
+ }
+
+ /* Dark Scrollbar */
+ ::-webkit-scrollbar {
+ width: 14px;
+ height: 14px;
+ }
+
+ ::-webkit-scrollbar-button {
+ background-color: #3e4346 !important;
+ }
+
+ ::-webkit-scrollbar-track {
+ background-color: #646464 !important;
+ }
+
+ ::-webkit-scrollbar-track-piece {
+ background-color: #3e4346 !important;
+ }
+
+ ::-webkit-scrollbar-thumb {
+ height: 50px;
+ background-color: #242424 !important;
+ border: 2px solid #3e4346 !important;
+ }
+
+ ::-webkit-scrollbar-corner {}
+
+ ::-webkit-resizer {}
+
+ ::-webkit-scrollbar-button:vertical:start:decrement {
+ background:
+ linear-gradient(130deg, #696969 40%, rgba(255, 0, 0, 0) 41%),
+ linear-gradient(230deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
+ linear-gradient(0deg, #696969 40%, rgba(0, 0, 0, 0) 31%);
+ background-color: #b6b6b6;
+ }
+
+ ::-webkit-scrollbar-button:vertical:end:increment {
+ background:
+ linear-gradient(310deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
+ linear-gradient(50deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
+ linear-gradient(180deg, #696969 40%, rgba(0, 0, 0, 0) 31%);
+ background-color: #b6b6b6;
+ }
+
+ ::-webkit-scrollbar-button:horizontal:end:increment {
+ background:
+ linear-gradient(210deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
+ linear-gradient(330deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
+ linear-gradient(90deg, #696969 30%, rgba(0, 0, 0, 0) 31%);
+ background-color: #b6b6b6;
+ }
+
+ ::-webkit-scrollbar-button:horizontal:start:decrement {
+ background:
+ linear-gradient(30deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
+ linear-gradient(150deg, #696969 40%, rgba(0, 0, 0, 0) 41%),
+ linear-gradient(270deg, #696969 30%, rgba(0, 0, 0, 0) 31%);
+ background-color: #b6b6b6;
+ }
+}
\ No newline at end of file
diff --git a/server/swagger/index.css b/server/swagger/index.css
new file mode 100644
index 00000000..1d346cbc
--- /dev/null
+++ b/server/swagger/index.css
@@ -0,0 +1,3 @@
+.schemes.wrapper>div:first-of-type {
+ display: none;
+}
\ No newline at end of file
diff --git a/server/swagger/index.js b/server/swagger/index.js
new file mode 100644
index 00000000..3f2529f8
--- /dev/null
+++ b/server/swagger/index.js
@@ -0,0 +1,28 @@
+function waitForElm(selector) {
+ return new Promise(resolve => {
+ if (document.querySelector(selector)) {
+ return resolve(document.querySelector(selector));
+ }
+
+ const observer = new MutationObserver(mutations => {
+ if (document.querySelector(selector)) {
+ resolve(document.querySelector(selector));
+ observer.disconnect();
+ }
+ });
+
+ observer.observe(document.body, {
+ childList: true,
+ subtree: true
+ });
+ });
+}
+
+// Force change the Swagger logo in the header
+waitForElm('img[alt="Swagger UI"]').then((elm) => {
+ if (window.SWAGGER_DOCS_ENV === 'development') {
+ elm.src = 'http://localhost:3000/public/anything-llm-light.png'
+ } else {
+ elm.src = `${window.location.origin}/anything-llm-light.png`
+ }
+});
\ No newline at end of file
diff --git a/server/swagger/init.js b/server/swagger/init.js
new file mode 100644
index 00000000..c84daf32
--- /dev/null
+++ b/server/swagger/init.js
@@ -0,0 +1,37 @@
+const swaggerAutogen = require('swagger-autogen')({ openapi: '3.0.0' });
+
+const doc = {
+ info: {
+ version: '1.0.0',
+ title: 'AnythingLLM Developer API',
+ description: 'API endpoints that enable programmatic reading, writing, and updating of your AnythingLLM instance. UI supplied by Swagger.io.',
+ },
+ host: '/api',
+ schemes: ['http'],
+ securityDefinitions: {
+ BearerAuth: {
+ type: 'http',
+ scheme: 'bearer',
+ bearerFormat: 'JWT'
+ }
+ },
+ security: [
+ { BearerAuth: [] }
+ ],
+ definitions: {
+ InvalidAPIKey: {
+ message: 'Invalid API Key',
+ },
+ }
+};
+
+const outputFile = './openapi.json';
+const endpointsFiles = [
+ '../endpoints/api/auth/index.js',
+ '../endpoints/api/admin/index.js',
+ '../endpoints/api/document/index.js',
+ '../endpoints/api/workspace/index.js',
+ '../endpoints/api/system/index.js',
+];
+
+swaggerAutogen(outputFile, endpointsFiles, doc)
\ No newline at end of file
diff --git a/server/swagger/openapi.json b/server/swagger/openapi.json
new file mode 100644
index 00000000..6ff36279
--- /dev/null
+++ b/server/swagger/openapi.json
@@ -0,0 +1,1767 @@
+{
+ "openapi": "3.0.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "AnythingLLM Developer API",
+ "description": "API endpoints that enable programmatic reading, writing, and updating of your AnythingLLM instance. UI supplied by Swagger.io."
+ },
+ "servers": [
+ {
+ "url": "http:///api/"
+ }
+ ],
+ "paths": {
+ "/v1/auth": {
+ "get": {
+ "tags": [
+ "Authentication"
+ ],
+ "description": "Verify the attached Authentication header contains a valid API token.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Valid auth token was found.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "authenticated": true
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/admin/is-multi-user-mode": {
+ "get": {
+ "tags": [
+ "Admin"
+ ],
+ "description": "Check to see if the instance is in multi-user-mode first. Methods are disabled until multi user mode is enabled via the UI.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "isMultiUser": true
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/admin/users": {
+ "get": {
+ "tags": [
+ "Admin"
+ ],
+ "description": "Check to see if the instance is in multi-user-mode first. Methods are disabled until multi user mode is enabled via the UI.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "users": [
+ {
+ "username": "sample-sam",
+ "role": "default"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Instance is not in Multi-User mode. Method denied"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ }
+ },
+ "/v1/admin/users/new": {
+ "post": {
+ "tags": [
+ "Admin"
+ ],
+ "description": "Create a new user with username and password. Methods are disabled until multi user mode is enabled via the UI.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "user": {
+ "id": 1,
+ "username": "sample-sam",
+ "role": "default"
+ },
+ "error": null
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Instance is not in Multi-User mode. Method denied"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ },
+ "requestBody": {
+ "description": "Key pair object that will define the new user to add to the system.",
+ "required": true,
+ "type": "object",
+ "content": {
+ "application/json": {
+ "example": {
+ "username": "sample-sam",
+ "password": "hunter2",
+ "role": "default | admin"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/admin/users/{id}": {
+ "post": {
+ "tags": [
+ "Admin"
+ ],
+ "description": "Update existing user settings. Methods are disabled until multi user mode is enabled via the UI.",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "id of the user in the database."
+ },
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "success": true,
+ "error": null
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Instance is not in Multi-User mode. Method denied"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ },
+ "requestBody": {
+ "description": "Key pair object that will update the found user. All fields are optional and will not update unless specified.",
+ "required": true,
+ "type": "object",
+ "content": {
+ "application/json": {
+ "example": {
+ "username": "sample-sam",
+ "password": "hunter2",
+ "role": "default | admin",
+ "suspended": 0
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "Admin"
+ ],
+ "description": "Delete existing user by id. Methods are disabled until multi user mode is enabled via the UI.",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "id of the user in the database."
+ },
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "success": true,
+ "error": null
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Instance is not in Multi-User mode. Method denied"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ }
+ },
+ "/v1/admin/invites": {
+ "get": {
+ "tags": [
+ "Admin"
+ ],
+ "description": "List all existing invitations to instance regardless of status. Methods are disabled until multi user mode is enabled via the UI.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "invites": [
+ {
+ "id": 1,
+ "status": "pending",
+ "code": "abc-123",
+ "claimedBy": null
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Instance is not in Multi-User mode. Method denied"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ }
+ },
+ "/v1/admin/invite/new": {
+ "post": {
+ "tags": [
+ "Admin"
+ ],
+ "description": "Create a new invite code for someone to use to register with instance. Methods are disabled until multi user mode is enabled via the UI.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "invite": {
+ "id": 1,
+ "status": "pending",
+ "code": "abc-123"
+ },
+ "error": null
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Instance is not in Multi-User mode. Method denied"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ }
+ },
+ "/v1/admin/invite/{id}": {
+ "delete": {
+ "tags": [
+ "Admin"
+ ],
+ "description": "Deactivates (soft-delete) invite by id. Methods are disabled until multi user mode is enabled via the UI.",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "id of the invite in the database."
+ },
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "success": true,
+ "error": null
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Instance is not in Multi-User mode. Method denied"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ }
+ },
+ "/v1/admin/workspaces/{workspaceId}/update-users": {
+ "post": {
+ "tags": [
+ "Admin"
+ ],
+ "description": "Overwrite workspace permissions to only be accessible by the given user ids and admins. Methods are disabled until multi user mode is enabled via the UI.",
+ "parameters": [
+ {
+ "name": "workspaceId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "id of the workspace in the database."
+ },
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "success": true,
+ "error": null
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Instance is not in Multi-User mode. Method denied"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ },
+ "requestBody": {
+ "description": "Entire array of user ids who can access the workspace. All fields are optional and will not update unless specified.",
+ "required": true,
+ "type": "object",
+ "content": {
+ "application/json": {
+ "example": {
+ "userIds": [
+ 1,
+ 2,
+ 4,
+ 12
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/admin/workspace-chats": {
+ "post": {
+ "tags": [
+ "Admin"
+ ],
+ "description": "All chats in the system ordered by most recent. Methods are disabled until multi user mode is enabled via the UI.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "success": true,
+ "error": null
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Instance is not in Multi-User mode. Method denied"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ },
+ "requestBody": {
+ "description": "Page offset to show of workspace chats. All fields are optional and will not update unless specified.",
+ "required": false,
+ "type": "integer",
+ "content": {
+ "application/json": {
+ "example": {
+ "offset": 2
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/admin/preferences": {
+ "get": {
+ "tags": [
+ "Admin"
+ ],
+ "description": "Show all multi-user preferences for instance. Methods are disabled until multi user mode is enabled via the UI.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "settings": {
+ "users_can_delete_workspaces": true,
+ "limit_user_messages": false,
+ "message_limit": 10
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Instance is not in Multi-User mode. Method denied"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ },
+ "post": {
+ "tags": [
+ "Admin"
+ ],
+ "description": "Update multi-user preferences for instance. Methods are disabled until multi user mode is enabled via the UI.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "success": true,
+ "error": null
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Instance is not in Multi-User mode. Method denied"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ },
+ "requestBody": {
+ "description": "Object with setting key and new value to set. All keys are optional and will not update unless specified.",
+ "required": true,
+ "type": "object",
+ "content": {
+ "application/json": {
+ "example": {
+ "users_can_delete_workspaces": false,
+ "limit_user_messages": true,
+ "message_limit": 5
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/document/upload": {
+ "post": {
+ "tags": [
+ "Documents"
+ ],
+ "description": "Upload a new file to AnythingLLM to be parsed and prepared for embedding.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "success": true,
+ "error": null
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ },
+ "requestBody": {
+ "description": "File to be uploaded.",
+ "required": true,
+ "type": "file",
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "file": {
+ "type": "string",
+ "format": "binary"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/documents": {
+ "get": {
+ "tags": [
+ "Documents"
+ ],
+ "description": "List of all locally-stored documents in instance",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "localFiles": {
+ "name": "documents",
+ "type": "folder",
+ "items": [
+ {
+ "name": "my-stored-document.json",
+ "type": "file",
+ "id": "bb07c334-4dab-4419-9462-9d00065a49a1",
+ "url": "file://my-stored-document.txt",
+ "title": "my-stored-document.txt",
+ "cached": false
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ }
+ },
+ "/v1/document/accepted-file-types": {
+ "get": {
+ "tags": [
+ "Documents"
+ ],
+ "description": "Check available filetypes and MIMEs that can be uploaded.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "types": {
+ "application/mbox": [
+ ".mbox"
+ ],
+ "application/pdf": [
+ ".pdf"
+ ],
+ "application/vnd.oasis.opendocument.text": [
+ ".odt"
+ ],
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": [
+ ".docx"
+ ],
+ "text/plain": [
+ ".txt",
+ ".md"
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Not Found"
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ }
+ },
+ "/v1/workspace/new": {
+ "post": {
+ "tags": [
+ "Workspaces"
+ ],
+ "description": "Create a new workspace",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "workspace": {
+ "id": 79,
+ "name": "Sample workspace",
+ "slug": "sample-workspace",
+ "createdAt": "2023-08-17 00:45:03",
+ "openAiTemp": null,
+ "lastUpdatedAt": "2023-08-17 00:45:03",
+ "openAiHistory": 20,
+ "openAiPrompt": null
+ },
+ "message": "Workspace created"
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ },
+ "requestBody": {
+ "description": "JSON object containing new display name of workspace.",
+ "required": true,
+ "type": "object",
+ "content": {
+ "application/json": {
+ "example": {
+ "name": "My New Workspace"
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/workspaces": {
+ "get": {
+ "tags": [
+ "Workspaces"
+ ],
+ "description": "List all current workspaces",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "workspaces": [
+ {
+ "id": 79,
+ "name": "Sample workspace",
+ "slug": "sample-workspace",
+ "createdAt": "2023-08-17 00:45:03",
+ "openAiTemp": null,
+ "lastUpdatedAt": "2023-08-17 00:45:03",
+ "openAiHistory": 20,
+ "openAiPrompt": null
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ }
+ },
+ "/v1/workspace/{slug}": {
+ "get": {
+ "tags": [
+ "Workspaces"
+ ],
+ "description": "Get a workspace by its unique slug.",
+ "parameters": [
+ {
+ "name": "slug",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "Unique slug of workspace to find"
+ },
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "workspace": {
+ "id": 79,
+ "name": "My workspace",
+ "slug": "my-workspace-123",
+ "createdAt": "2023-08-17 00:45:03",
+ "openAiTemp": null,
+ "lastUpdatedAt": "2023-08-17 00:45:03",
+ "openAiHistory": 20,
+ "openAiPrompt": null,
+ "documents": []
+ }
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "Workspaces"
+ ],
+ "description": "Deletes a workspace by its slug.",
+ "parameters": [
+ {
+ "name": "slug",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "Unique slug of workspace to delete"
+ },
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK"
+ },
+ "400": {
+ "description": "Bad Request"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ }
+ },
+ "/v1/workspace/{slug}/update": {
+ "post": {
+ "tags": [
+ "Workspaces"
+ ],
+ "description": "Update workspace settings by its unique slug.",
+ "parameters": [
+ {
+ "name": "slug",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "Unique slug of workspace to find"
+ },
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "workspace": {
+ "id": 79,
+ "name": "My workspace",
+ "slug": "my-workspace-123",
+ "createdAt": "2023-08-17 00:45:03",
+ "openAiTemp": null,
+ "lastUpdatedAt": "2023-08-17 00:45:03",
+ "openAiHistory": 20,
+ "openAiPrompt": null,
+ "documents": []
+ },
+ "message": null
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ },
+ "requestBody": {
+ "description": "JSON object containing new settings to update a workspace. All keys are optional and will not update unless provided",
+ "required": true,
+ "type": "object",
+ "content": {
+ "application/json": {
+ "example": {
+ "name": "Updated Workspace Name",
+ "openAiTemp": 0.2,
+ "openAiHistory": 20,
+ "openAiPrompt": "Respond to all inquires and questions in binary - do not respond in any other format."
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/workspace/{slug}/chats": {
+ "get": {
+ "tags": [
+ "Workspaces"
+ ],
+ "description": "Get a workspaces chats regardless of user by its unique slug.",
+ "parameters": [
+ {
+ "name": "slug",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "Unique slug of workspace to find"
+ },
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "history": [
+ {
+ "role": "user",
+ "content": "What is AnythingLLM?",
+ "sentAt": 1692851630
+ },
+ {
+ "role": "assistant",
+ "content": "AnythingLLM is a platform that allows you to convert notes, PDFs, and other source materials into a chatbot. It ensures privacy, cites its answers, and allows multiple people to interact with the same documents simultaneously. It is particularly useful for businesses to enhance the visibility and readability of various written communications such as SOPs, contracts, and sales calls. You can try it out with a free trial to see if it meets your business needs.",
+ "sources": [
+ {
+ "source": "object about source document and snippets used"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ }
+ },
+ "/v1/workspace/{slug}/update-embeddings": {
+ "post": {
+ "tags": [
+ "Workspaces"
+ ],
+ "description": "Add or remove documents from a workspace by its unique slug.",
+ "parameters": [
+ {
+ "name": "slug",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "Unique slug of workspace to find"
+ },
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "workspace": {
+ "id": 79,
+ "name": "My workspace",
+ "slug": "my-workspace-123",
+ "createdAt": "2023-08-17 00:45:03",
+ "openAiTemp": null,
+ "lastUpdatedAt": "2023-08-17 00:45:03",
+ "openAiHistory": 20,
+ "openAiPrompt": null,
+ "documents": []
+ },
+ "message": null
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ },
+ "requestBody": {
+ "description": "JSON object of additions and removals of documents to add to update a workspace. The value should be the folder + filename with the exclusions of the top-level documents path.",
+ "required": true,
+ "type": "object",
+ "content": {
+ "application/json": {
+ "example": {
+ "adds": [],
+ "deletes": [
+ "custom-documents/anythingllm-hash.json"
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "/v1/system/env-dump": {
+ "get": {
+ "tags": [
+ "System Settings"
+ ],
+ "description": "Dump all settings to file storage",
+ "responses": {
+ "200": {
+ "description": "OK"
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ }
+ },
+ "/v1/system": {
+ "get": {
+ "tags": [
+ "System Settings"
+ ],
+ "description": "Get all current system settings that are defined.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "settings": {
+ "VectorDB": "pinecone",
+ "PineConeEnvironment": "us-west4-gcp-free",
+ "PineConeKey": true,
+ "PineConeIndex": "my-pinecone-index",
+ "LLMProvider": "azure",
+ "[KEY_NAME]": "KEY_VALUE"
+ }
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ }
+ },
+ "/v1/system/vector-count": {
+ "get": {
+ "tags": [
+ "System Settings"
+ ],
+ "description": "Number of all vectors in connected vector database",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "vectorCount": 5450
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ }
+ }
+ },
+ "/v1/system/update-env": {
+ "post": {
+ "tags": [
+ "System Settings"
+ ],
+ "description": "Update a system setting or preference.",
+ "parameters": [
+ {
+ "name": "Authorization",
+ "in": "header",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "example": {
+ "newValues": {
+ "[ENV_KEY]": "Value"
+ },
+ "error": "error goes here, otherwise null"
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/InvalidAPIKey"
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal Server Error"
+ }
+ },
+ "requestBody": {
+ "description": "Key pair object that matches a valid setting and value. Get keys from GET /v1/system or refer to codebase.",
+ "required": true,
+ "type": "object",
+ "content": {
+ "application/json": {
+ "example": {
+ "VectorDB": "lancedb",
+ "AnotherKey": "updatedValue"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "InvalidAPIKey": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "string",
+ "example": "Invalid API Key"
+ }
+ },
+ "xml": {
+ "name": "InvalidAPIKey"
+ }
+ }
+ },
+ "securitySchemes": {
+ "BearerAuth": {
+ "type": "http",
+ "scheme": "bearer",
+ "bearerFormat": "JWT"
+ }
+ }
+ },
+ "security": [
+ {
+ "BearerAuth": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/server/swagger/utils.js b/server/swagger/utils.js
new file mode 100644
index 00000000..bd8e3e81
--- /dev/null
+++ b/server/swagger/utils.js
@@ -0,0 +1,52 @@
+const fs = require('fs');
+const path = require('path');
+const swaggerUi = require('swagger-ui-express');
+
+function faviconUrl() {
+ return process.env.NODE_ENV === "production" ?
+ '/public/favicon.png' :
+ 'http://localhost:3000/public/favicon.png'
+}
+
+function useSwagger(app) {
+ app.use('/api/docs', swaggerUi.serve);
+ const options = {
+ customCss: [
+ fs.readFileSync(path.resolve(__dirname, 'index.css')),
+ fs.readFileSync(path.resolve(__dirname, 'dark-swagger.css'))
+ ].join('\n\n\n'),
+ customSiteTitle: 'AnythingLLM Developer API Documentation',
+ customfavIcon: faviconUrl(),
+ }
+
+ if (process.env.NODE_ENV === "production") {
+ const swaggerDocument = require('./openapi.json');
+ app.get('/api/docs', swaggerUi.setup(
+ swaggerDocument,
+ {
+ ...options,
+ customJsStr: 'window.SWAGGER_DOCS_ENV = "production";\n\n' + fs.readFileSync(path.resolve(__dirname, 'index.js'), 'utf8'),
+ },
+ ));
+ } else {
+ // we regenerate the html page only in development mode to ensure it is up-to-date when the code is hot-reloaded.
+ app.get(
+ "/api/docs",
+ async (_, response) => {
+ // #swagger.ignore = true
+ const swaggerDocument = require('./openapi.json');
+ return response.send(
+ swaggerUi.generateHTML(
+ swaggerDocument,
+ {
+ ...options,
+ customJsStr: 'window.SWAGGER_DOCS_ENV = "development";\n\n' + fs.readFileSync(path.resolve(__dirname, 'index.js'), 'utf8'),
+ }
+ )
+ );
+ }
+ );
+ }
+}
+
+module.exports = { faviconUrl, useSwagger }
\ No newline at end of file
diff --git a/server/utils/database/index.js b/server/utils/database/index.js
index 0cdc7ba1..75f5f711 100644
--- a/server/utils/database/index.js
+++ b/server/utils/database/index.js
@@ -62,6 +62,7 @@ async function validateTablePragmas(force = false) {
const { WorkspaceChats } = require("../../models/workspaceChats");
const { Invite } = require("../../models/invite");
const { WelcomeMessages } = require("../../models/welcomeMessages");
+ const { ApiKey } = require("../../models/apiKeys");
await SystemSettings.migrateTable();
await User.migrateTable();
@@ -72,6 +73,7 @@ async function validateTablePragmas(force = false) {
await WorkspaceChats.migrateTable();
await Invite.migrateTable();
await WelcomeMessages.migrateTable();
+ await ApiKey.migrateTable();
} catch (e) {
console.error(`validateTablePragmas: Migrations failed`, e);
}
diff --git a/server/utils/middleware/validApiKey.js b/server/utils/middleware/validApiKey.js
new file mode 100644
index 00000000..6c743106
--- /dev/null
+++ b/server/utils/middleware/validApiKey.js
@@ -0,0 +1,30 @@
+const { ApiKey } = require("../../models/apiKeys");
+const { SystemSettings } = require("../../models/systemSettings");
+
+async function validApiKey(request, response, next) {
+ const multiUserMode = await SystemSettings.isMultiUserMode();
+ response.locals.multiUserMode = multiUserMode;
+
+ const auth = request.header("Authorization");
+ const bearerKey = auth ? auth.split(" ")[1] : null;
+ if (!bearerKey) {
+ response.status(403).json({
+ error: "No valid api key found.",
+ });
+ return;
+ }
+
+ const apiKey = await ApiKey.get(`secret = '${bearerKey}'`);
+ if (!apiKey) {
+ response.status(403).json({
+ error: "No valid api key found.",
+ });
+ return;
+ }
+
+ next();
+}
+
+module.exports = {
+ validApiKey,
+};
diff --git a/server/yarn.lock b/server/yarn.lock
index 6a9e1669..1d47eb61 100644
--- a/server/yarn.lock
+++ b/server/yarn.lock
@@ -252,6 +252,11 @@ accepts@~1.3.4, accepts@~1.3.8:
mime-types "~2.1.34"
negotiator "0.6.3"
+acorn@^7.4.1:
+ version "7.4.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
+ integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
+
agent-base@6, agent-base@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
@@ -800,6 +805,11 @@ deep-extend@~0.6.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
+deepmerge@^4.2.2:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
+ integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
+
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
@@ -1149,7 +1159,7 @@ glob-parent@~5.1.2:
dependencies:
is-glob "^4.0.1"
-glob@^7.1.3, glob@^7.1.4:
+glob@^7.1.3, glob@^7.1.4, glob@^7.1.7:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -1458,6 +1468,11 @@ json-bignum@^0.0.3:
resolved "https://registry.yarnpkg.com/json-bignum/-/json-bignum-0.0.3.tgz#41163b50436c773d82424dbc20ed70db7604b8d7"
integrity sha512-2WHyXj3OfHSgNyuzDbSxI1w2jgw5gkWSWhS7Qg4bWXx1nLk3jnbwfUeS0PSba3IzpTUWdHxBieELUzXRjQB2zg==
+json5@^2.2.3:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
+ integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
jsonpointer@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559"
@@ -2429,6 +2444,28 @@ supports-color@^5.3.0, supports-color@^5.5.0:
dependencies:
has-flag "^3.0.0"
+swagger-autogen@^2.23.5:
+ version "2.23.5"
+ resolved "https://registry.yarnpkg.com/swagger-autogen/-/swagger-autogen-2.23.5.tgz#fe86bde66daf991a2e9064ec83f2136319d19258"
+ integrity sha512-4Tl2+XhZMyHoBYkABnScHtQE0lKPKUD3NBt09mClrI6UKOUYljKlYw1xiFVwsHCTGR2hAXmhT4PpgjruCtt1ZA==
+ dependencies:
+ acorn "^7.4.1"
+ deepmerge "^4.2.2"
+ glob "^7.1.7"
+ json5 "^2.2.3"
+
+swagger-ui-dist@>=5.0.0:
+ version "5.4.2"
+ resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.4.2.tgz#ff7b936bdfc84673a1823a0f05f3a933ba7ccd4c"
+ integrity sha512-vT5QxP/NOr9m4gLZl+SpavWI3M9Fdh30+Sdw9rEtZbkqNmNNEPhjXas2xTD9rsJYYdLzAiMfwXvtooWH3xbLJA==
+
+swagger-ui-express@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/swagger-ui-express/-/swagger-ui-express-5.0.0.tgz#7a00a18dd909574cb0d628574a299b9ba53d4d49"
+ integrity sha512-tsU9tODVvhyfkNSvf03E6FAk+z+5cU3lXAzMy6Pv4av2Gt2xA0++fogwC4qo19XuFf6hdxevPuVCSKFuMHJhFA==
+ dependencies:
+ swagger-ui-dist ">=5.0.0"
+
table-layout@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04"